持续集成(CI)是软件开发过程中的一个重要部分,在这个过程中,共享的代码存储库会由于将团队成员的新工作集成到其中而不断地发生变化。
为了确保代码的高质量并减少潜在的错误,每次集成通常由一个自动化的构建和测试过程来验证。
在本文中,我们将学习如何使用Github Actions设置该过程,从而使项目自动构建和运行单元测试。该项目使用Golang编写,并使用PostgreSQL作为其主要数据库。
一、Github Actions如何运作
Github Action是Github提供的一项服务,具有与其他CI工具(如Jenkins,Travis或CircleCI)相似的功能。
Workflow
为了使用Github Actions,我们必须定义一个工作流程(workflow)。 工作流基本上是由一个或多个作业组成的自动化过程。它可以通过3种不同的方式触发:
- 通过Github存储库上发生的事件
- 通过设置一个重复的时间表
- 手动单击repository UI上的run workflow按钮

要创建工作流,我们只需要将.yml
文件添加到存储库的.github/workflows
文件夹中。例如,这是一个简单的工作流文件ci.yml
:
name: build-and-test
on:
push:
branches: [ master ]
schedule:
- cron: '*/15 * * * *'
jobs:
build:
runs-on: ubuntu-latest
这个工作流的名称为build-and-test
。我们可以使用on
关键字定义如何触发它。
在这个流程中,有一个事件将在更改被推送到master分支时触发工作流,以及还有一个每15分钟运行一次工作流的定期调度触发器。
然后,在工作流yaml文件的jobs
部分中定义要运行的作业列表。
Runner
为了运行jobs,我们必须为每个job指定一个runner
。 runner
是侦听可用作业的服务器,并且一次只能运行一个作业。
我们可以直接使用Github托管的runner,或者指定我们自己的自托管runner。
runners将运行这些jobs,然后将其进度,日志和结果报告回Github,这样我们就可以很容易地在存储库的UI上进行检查。

我们使用run-on
关键字指定要使用的runner
,如上图那样。在此示例工作流程中,将使用Github托管的运行程序来安装Ubuntu最新版本。
Job
job
是在同一runner
上执行的一组步骤。

