用Go和GitHub行动进行持续集成(详细指南)

356 阅读2分钟

在这篇文章中,我们将介绍如何使用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 命令和额外的golintstaticcheck 工具对其进行审计。

重要提示:如果你正在跟随,请在继续之前运行$ 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)管道,构建和部署你的二进制文件。为了给你一些想法,下面是我自己项目中几个稍微复杂的工作流程。