# GitHub 自动化部署
最近我的女儿迷恋上玩数独游戏,有一天她问我:“这一关你会不会啊?”。 我一看盘面上只有 17 个数字,这太难了吧,但是作为父亲不能说不会,于是我去 Sudoku.com (opens new window) 网站上学习了一下数独,并且开发了一个 Sudoku (opens new window) 数独推算应用。但是我没有服务器怎么部署我的应用呢?于是我想到了 Github 可以部署,也正好可以学习一下怎么在 Github 上部署前端应用。
# GitHub Pages
GitHub Pages 是一项静态站点托管服务,它直接从 GitHub 上的代码仓库获取 HTML、CSS 和 JavaScript 文件,通过构建过程运行文件,然后发布网站。所以我们可以使用 GitHub Pages 来展示博客、开源项目、获取分享简历,比如我的这个博客就是用 VuePress (opens new window) 开发,然后部署在我的 GitHub 主页上。
创建 GitHub Pages 有两种方式
Deploy from a branch
,从固定分支上部署,当 push 代码到这个分支时,Github 会自动从这个分支拉取代码进行部署Github Actions
,自定义 GitHub action 进行部署,完全由您编写的工作流文件决定怎么部署。
我们先讲一下 Deploy from a branch
,从分支部署,后面再讲 Github Actions.
# 创建 GitHub 代码仓库
在 GitHub 创建一个 Sudoku 代码仓库,在本地创建一个 Sudoku 项目,然后设置 GitHub 代码仓库的地址,然后提交代码并推送到 GitHub 代码仓库。
# 配置 GitHub Pages
配置 GitHub Pages:
- 点击 "Settings" > "Pages"
- "Source" 选择 "Deploy from a branch"
- "Branch" 我们先选择 "main",点击 "Save"。
GitHub 将会自动创建一个叫 pages-build-deployment
的 GitHub action,并且运行一次。
这个 GitHub action 由三个 job 组成
build
,拉取分支代码,使用 Jekyll (opens new window) 构建出静态页面,将静态页面,上传到 GitHub 服务器report-build-status
,报告构建结果deploy
,部署 GitHub Pages
后面讲 Github Actions 再来详细解析析这个 GitHub action.
因为我的 github.io
配置了域名,所以我的代码仓库 pages 自动添加了域名。因为我们刚才选的是 main
分支,Jekyll (opens new window) 将使用项目里的 index.html
或者 README.md
文件构建静态页面,然后部署到 GitHub Pages.
而且以后我们只要推送更新到 main
分支,pages-build-deployment
将会重新运行,从而重新部署。
关于怎么配置 Jekyll,请参考 Jekyll 官方文档 (opens new window)
# 手动部署
有些项目生成 README.md
静态页面就行了,但是这不是我们这个项目的目的。我们需要编译前端工程,然后使用编译后的文件来部署 GitHub Pages。
首先我们创建 gh-pages
分支,然后在 Pages 的配置页面选择 gh-pages
分支并保存。
然后去到工程目录,运行
$ npm run build
然后将编译后的文件提交到这个分支
$ cd dist
$ git init
$ git add -A
$ git commit -m "deploy"
$ git push -f git@github.com:cp3hnu/Sudoku.git main:gh-pages
2
3
4
5
也可以使用
gh-pages
(opens new window) 命令行工具
当 gh-pages
分支代码更新后,pages-build-deployment
会自动运行,然后部署。使用这种方式就不再需要 Jekyll (opens new window) 了,可以在 gh-pages
分支里添加一个空文件 .nojekyll
来取消 Jekyll 构建。
因为经常需要部署,因此可以写一个脚本:deploy.sh
#!/usr/bin/env sh
# 确保脚本抛出遇到的错误
set -e
# 生成静态文件
npm run build
# 进入生成的文件夹
cd dist
git init
git add -A
git commit -m 'deploy'
# 如果发布到 https://<USERNAME>.github.io
git push -f git@github.com:cp3hnu/Sudoku.git main:gh-pages
cd -
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
给 deploy.sh
文件添加执行权限
$ chmod 755 deploy.sh
在 package.json
里添加脚本命令
{
"scripts": {
"deploy": "./deploy.sh"
}
}
2
3
4
5
这样只要运行 deploy
命令,就能成功部署了您的 Web 应用
$ npm run deploy
# YAML
学习 GitHub Actions 之前,我们先来学习一下 YAML (opens new window),因为 GitHub Actions 是用 YAML 编写的。
YAML 是一种数据序列化语言,它是 JSON 的严格超集,在语法上添加了缩进和换行,但是不允许使用制表符进行缩进。YAML 多用于配置文件中。
# YAML vs JSON
YAML 与 JSON 显著的不同之处:
- YAML 使用空格缩进(一般是 2 个空格,不允许使用 tab)表示层级关系,JSON 使用
{}
- YAML 中的 key 不需要双引号
- YAML 中数组元素是用带缩进的破折号(-)表示,JSON 使用
[]
- YAML 使用
#
表示注释,而 JSON 不允许注释 - YAML 适合配置文件,JSON 适合数据传输
# 教程
YAML 文件可以支持多个文档。YAML 文档以三个破折号(-)开始,以三个点(.)结束(可选)。
下面是一个简单的 YAML 文件
---
doe: "a deer, a female deer"
ray: "a drop of golden sun"
pi: 3.14159
xmas: true
french-hens: 3
# This is a list
calling-birds:
- huey
- dewey
- louie
- fred
xmas-fifth-day:
calling-birds: four
french-hens: 3
golden-rings: 5
partridges:
count: 1
location: "a pear tree"
turtle-doves: two
...
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
对应的 JSON 文件是
{
"doe": "a deer, a female deer",
"ray": "a drop of golden sun",
"pi": 3.14159,
"xmas": true,
"french-hens": 3,
"calling-birds": [
"huey",
"dewey",
"louie",
"fred"
],
"xmas-fifth-day": {
"calling-birds": "four",
"french-hens": 3,
"golden-rings": 5,
"partridges": {
"count": 1,
"location": "a pear tree"
},
"turtle-doves": "two"
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
YAML 的详细教程请参考 Learn X in Y minutes (opens new window) 和 YAML Tutorial: Everything You Need to Get Started in Minutes (opens new window)
# Github Actions
上面我们实现了手动部署我们的 Web 应用,但是还是比较麻烦。我们每次修改完代码、提交代码,还要运行部署命令。那有没有可能我们提交代码到 main
分支后,Github 自动从 main
分支编译,把编译后的文件自动推送到 gh-pages
分支,然后依靠 pages-build-deployment
action,实现部署呢?答案是可以的,那就是通过 Github Actions。
实现上面的方案之前,我们先来学习一下 Github Actions。
GitHub Actions 是一个持续集成和持续部署(CI/CD)平台,允许您自动化构建、测试和部署。
一个 GitHub Action 由一个工作流(workflow)定义,工作流由多个作业(job)组成,而作业又是由多个步骤(step)组成的,一个步骤可以是一个命令、自定义脚本或操作(action,即 GitHub Actions 自定义应用程序)。当存储库中发生事件(event)时(比如提交代码到仓库),就会在虚拟机运行器(runner)或容器内运行工作流的作业。
工作流是由 YAML (opens new window) 文件定义,存储在代码仓库的 .github/workflows
目录里。一个代码仓库可以有多个工作流,每个工作流可以执行一组不同的任务。
GitHub 提供了很多的工作流模板 (opens new window),当您在 GitHub 上创建新的工作流时,GitHub 会提示您选择工作流模板,以此来帮助您快速创建工作流。
# 事件 Event
GitHub 的工作流由事件触发的,比如推送提交时触发工作流使用 push
事件
on:
push
2
还可以限定推送到哪个分支才触发工作流,比如下面是当推送到 main
或以 releases/
开头的分支时,触发工作流
on:
push:
branches:
- 'main'
- 'releases/**'
2
3
4
5
GitHub Actions 定义了多个事件,详情请参考触发工作流程 (opens new window)和触发工作流的事件 (opens new window)。
# 作业 Job
工作流由一个或多个作业组成。默认情况下这些作业并行运行的,若要按顺序运行作业,可以使用 needs
选项定义作业之间的依赖关系。
每个作业必须有一个唯一标识符 job_id
,job_id
是一个字符串,必须以字母或 _
开头,并且只能包含字母数字、-
或 _
。
每个作业通过 name
设置名称,runs-on
指定运行器,steps
设置步骤/任务。
下面创建了一个 build
的作业,名称是 "My Job",运行在 ubuntu-latest
上,只有一个任务就是安装 npm 依赖:
jobs:
build:
name: My Job
runs-on: ubuntu-latest
steps:
- name: Install dependencies
run: npm install
2
3
4
5
6
7
8
关于作业的详细介绍请参考在工作流程中使用作业 (opens new window)和 Jobs 语法 (opens new window)
# 步骤 Step
步骤就是作业的一个任务,一个作业可以包含一系列任务。步骤可以是一个简单命令、一段自定义脚本或者 GitHub Actions 自定义应用程序。
jobs:
my-job:
name: My Job
runs-on: ubuntu-latest
steps:
- name: Run a command
run: npm install
- name: Run a script
run: ./scripts/my.sh
- name: Run a GitHub action
uses: actions/checkout@v2
2
3
4
5
6
7
8
9
10
11
关于步骤的详细介绍请参考在 GitHub Actions 的工作流命令 (opens new window)、添加脚本到工作流程 (opens new window)和 Steps 语法 (opens new window)
# 操作 Action
这里的操作是指 GitHub Actions 自定义应用程序,是一组预先创建的可重用作业或代码,用于在工作流中执行特定任务。
操作可以是 GitHub Actions 官方的,比如 actions/checkout
(opens new window)、actions/setup-node
等,也可以是社区的,比如 peaceiris/actions-gh-pages
(opens new window),同时我们也可以创建自己的操作。
可以通过 GitHub Marketplace (opens new window) 来查找 GitHub 社区创建的操作。
- 在代码仓库中,打开要编辑的工作流程文件
- 打开工作流编辑器
- 在编辑器右侧,使用 GitHub Marketplace 边栏浏览操作。 其中带有徽章的操作是 GitHub 已验证其创建者是合作伙伴组织
- 找到要使用的操作
- 单击操作,查看操作详情
- 在"安装"下,复制工作流语法,添加到您的工作流文件中
关于操作的详细介绍请参考工作流中使用预编写的构建基块 (opens new window)。
# 常用的 Action
# actions/checkout
actions/checkout
(opens new window) 在 $GITHUB_WORKSPACE
下 checkout 您的仓库代码,以便您的工作流程可以访问它。
# actions/setup-node
actions/setup-node
(opens new window) 设置 Node 环境,提供以下功能:
- 下载和缓存 Node.js,并将其添加到 PATH
- 缓存 npm/yarn/pnpm 依赖项
- 为错误输出注册问题匹配器
- 为 GPR 或 npm 配置身份验证
actions/setup-node
使用 actions/cache
(opens new window) 缓存全局包数据,注意这个缓存不是缓存 node_modules
里的依赖包,具体是什么数据还需要进一步研究。
# actions/upload-artifact
actions/upload-artifact
(opens new window) 从工作流程运行中上传操作构件 (opens new window)。
# actions/download-artifact
actions/download-artifact
(opens new window) 从工作流程运行中下载操作构件 (opens new window)。
# actions/upload-pages-artifact
actions/upload-pages-artifact
(opens new window) 打包和上传可部署到 GitHub Pages (opens new window) 的构件,内部使用 actions/upload-artifact
上传构件。
# actions/configure-pages
actions/configure-pages
(opens new window) 启用 GitHub Pages (opens new window) 并提取有关站点的各种元数据。它还可用于配置 GitHub 支持的作为入门工作流程 (opens new window)的各种静态站点生成器。
# actions/deploy-pages
actions/deploy-pages
(opens new window) 部署 操作构建 (opens new window) 到 GitHub Pages (opens new window).
# peaceiris/actions-gh-pages
peaceiris/actions-gh-pages
(opens new window) 操作用于将静态文件上传的 gh-pages
分支。此操作通常用于静态站点生成器 (opens new window)来进行部署。
# 运行器 Runner
在作业中通过 runs-on
指定运行器,GitHub Actions 支持 GitHub 托管的运行器、大型运行器 或自托管运行器。
用于公共代码库的 GitHub 托管的运行器包括下面这些
虚拟机 | 处理器 (CPU) | 内存 (RAM) | 存储 (SSD) | 工作流标签 |
---|---|---|---|---|
Linux | 4 | 16 GB | 14 GB | ubuntu-latest ,ubuntu-24.04 ,ubuntu-22.04 ,ubuntu-20.04 |
Windows | 4 | 16 GB | 14 GB | windows-latest ,windows-2022 ,windows-2019 |
macOS | 3 | 14 GB | 14 GB | macos-12 |
macOS | 4 | 14 GB | 14 GB | macos-13 |
macOS | 3 (M1) | 7 GB | 14 GB | macos-latest 、macos-14 、macos-15 |
📢:
-latest
运行器是 GitHub 提供的最新稳定映像,但可能不是操作系统供应商提供的最新版本的操作系统。
关于运行器的详细介绍请参考选择作业的运行器 (opens new window)、在容器中运行作业 (opens new window)和在工作流中运行作业的变体 (opens new window)
# 更多配置
上面只是介绍了工作流的重要组成部分,其实工作流还有很多内容,比如变量、环境、上下文、并发、权限、工作流命令、安全性等等,更多详情请参考 GitHub Actions 文档 (opens new window)。
# 自动部署
学会了 GitHub Actions 和 YAML 之后,我们来创建一个自动部署的 GitHub action,实现这些功能:当我们提交代码并推送到 main
分支时,GitHub 自动编译我们的应用,并将编译后的文件提交并推送到 gh-pages
分支,这个时候 GitHub 的 pages-build-deployment
的 action 就会运行,部署我们的应用。
首先在项目里创建 .github/workflows
目录,然后创建 deploy.yml
文件或者 deploy.yaml
(文件名可以随便取),输入下面的代码。
# deploy.yml
name: Deploy to GitHub Pages
on:
push:
branches:
- main # 当推送到 main 分支时触发
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: "16" # 你可以根据项目需要调整 Node.js 版本
- name: Install dependencies
run: yarn install # 如果使用的是其他包管理工具,调整命令
- name: Build the project
run: npm run build # 构建项目,确保构建输出到指定目录(如 ./dist)
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v4 # 用于将构建产物推送到 gh-pages 分支
with:
github_token: ${{ secrets.GITHUB_TOKEN }} # 内置 GitHub Token,用于授权操作
publish_branch: gh-pages # 部署的分支
publish_dir: ./dist # 构建后的输出目录,根据项目调整(如 ./dist)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
提交代码并推送的 GitHub 仓库,这个时候在仓库的 Actions
页签下就能看到我们刚才创建的 GitHub action了,并且会自动运行一次,而且以后只要推送代码到 main
分支,该 GitHub action 都会自动运行,从而更新我们的应用。
# 更进一步
上面的自动部署的 GitHub action 是将编译后的文件推送到 gh-pages
分支,然后再依靠 GitHub 的 pages-build-deployment
实现部署,其实我们可以跳过 pages-build-deployment
,直接部署。我们需要创建一个 GitHub action,实现以下功能:当我们修改代码并推送到 main
分支时,Github action 自动编译我们的应用,并且直接部署到 GitHub Pages。
首先修改 Pages 配置:
- 点击 "Settings" > "Pages"
- "Source" 选择 "GitHub Actions"
然后修改 deploy.yml
文件。
# 构建并将其部署到 GitHub Pages
name: Deploy to GitHub Pages
on:
# 在针对 `main` 分支的推送上运行。如果你
# 使用 `master` 分支作为默认分支,请将其更改为 `master`
push:
branches: [main]
# 允许你从 Actions 选项卡手动运行此工作流程
workflow_dispatch:
# 设置 GITHUB_TOKEN 的权限,以允许部署到 GitHub Pages
permissions:
contents: read
pages: write
id-token: write
# 只允许同时进行一次部署,跳过正在运行和最新队列之间的运行队列
# 但是,不要取消正在进行的运行,因为我们希望允许这些生产部署完成
concurrency:
group: pages
cancel-in-progress: false
jobs:
# 构建作业
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm # 或 pnpm / yarn
- name: Setup Pages
uses: actions/configure-pages@v4
- name: Install dependencies
run: npm ci # 或 pnpm install / yarn install / bun install
- name: Build
run: npm run build
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: dist
# 部署作业
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
needs: build
runs-on: ubuntu-latest
name: deploy
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
这个工作流文件有两个作业,build
和 deploy
。
build
是构建作业,负责:
actions/checkout
拉取代码actions/setup-node
设置 Node 环境,版本是v20.x
,并且进行缓存(注意缓存的依赖不是node_modules
)actions/configure-pages
启用 GitHub Pagesnpm ci
安装依赖actions/upload-pages-artifact
打包和上传构件
deploy
是部署作业,它依赖 build
作业,只有一个任务,使用 actions/deploy-pages
将 build
上传的构件部署到 GitHub Pages.
最后提交代码并推送到 GitHub 仓库,然后 GitHub 就运行这个 GitHub action,直接编译并部署我们的应用到 GitHub Pages,不再需要 pages-build-deployment
GitHub action 了。
# 解析 pages-build-deployment
前面讲到选择从分支部署 GitHub Pages 时,GitHub 会自动创建一个 GitHub Action pages-build-deployment
,现在我们来分析一下这个 GitHub action
pages-build-deployment
GitHub action 由三个 job 组成
build
,拉取分支代码,使用 Jekyll (opens new window) 构建出静态页面,将静态页面,上传到 GitHub 服务器report-build-status
,报告构建结果deploy
,部署 GitHub Pages
其中最重要的就是 build
和 deploy
作业。
其中 build
的任务如下图所示:
build
上面我们创建的工作流 build
类似:
Set up job
:建立作业,主要是设置作业的运行器、权限、下载后面要用到的 action- 下载
ghcr.io/actions/jekyll-build-pages:v1.0.13
镜像 Checkout
,使用actions/checkout
action 拉取代码Build with JekyII
,使用actions/jekyll-build-pages
(opens new window) 编译构件Upload artifact
,使用actions/upload-pages-artifact
打包和上传构件
而 deploy
和我们创建的工作流一模一样,使用 actions/deploy-pages
将 build
上传的构件部署到 GitHub Pages.
# 总结
今天我们学习了 GitHub Pages (opens new window) 和 GitHub Actions (opens new window),知道了怎么手动部署和自动部署我们的 Web 前端应用。GitHub Actions 功能非常强大,我们只用了其中一小部分的功能,可谓是冰山一角,更多的功能值得我们继续学习、研究、探索。
# 代码仓库
cp3hnu/Sudoku (opens new window)
# References
- GitHub Docs (opens new window)
- GitHub Pages (opens new window)
- GitHub Actions (opens new window)
- Jekyll (opens new window)
gh-pages
(opens new window)- GitHub Marketplace (opens new window)
actions/starter-workflows
(opens new window)actions/checkout
(opens new window)actions/setup-node
(opens new window)actions/upload-artifact
(opens new window)actions/download-artifact
(opens new window)actions/configure-pages
(opens new window)actions/deploy-pages
(opens new window)peaceiris/actions-gh-pages
(opens new window)- YAML (opens new window)
- Learn X in Y minutes (opens new window)
- YAML Tutorial: Everything You Need to Get Started in Minutes (opens new window)
- YAML Lint (opens new window)
- JSON ⇆ YAML (opens new window)