文章目录
介绍
Github Actions是 github 官方推出的一款 CI(持续集成)工具,目前还处于Beta版本,需要申请内测资格才能使用,申请成功之后在自己的代码仓库就可以看到Actions了。
使用说明
这里简单介绍下 Github Actions中的概念,具体可以参考官方文档。
术语
- workflow 表示一次持续集成的过程
- job 表示构建任务,每个 workflow 可以由一个或者多个 job 组成,可支持并发执行 job,所有 job 执行完也就代表着 workflow 结束
- step 每个 job 由一个或多个 step 组成,按顺序依次执行
- action 每个 step 由一个或多个 action 组成,按顺序依次执行,这里 action 需要特别说明一下,action 是可以是自定义脚本或引用第三方的脚本,依赖着 github 开源社区,许多 action 都可以直接复用,无需自己编写,github 已经提供了一个action 市场,可以搜索到各种第三方 actions,并且官方也提供了许多 actions。
构建环境
每个 job 都可以指定对应的操作系统,支持Windows、Linux、macOS,github 会提供一个虚拟机来执行对应的 job。
硬件规格:
- 双核 CPU
- 7GB 内存
- 14GB 固态硬盘
使用限制:
- 每个仓库只能同时支持 20 个 workflow 并行
- 每小时可以调用 1000 次 github API
- 每个 job 最多可以执行 6 个小时
- 免费版的用户最大支持 20 个 job 并发执行,macOS 系统的话最大只支持 5 个
可以看到这个配置下,普通的项目持续集成肯定没什么问题的。
构建记录
通过仓库中的Actions选项卡,可以看到项目中的 workflow 构建记录:

点击一条记录可以进入详情页面,可以实时查看每一个action的控制台输出,方便调试:

实例
前面大概介绍了一下基本的概念,下面就直接通过几个实例看看 Github Actions是如何使用的。
自动部署 Hexo 博客到 Github Page
首先第一个想到能用到Github Actions的就是我的博客了,项目托管在https://github.com/monkeyWie/monkeywie.github.io,目前项目有两个分支,master分支用于存放 hexo 编译之后的静态文件,另一个hexo分支用于存放 hexo 项目环境和 markdown 文章,master分支通过Github Page配置之后可以通过monkeywie.github.io域名访问。

之前写完博客之后都是需要手动执行一遍命令进行部署:
hexo clean&&hexo d然后再把hexo分支代码推送到 github 上
git push在使用Github Actions之后,只需要把hexo分支代码推送到 github 上,剩下的全部交给Github Actions即可,在此之前我们需要生成一对公私钥用于 hexo 的部署操作,因为 hexo 自带的部署命令hexo d需要有 git 远程仓库读写权限。
ssh-keygenGenerating public/private rsa key pair.Enter file in which to save the key (/root/.ssh/id_rsa):Created directory '/root/.ssh'.Enter passphrase (empty for no passphrase):Enter same passphrase again:Your identification has been saved in /root/.ssh/id_rsa.Your public key has been saved in /root/.ssh/id_rsa.pub.The key fingerprint is:SHA256:XG1vkchp5b27tteZASx6ZrPRtTayGYmacRdjjRxR1Y0 root@8fe85d51123bThe key's randomart image is:+---[RSA 2048]----+| .+o=|| o *Eoo|| . X B .|| . . + X +.|| S . = O..|| o O B =.|| O = *.*|| o . o ++|| .oo|+----[SHA256]-----+先把~/.ssh/id_rsa.pub中的公钥添加到 Github 对应仓库的Deploye keys中:

再将~/.ssh/id_rsa中的私钥添加到 Github 对应仓库的Secrets中,Name 定义为ACTION_DEPLOY_KEY,目的是在构建的时候可以读取该私钥并配添加到虚拟机中,以获取 git 仓库访问权限:

