在这篇文章中,我们将介绍如何使用GitHub Actions来创建一个持续集成(CI)管道,自动测试、审查和衬托你的Go代码。
对于个人项目,我通常会创建一个预提交的 Git 钩子来进行这类检查,但对于团队项目或开源工作--你无法控制每个人的开发环境--使用 CI 工作流是一个很好的方法,可以标记出潜在的问题,并在它们进入生产或版本发布之前帮助捕捉错误。
如果你已经在使用GitHub来托管你的仓库,那么使用他们的内置功能来做这件事就很好,而且很容易,不需要任何额外的第三方工具或服务。

为了演示它是如何工作的,让我们通过一个逐步的例子。
如果你想跟着做,请创建一个新的版本库,并将其克隆到你的本地机器上。在这篇文章中,我将使用私有版本库alexedwards/example 。
$ git clone git@github.com:alexedwards/example.git
Cloning into 'example'...
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (3/3), done.
然后让我们把一个简单的Go程序和一个(失败的)测试放在一起,像这样。
$ cd example/
$ touch main.go main_test.go
$ go mod init github.com/alexedwards/example
File: main.go
package main
import "fmt"
func main() {
msg := sayHello("Alice")
fmt.Println(msg)
}
func sayHello(name string) string {
return fmt.Sprintf("Hi %s", name)
}
File: main_test.go
package main
import "testing"
func Test_sayHello(t *testing.T) {
name := "Bob"
want := "Hello Bob"
if got := sayHello(name); got != want {
t.Errorf("hello() = %q, want %q", got, want)
}
}
如果你运行这个应用程序,它应该能正确编译并打印出"Hi Alice" ,但执行go test . 将导致失败。与此类似。
$ go test .
--- FAIL: Test_sayHello (0.00s)
main_test.go:10: hello() = "Hi Bob", want "Hello Bob"
FAIL
FAIL github.com/alexedwards/example 0.002s
FAIL
创建一个工作流文件
我们要做的下一件事是创建一个工作流文件,描述我们要在CI检查中做什么,以及我们希望它们何时运行。按照惯例,这个文件应该存储在版本库根部的.github/workflow 目录中,并且应该是YAML格式。
让我们创建这个目录和一个audit.yml 工作流文件。
$ mkdir -p .github/workflows
$ touch .github/workflows/audit.yml
这里有一个关于工作流文件语法的很好的介绍,也有一个针对不同语言和框架的模板集,你可以把它作为一个起点。
但现在,让我们跳进去,更新工作流程文件,使其看起来像这样:
File: .github/workflows/audit.yml
name: Audit
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
audit:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.17
- name: Verify dependencies
run: go mod verify
- name: Build
run: go build -v ./...
- name: Run go vet
run: go vet ./...
- name: Install staticcheck
run: go install honnef.co/go/tools/cmd/staticcheck@latest
- name: Run staticcheck
run: staticcheck ./...
- name: Install golint
run: go install golang.org/x/lint/golint@latest
- name: Run golint
run: golint ./...
- name: Run tests
run: go test -race -vet=off ./...
让我们快速浏览一下,解释一下文件的不同部分是做什么的:
- 首先,我们使用
on关键字来定义我们希望工作流何时运行。在本例中,我对工作流进行了配置,使其在main分支有新的提交或有拉动请求提交时运行。 - 然后我们使用
jobs关键字来定义要运行的作业列表。目前我们的工作流只包含一个名为audit的作业,但如果你想的话,可以指定多个作业,(默认情况下)它们将被并行执行。 - 每个作业都会有一个独立的运行器被启动。这本质上是一个虚拟机,它将执行作业的
steps。在上面的文件中,我们使用runs-on关键字来指定我们希望运行器使用Ubuntu 20.04作为基础操作系统,但也可以使用其他操作系统。还值得注意的是,该运行程序已经预装了很多有用的软件和工具。 - 在我们的
audit工作的第一步,我们使用uses关键字来执行社区行动actions/checkout@v2.这个动作将把我们的项目库签出到运行器中,以便下面的步骤访问代码。 - 然后我们使用
actions/setup-go@v2动作在运行器上安装Go 1.17版本。 - 一旦完成了这些,在剩下的步骤中,我们使用
run关键字来在运行器上执行特定的命令。在这种情况下,我们构建我们的代码,然后使用标准的go build|vet|test命令和额外的golint和staticcheck工具对其进行审计。
重要提示:如果你正在跟随,请在继续之前运行$ git branch --show-current ,检查你的分支名称。在某些情况下,你的分支的名称可能是master 而不是main ,在这种情况下,请相应地编辑audit.yml 文件中的on 指令。
现在已经就绪,让我们提交所有内容并将修改推送到你的仓库。
$ git add .
$ git commit -m "Initial commit"
$ git push
推送完成后,前往你的版本库,选择行动标签。你应该看到CI "审计 "工作流正在运行,类似于下面的截图:

你可以在工作流运行时点击查看更多细节,一两分钟后,你应该看到工作流由于我们的测试失败而被终止了:


此外,作为仓库的所有者,你也应该收到一封邮件通知,告诉你工作流失败了,而且每个浏览仓库的人都会在 Git 历史记录中看到提交内容旁边的红叉符号。

修复代码
让我们通过更新sayHello() 函数来修复我们的代码库,以返回正确的输出,像这样:
File: main.go
package main
import "fmt"
func main() {
msg := sayHello("Alice")
fmt.Println(msg)
}
func sayHello(name string) string {
// Change this to "Hello %s" instead of "Hi %s".
return fmt.Sprintf("Hello %s", name)
}
如果你愿意,你可以提交这个改动并推送它...
$ git add .
$ git commit -m "Fix sayHello() to return the correct value"
$ git push
......你应该看到,我们工作流程文件中的 "审计 "工作现在成功完成了,所有东西旁边都有一个漂亮的绿色勾号。


这下好了!这样做非常好,从现在开始,任何时候有人向main 分支推送或拉取请求,测试、审核和林特检查都会自动运行。
从这里开始,你可以扩展工作流程,以进行更多的检查或发送额外的通知,如果你想的话--甚至可以将它扩展为一个持续部署(CD)管道,构建和部署你的二进制文件。为了给你一些想法,下面是我自己项目中几个稍微复杂的工作流程。