通常,工作流(workflow)中的所有job
都是并行运行的,除非你有一些相互依赖的job
,否则它们将按顺序运行。

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v2
- name: Build server
run: ./build_server.sh
test:
needs: build
runs-on: ubuntu-latest
steps:
- run: ./test_server.sh
在此示例中,有2个job
:
-
第一个是
build
,它包含2个步骤:检查代码和构建服务器。 -
第二个是
test
,它将运行应用程序的测试。在这里,使用
need
关键字来表示test
依赖于build
,因此只有在应用程序成功构建后才能运行测试。这个
test
只有一个步骤,运行test_server.sh
脚本。
Step
step
是在每个job
中一个接一个连续运行的单个任务。 一个步骤可以包含1个或多个action。
action基本上是一个独立的命令,例如上面的run test_server.sh
命令。 如果一个step
包含多个action
,它们将按顺序运行。
关于action
的一个有趣的事情是它可以被重复利用。 因此,如果有人已经编写了我们需要的github action
,则可以在workflow
中使用它。
让我们看一下这个例子:
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v2
- name: Build server
run: ./build_server.sh
在这里,使用steps
关键字列出要在jobs
中运行的所有步骤。
第一步是从Github上将代码转移到runner
服务器。要做到这一点,只需使用Github actions团队已经编写好的Github actions checkout@v2
第二步是构建应用服务器。在本例中,我们提供了自己的操作,它只是运行存储库中的build_server.sh脚本。
总结
在开始编码之前,让我们做一个简短的总结:
- 我们可以通过3种方式触发工作流(workflow):事件,调度计划或手动。
- 一个工作流由一个或多个任务(jobs)组成。
- 一个任务由多个步骤(steps)组成。
- 每个步骤可以有1个或多个动作(actions)。
- 工作流(workflow)中的所有任务(jobs)通常并行运行,除非它们相互依赖,在这种情况下,它们是串行运行的。
- 每个任务(jobs)由特定服务器(runner)单独运行。
- 服务器(runner)会将进度,日志和任务结果报告回github。 我们可以直接在Github存储库的UI上检查它们。
二、实战,为Golang+Postgres设置workflow
结合https://github.com/shisuizhe/simple-bank食用。
现在让我们学习如何为Golang应用程序设置一个真实的workflow,以便它可以连接到Postgres,并在每当git push
到github时运行所有单元测试。
使用workflow模板
在Github仓库中,选择Actions
标签。
Github知道我们的项目主要是用Go编写的,因此建议我们为Go设置工作流程。 让我们单击此设置按钮。
可以看到,存储库下的.github / workflows文件夹下将创建一个新文件go.yml:
name: Go
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Set up Go 1.x
uses: actions/setup-go@v2
with:
go-version: ^1.13
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Get dependencies
run: |
go get -v -t -d ./...
if [ -f Gopkg.toml ]; then
curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
dep ensure
fi
- name: Build
run: go build -v .
- name: Test
run: go test -v .
我们可以在这里直接使用Github编辑器编辑这个文件。但是,我更喜欢先将文件添加到本地项目目录下,然后在推送到Github之前在本地编辑好它。
创建一个workflow的yaml文件
进入项目根路径,创建一个新文件夹.github/workflows
mkdir -p .github/workflows
然后在这个文件夹中为工作流程创建一个新的yaml文件。 可以随意命名,这里,我命名为ci.yml。
touch .github/workflows/ci.yml
将github提供的模板内容拷贝到ci.yml,并做一些修改。
配置触发事件
定义可以触发此工作流程的事件。 通常,只要有内容修改被push到master分支,或者有合并到master分支的拉取请求,我们都希望运行测试。
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
还有很多其他事件可以配置。请参考Github Actions文档。
设置任务
接下来设置jobs。 在Github提供的模板中,只有1个job名为build,其运行在Ubuntu runner上。我把它重命名为test,因为这个名称表示它的主要目的。
jobs:
test:
name: Test
runs-on: ubuntu-latest
这个任务job还有几步需要做:
-
步骤1:安装Go环境。
在runner服务器上安装go,在这一步中,只需要使用现有的Github操作,即
setup-go @ v2
。steps: - name: Set up Go 1.x uses: actions/setup-go@v2 with: go-version: ^1.15 id: go
使用with关键字为该操作提供输入参数。 在这里,要求它使用特定版本的Go,例如1.15版。
id字段只是此步骤的唯一标识符。 如果要在其他上下文中引用此步骤,则可能需要它。
-
步骤2:转移代码
将这个存储库的代码转移到runner服务器上。 为此,我们还重复使用现有操作:checkout @ v2。
- name: Check out code into the Go module directory uses: actions/checkout@v2
-
步骤3:获取依赖关系
获取项目正在使用的所有依赖项或外部软件包。
- name: Get dependencies run: | go get -v -t -d ./... if [ -f Gopkg.toml ]; then curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh dep ensure fi
但是,在这里,我们不需要这一步,因为当构建应用程序或运行测试时,go mod会自动下载缺少的库。 因此,将其删除!
构建步骤也是不必要的,因为应用程序将运行go test时会自动构建。
- name: Build run: go build -v .
-
步骤4:运行测试
最后一步是运行单元测试。 为此,我们已经在Makefile中定义了一个
make test
命令。 因此,在此步骤中要做的就是调用它:- name: Test run: make test
将workflow推送到github
至此,我们完成了CI workflow的第一个基本版本:
name: ci-test
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
test:
name: Test
runs-on: ubuntu-latest
steps:
- name: Set up Go 1.x
uses: actions/setup-go@v2
with:
go-version: ^1.15
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Test
run: make test
由于我们尚未设置Postgres数据库,因此它可能无法正常工作。 但让我们将其推送到Github上看看其运行方式:
git add .
git commit -m "init ci workflow"
git push origin master
现在,回到Github存储库页面并选择Actions。所有步骤(steps)均在右侧列出。Setup job
,Setup Go
和Checkout code
步骤成功完成,这里,Test
虽然已经完成,但是失败了。
打开步骤Test,查看日志可以看到:
正如日志中看到的,代码不能连接到Postgres的端口5432,因为我们还没有在工作流中设置它。现在就开始吧!
添加Postgres服务
让我们搜索github action postgres
,然后打开有关创建Postgres服务容器的Github Action官方文档页面。
从上图可以看到Postgres被声明为该job的外部服务。 让我们复制此代码块并将其粘贴到我们的工作流文件中。
因此,我们使用services关键字来指定要与我们的job一起运行的外部服务列表。在本例中,只需要1个服务,即Postgres。
并且,由于项目中使用的是Postgres版本12,因此我们将此Docker映像名称设置为postgres:12。 可以在Docker Hub上查看此Postgres镜像的可用版本和标签。
services:
postgres:
image: postgres:12
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: 123456
POSTGRES_DB: simple_bank
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
接下来,我们需要为访问数据库的认证设置一些环境变量。
请记住,我们在本地Postgres容器中使用user="postgres",password="123456"和database="simple_bank"。 因此,我们在此处为CI工作流程设置相同的值。
options
选项非常重要,因为runner
服务器使用它来检查Postgres是否已成功启动,以便它知道何时运行工作流程中的后续步骤。
添加运行数据库迁移步骤
现在已经定义好了Postgres服务,但是为了让测试成功运行,还需要运行db迁移,为我们的应用程序创建正确的数据库模型。
因此,在Check out code ...
步骤之后定义一个新步骤。 名称为Run migrations
。 它唯一需要做的就是运行make migrationup
。
好了,现在让我们尝试将这个新的工作流更改推送到Github,看看会发生什么。
在图中箭头处可以看到一条日志说postgres service is healthy
。Setup Go步骤也成功。然后它转移新的代码到runner。
但是,数据库迁移失败了,因为没有安装golang-migrate CLI工具来运行迁移。
安装 golang-migrate 工具
因此,让我们搜索golang migrate,然后打开此Github页面文档。
根据您使用的操作系统,有几个选项。 因为我们正在使用Ubuntu作为runner,因此我将复制此curl命令以下载构建迁移的二进制文件。
curl -L https://github.com/golang-migrate/migrate/releases/download/$version/migrate.$platform-amd64.tar.gz | tar xvz
现在,在工作流程中,添加一个新的Install golang-migrate
步骤在Run migrations
之前。 然后,在run操作中,粘贴curl命令。
- name: Install golang-migrate
run: curl -L https://github.com/golang-migrate/migrate/releases/download/v4.12.2/migrate.linux-amd64.tar.gz | tar xvz
它将下载zip文件,并解压得到名为migration .linux-amd64
的迁移二进制文件。 现在,为了使migration命令起作用,我们必须将该二进制文件移动到/usr/bin
文件夹中。
因为这一步骤包含不止一个命令。在这里可以使用管道符|
来指定多行命令。让我们将这个move命令添加到步骤中:
- name: Install golang-migrate
run: |
curl -L https://github.com/golang-migrate/migrate/releases/download/v4.12.2/migrate.linux-amd64.tar.gz | tar xvz
sudo mv migrate.linux-amd64 /usr/bin/
which migrate
请注意,只有超级用户才能更改/usr/bin
文件夹的内容,因此我们必须使用sudo运行此命令。
还增加了1个命令:which migrate
,仅用于检查migrate CLI二进制文件是否已成功安装,是否可以在runner中使用。
现在,workflow是完美配置了吗?如果就这一样push到github的话,再次测试还是会失败。
因为项目中的Makefile
指定了迁移命令的二进制文件名为migrate
,而不是上面设置的migrate.linux-amd64
,所以还要做修改:
- name: Install golang-migrate
run: |
curl -L https://github.com/golang-migrate/migrate/releases/download/v4.12.2/migrate.linux-amd64.tar.gz | tar xvz
sudo mv migrate.linux-amd64 /usr/bin/migrate
which migrate
好了,让我们重新看看完整的workfolw配置文件:
name: ci-test
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
test:
name: Test
runs-on: ubuntu-latest
services:
postgres:
image: postgres:12
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: 123456
POSTGRES_DB: simple_bank
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- name: Set up Go 1.x
uses: actions/setup-go@v2
with:
go-version: ^1.15
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Install golang-migrate
run: |
curl -L https://github.com/golang-migrate/migrate/releases/download/v4.12.2/migrate.linux-amd64.tar.gz | tar xvz
sudo mv migrate.linux-amd64 /usr/bin/migrate
which migrate
- name: Run migrations
run: make migrateup
- name: Test
run: make test
提交测试:
Nice!最终我们的CI测试工作流程成功运行。
通过编写一个Github Action工作流程来运行需要连接到外部Postgres服务的Golang单元测试,让我们已经了解了持续集成。
Github Actions还可以做更多的事情。 查看其官方文档以了解有关它们的更多信息。
That's all,see you next time.