准备工作完成后,接着就按照教程,在hexo分支创建.github/workflows/main.yaml文件用于配置 hexo 部署。
name: CI
on: push: branches: - hexojobs: build: runs-on: ubuntu-latest
steps: - name: Checkout source uses: actions/checkout@v1 with: ref: hexo - name: Use Node.js ${{ matrix.node_version }} uses: actions/setup-node@v1 with: version: ${{ matrix.node_version }} - name: Setup hexo env: ACTION_DEPLOY_KEY: ${{ secrets.ACTION_DEPLOY_KEY }} run: | mkdir -p ~/.ssh/ echo "$ACTION_DEPLOY_KEY" > ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa ssh-keyscan github.com >> ~/.ssh/known_hosts git config --global user.email "liwei2633@163.com" git config --global user.name "monkeyWie" npm install hexo-cli -g npm install - name: Hexo deploy run: | hexo clean hexo d具体的配置语法这里就不详细说明了,可以自行在官方文档中查阅。
构建流程如下:
- 监听
hexo分支的 push 操作 - 运行一个 job,在
ubuntu虚拟机环境下 - 使用官方提供的actions/checkout@v1来拉取源码
- 使用官方提供的actions/setup-node@v1来安装 node 环境
- 使用
${{ secrets.ACTION_DEPLOY_KEY }}读取刚刚生成的私钥,并设置成环境变量,${{ exp }}写法为 actions 内置的表达式语法,详细文档参考:contexts-and-expression-syntax-for-github-actions - 将私钥写入到
~/.ssh/id_rsa文件中,并把github.com域名加入到~/.ssh/known_hosts文件中,以免第一次 ssh 访问时弹出交互式命令。 - 配置 git 用户信息
- 安装 hexo 命令行工具和项目的依赖
- 调用 hexo 命令进行部署
把hexo分支代码推到 github 上触发 workflow ,通过Actions选项卡进入就可以看到项目的构建情况了。

至此改造完成,以后只需要写完文章直接提交代码就可以自动部署了,甚至都可以不装 node 环境进行写作简直不要太方便。
自动创建项目 Release
有些项目在发布新版本时,一般都会创建一个Github Release,并且把对应编译好之后的文件上传到Release的资源列表中,例如:

如果这个使用手动操作的话,不仅步骤重复又繁琐(每次都要编译出各个操作系统对应的发行包再进行上传),而且最蛋疼的是对于国内的网络环境来说,上传文件速度简直不能忍,好不容易上传了一大半搞不好就因为网络原因又要重新上传,相信用过的人都深有体会。
我就在想如果能用Github Actions来创建Release,并且做对应的编译和上传,那上面的问题都可以迎刃而解了,于是在官方市场搜索了一下Release关键字,果然已经有提供对应的actions了:
- create-release: 用于创建 release
- upload-release-asset: 用于上传资源到对应的 release 中
接着创建一个Github仓库,我测试的仓库地址是https://github.com/monkeyWie/github-actions-demo,项目用 go 语言写的,代码非常简单就是两个 hello world 级别的代码,里面包含了普通的 go 程序和 cgo 程序。
项目的构建流程是在项目git push --tags的时候,触发 workflow,通过Github Actions编译出来Windows、Linux、macOS三个操作系统对应的 64 位可执行文件,再根据tag name和tag message来创建对应的Github Release,并将编译好的文件上传。
同样的创建一个.github/workflows/main.yml文件,内容如下:
name: CI
on: push: # Sequence of patterns matched against refs/tags tags: - "v*" # Push events to matching v*, i.e. v1.0, v20.15.10jobs: build: runs-on: ubuntu-latest
steps: - name: Checkout source uses: actions/checkout@v1 - name: Use Golang uses: actions/setup-go@v1 with: go-version: "1.13.x" - name: Build normal run: | CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o normal-windows-x64.exe cmd/normal/main.go CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o normal-linux-x64 cmd/normal/main.go CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -o normal-darwin-x64 cmd/normal/main.go zip normal-windows-x64.zip normal-windows-x64.exe zip normal-linux-x64.zip normal-linux-x64 zip normal-darwin-x64.zip normal-darwin-x64 - name: Build cgo run: | go get github.com/monkeyWie/xgo ~/go/bin/xgo -targets=windows/amd64,linux/amd64,darwin/amd64 -ldflags="-w -s" -pkg=cmd/cgo/main.go -out=cgo . mv cgo-windows-* cgo-windows-x64.exe mv cgo-linux-* cgo-linux-x64 mv cgo-darwin-* cgo-darwin-x64 zip cgo-windows-x64.zip cgo-windows-x64.exe zip cgo-linux-x64.zip cgo-linux-x64 zip cgo-darwin-x64.zip cgo-darwin-x64 - name: Create Release id: create_release uses: monkeyWie/create-release@master env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: tag_name: ${{ github.ref }} release_name: Release ${{ github.ref }} draft: false prerelease: false
- name: Upload Release normal windows uses: actions/upload-release-asset@v1.0.1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps asset_path: ./normal-windows-x64.zip asset_name: normal-${{ steps.create_release.outputs.tag }}-windows-x64.zip asset_content_type: application/zip - name: Upload Release normal linux uses: actions/upload-release-asset@v1.0.1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps asset_path: ./normal-linux-x64.zip asset_name: normal-${{ steps.create_release.outputs.tag }}-linux-x64.zip asset_content_type: application/zip - name: Upload Release normal darwin uses: actions/upload-release-asset@v1.0.1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps asset_path: ./normal-darwin-x64.zip asset_name: normal-${{ steps.create_release.outputs.tag }}-darwin-x64.zip asset_content_type: application/zip
- name: Upload Release cgo windows uses: actions/upload-release-asset@v1.0.1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps asset_path: ./cgo-windows-x64.zip asset_name: cgo-${{ steps.create_release.outputs.tag }}-windows-x64.zip asset_content_type: application/zip - name: Upload Release cgo linux uses: actions/upload-release-asset@v1.0.1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps asset_path: ./cgo-linux-x64.zip asset_name: cgo-${{ steps.create_release.outputs.tag }}-linux-x64.zip asset_content_type: application/zip - name: Upload Release cgo darwin uses: actions/upload-release-asset@v1.0.1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps asset_path: ./cgo-darwin-x64.zip asset_name: cgo-${{ steps.create_release.outputs.tag }}-darwin-x64.zip asset_content_type: application/zip构建流程如下:
- 监听 tag name 为
v开头的 push - 运行一个 job,在
ubuntu虚拟机环境下 - 拉取源码,安装
golang 1.13.x环境 - 使用
go build交叉编译出不同操作系统下 64 位可执行文件,并使用 zip 压缩 - 使用
xgo交叉编译出不同操作系统下 64 位可执行文件,并使用 zip 压缩 - 使用
monkeyWie/create-release@master创建 Release,其中会用到${{ secrets.GITHUB_TOKEN }},这是Github Actions内置的一个秘钥,用于授权访问你自己的 github 存储库,原理就是使用这个TOKEN调用Github API来进行创建 release,还有一个${{ github.ref }}也是Github Actions内置的一个变量,然后通过 action 的with进行参数传递。 - 使用
actions/upload-release-asset@v1.0.1上传文件,这里使用了两个表达式${{ steps.create_release.outputs.upload_url }}和${{ steps.create_release.outputs.tag }},可以获取到指定action的输出,第一个是获取创建好的 release 对应的上传地址,第二个是获取对应的 tag(例如:v1.0.0),这样就可以在把上传的文件带上版本号。因为这个action不支持多个文件上传,所以就写了多个 action 进行上传。
接下来在项目打个tag,然后push上去看看效果:
# 创建tag名为v1.0.8,并添加描述git tag -a "v1.0.8" -m '发布v1.0.8版本修复了以下bug:1. xxxxx2. xxxxx'# 把tag推到github上git push --tags然后就可以看到已经有一个新的workflow正在运行了:

运行完成后在Releases页面查看结果:

完美!和预想的结果一致。
注:由于官方的create-release有点不能满足需求,于是我自己
fork了一份create-release代码,就是把tag name给输出来了,这里是相关的PR,还没被合并,所以上面的创建 Release 的 action 是用的我自己的仓库monkeyWie/create-release@master,还有关于 go 交叉编译的知识,有兴趣可以看看我的这篇博客:go-cross-compile。
自动构建和部署 docker 镜像
在Github Actions提供的虚拟机中,已经内置了docker,而刚好我有一个项目因为国内的网络原因构建docker镜像非常的慢,这是我fork的一个用于 go 项目交叉编译的项目,仓库地址https://github.com/monkeyWie/xgo,这个项目的主要工作原理就是通过 docker 里内置好各种交叉编译的工具链,然后对外提供 go 项目交叉编译功能,下面节选一点Dockerfile内容:
看这大量的apt-get install,就知道在本地构建有多慢了,下面就改用Github Actions来帮忙构建和部署镜像。
由于要将镜像推送到docker hub官方镜像仓库上,需要验证账号信息,
这里我把自己的用户密码配置到了Secrets中,以便在 workflow 配置文件中可以访问到:

编写构建文件.github/workflows/main.yml:
name: CI
on: push: branches: - master paths: - "docker/base/*"
jobs: build: runs-on: ubuntu-latest
steps: - name: Checkout source uses: actions/checkout@v1 - name: Docker login run: docker login -u liwei2633 -p ${{ secrets.DOCKER_HUB_PWD }} - name: Docker build base run: | docker pull liwei2633/xgo:base docker build --cache-from=liwei2633/xgo:base -t liwei2633/xgo:base ./docker/base docker push liwei2633/xgo:base - name: Docker build other run: | docker build -t liwei2633/xgo:go-1.12.10 ./docker/go-1.12.10 docker push liwei2633/xgo:go-1.12.10 docker build -t liwei2633/xgo:go-1.12.x ./docker/go-1.12.x docker push liwei2633/xgo:go-1.12.x docker build -t liwei2633/xgo:go-1.13.1 ./docker/go-1.13.1 docker push liwei2633/xgo:go-1.13.1 docker build -t liwei2633/xgo:go-1.13.x ./docker/go-1.13.x docker push liwei2633/xgo:go-1.13.x docker build -t liwei2633/xgo:go-latest ./docker/go-latest docker push liwei2633/xgo:go-latest构建流程如下:
- 监听 master 分支的 push 操作,并且
docker/base目录下文件有修改才进行构建,这样做的目的是在其它与 docker 构建无关的文件改动了不会去触发 workflow - 运行一个 job,在
ubuntu虚拟机环境下 - 拉取源码
- 登录 docker hub,通过之前配置的
${{ secrets.DOCKER_HUB_PWD }},这里不用担心控制台输出会暴露密码,通过secrets访问的变量在控制台输出时都会打上马赛克
- 构建镜像,这里使用了一个小技巧
--cache-from=liwei2633/xgo:base,预先下载好之前的镜像liwei2633/xgo:base,然后可以使用docker的缓存机制加快构建速度
- 推送镜像并编译和推送不同 go 版本的镜像
这样通过Github Actions就把构建镜像和部署时间的缩小了到了13分钟:

虽然还是挺慢的但是跟本地构建比起来快了不是一个量级,有次本地构建等了一个多小时,因为网络原因导致一个软件源安装失败直接没了又要重头开始构建,所以高下立判,Github Actions真香!!
后记
通过上面三个实例项目,可以看得出Github Actions为我们节省大量的时间和重复的操作,且通过官方的 actions 市场很方便的就可以实现大部分编排功能,真是一个可以吹爆的良心产品,所以赶紧一起来尝鲜啊。