如何使用 GoReleaser 自动化 GoLang 构建版本

为了使项目更加用户友好和有吸引力,作者添加了 docker 图像并为许多不同的平台进行了分发构建。项目的每个新版本都需要它,即使是次要版本。因此,有必要编写这个过程的自动化,因为手工编写非常冗长且例行公事,而且很容易出错或忘记某些东西。下面我将告诉您有关GoReleaser的信息,它几乎可以免费地自动构建 golang 项目的发布版本。

在本文中,所有示例都将用于 GitHub。但是这些相同的技术也可以很容易地适应关闭项目。

我准备了一个简单的项目来展示 GoReleaser 的可能性。该项目由客户端和服务器两部分组成。服务器可以计算文本中的字数,客户端可以通过查询字数来寻址服务器。

我需要尽可能方便用户使用该应用程序,并且我需要:

  • 二进制服务器(mac/linux/windows)
  • 二进制客户端(mac/linux/windows)

GoReleaser 是一个用 Go 编写的实用程序,它可以基于一个简单的 yaml 脚本执行所有这些操作(实际上,该实用程序仍然可以做很多有用的事情)。

在您的操作系统上安装后,您需要运行命令才能开始:

goreleaser init
   • Generating .goreleaser.yaml file
   • config created; please edit accordingly to your needs file=.goreleaser.yaml

此命令在项目根目录中创建一个文件.goreleaser.yaml。到目前为止它是空的,什么也没做,我必须把它填满。所以,

# This is an example .goreleaser.yml file with some sensible defaults.
# Make sure to check the documentation at https://goreleaser.com
before:
  hooks:
   # You may remove this if you don't use go modules.
   - go mod tidy
   # you may remove this if you don't need go generate
   - go generate ./...
builds:
  - env:
   - CGO_ENABLED=0
   goos:
   - linux
   - windows
   - darwin
archives:
  - replacements:
      darwin: Darwin
      linux: Linux
      windows: Windows
      386: i386
      amd64: x86_64
checksum:
  name_template: 'checksums.txt'
snapshot:
  name_template: "{{ incpatch .Version }}-next"
changelog:
  sort: asc
  filters:
    exclude:
      - '^docs:'
      - '^test:' 

我将从描述构建部分开始。它定义了应该开始的 go build。这将创建我们需要的二进制文件。让我们描述一下:

builds:  
  - id: srv
    binary: srv
    env:
      - CGO_ENABLED=0
    goos:
      - linux
      - windows
      - darwin
    main: ./cmd/server/main.go

  - id: cli
    binary: cli
    env:
      - CGO_ENABLED=0
    goos:
      - linux
      - windows
      - darwin
    main: ./cmd/client/main.go

在这个配置中,我设置了两个构建,因为需要构建服务器和客户端。两者的构建看起来大致相同。我指定main.go应该使用哪个以及如何调用生成的二进制文件。

另外,我指出了GOOS并列出了我需要编译二进制文件的平台,即结果不应该是一个客户端和服务器文件,而是三个:Linux、Mac和Windows。如果构建是手动完成的,它将如下所示:

CGO_ENABLED=0 GOOS=darwin go build -o srv

这是一个仅适用于 Mac 且仅适用于服务器的示例。也就是说,一栋 6 栋。除了操作系统之外,您还可以指定架构:

goarch: 
	- amd64 
	- arm 
	- arm64

然后,每个操作系统仍然会为每个架构构建额外的二进制文件。为了不收集任何您不需要的特定操作系统/架构对,您需要在列表中指定它们:

ignore: 
- goos: darwin 
  goarch: 386 
- goos: linux 
  goarch: arm 
  goarm: 7 

在我的示例中,我不会这样做,以免因大量结果文件而使对结果的理解变得复杂。读者稍后将能够将其添加到他们的实际项目中。其他环境变量也可能经常受到影响。例如,在我使用的最新项目中GOPROXYGOPRIVATE. 所有这些都可以在env每个程序集单独的部分中指定。

让我向您展示构建的工作原理:

goreleaser release --skip-publish --snapshot
   • releasing...
   • loading config file       file=.goreleaser.yaml
   • loading environment variables
   • getting and validating git state
      • ignoring errors because this is a snapshot error=git doesn't contain any tags. Either add a tag or use --snapshot
      • building...               commit=87eea7148d7e07b947cdef49e0b1b6a8c406a60e latest tag=v0.0.0
      • pipe skipped              error=disabled during snapshot mode
   • parsing tag
   • running before hooks
      • running                   hook=go mod tidy
      • running                   hook=go generate ./...
   • setting defaults
   • snapshotting
      • building snapshot...      version=0.0.1-next
   • checking distribution directory
   • loading go mod information
   • build prerequisites
   • writing effective config file
      • writing                   config=dist/config.yaml
   • building binaries
      • building                  binary=/Users/antgubarev/project/gorelex/dist/srv_darwin_arm64/srv
      • building                  binary=/Users/antgubarev/project/gorelex/dist/srv_linux_amd64/srv
      • building                  binary=/Users/antgubarev/project/gorelex/dist/srv_windows_386/srv.exe
      • building                  binary=/Users/antgubarev/project/gorelex/dist/srv_linux_arm64/srv
      • building                  binary=/Users/antgubarev/project/gorelex/dist/srv_windows_arm64/srv.exe
      • building                  binary=/Users/antgubarev/project/gorelex/dist/srv_windows_amd64/srv.exe
      • building                  binary=/Users/antgubarev/project/gorelex/dist/srv_linux_386/srv
      • building                  binary=/Users/antgubarev/project/gorelex/dist/srv_darwin_amd64/srv
      • building                  binary=/Users/antgubarev/project/gorelex/dist/cli_windows_386/cli.exe
      • building                  binary=/Users/antgubarev/project/gorelex/dist/cli_windows_amd64/cli.exe
      • building                  binary=/Users/antgubarev/project/gorelex/dist/cli_linux_386/cli
      • building                  binary=/Users/antgubarev/project/gorelex/dist/cli_linux_amd64/cli
      • building                  binary=/Users/antgubarev/project/gorelex/dist/cli_linux_arm64/cli
      • building                  binary=/Users/antgubarev/project/gorelex/dist/cli_darwin_amd64/cli
      • building                  binary=/Users/antgubarev/project/gorelex/dist/cli_darwin_arm64/cli
      • building                  binary=/Users/antgubarev/project/gorelex/dist/cli_windows_arm64/cli.exe
   • archives
      • creating                  archive=dist/gorelex_0.0.1-next_Linux_x86_64.tar.gz
      • creating                  archive=dist/gorelex_0.0.1-next_Linux_arm64.tar.gz
      • creating                  archive=dist/gorelex_0.0.1-next_Darwin_x86_64.tar.gz
      • creating                  archive=dist/gorelex_0.0.1-next_Darwin_arm64.tar.gz
      • creating                  archive=dist/gorelex_0.0.1-next_Windows_arm64.tar.gz
      • creating                  archive=dist/gorelex_0.0.1-next_Windows_i386.tar.gz
      • creating                  archive=dist/gorelex_0.0.1-next_Linux_i386.tar.gz
      • creating                  archive=dist/gorelex_0.0.1-next_Windows_x86_64.tar.gz
   • calculating checksums
   • storing artifact list
      • writing                   file=dist/artifacts.json
   • release succeeded after 3.39s

从日志中,您可以看到收集了哪些文件以及所有文件都在/dist目录中。我在命令的开头传递了两个参数:

  • --skip-publish 默认情况下,GoReleaser 会立即发布文件。这还不是必需的,因为它是我们仍在调试,但我们稍后会需要它。
  • --snapshot 发布应该使用版本创建。GoReleaser 从最新的 git 标签中获取版本。既然还没有,我需要那面旗帜。

在未来版本的准备和调试过程中,经常需要这两个标志。我有时会在 GitHub 上使用一个草稿存储库,发布版本。所以我有能力在不影响用户的情况下编辑和改进我的发布系统。我强烈建议也这样做。

正如您可能已经注意到的,除了构建文件也已存档。这是下一个机会谈。存档必须包含您的用户使用产品所需的所有内容。在当前示例中,这是服务器和客户端文件。

默认情况下,归档名称是使用以下模板构建的{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}{{ if .Mips }}_{{ .Mips }}{{ end }}

(您也可以根据需要替换此模板)。替换部分指定模板中变量的适当替换。默认这个参数是空的,好在created.goreleaser.yaml已经指定了组合,这样就足够了。

当前创建的档案包含 srv 和客户端文件。有时可能需要将它们放在不同的档案中。这可以通过以下方式轻松完成:

archives:
  -
    id: srv
    builds:
      - srv
    replacements:
      darwin: Darwin
      linux: Linux
      windows: Windows
      386: i386
      amd64: x86_64
    name_template: "{{ .ProjectName }}_srv_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
    files:
      - LICENSE
      - README.md
      - doc/server/*
  -
    id: cli
    builds:
      - cli
    replacements:
      darwin: Darwin
      linux: Linux
      windows: Windows
      386: i386
      amd64: x86_64
    name_template: "{{ .ProjectName }}_cli_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
    files:
      - LICENSE
      - README.md
      - doc/cli/*

在这个例子中,我列出了需要创建的档案。在该builds部分中,我列出了需要归档的构建。也就是说,您可以分发任何文件以方便用户。这在组件很多的项目中很有用。

例如,除了客户端和服务器之外,可能还有其他代理和实用程序用于备份和监控。然后将有理由将 CLI 实用程序与其他服务器分开。

可执行文件不是唯一可以进入存档的文件。正如您在示例中看到的那样,我添加了一个带有许可证并读取的文件。对于每个存档,您可以设置自己的集合。这对于我上面引用的示例很方便。对于 CLI 实用程序,文档集将不同于服务器实用程序。

我不得不提到另一种可能性。它的钩子。

before:
  hooks:
  - make clean
  - go mod tidy

此功能允许您在构建之前执行其他操作。例如,删除额外的包,或创建默认配置文件等。我建议在将 GoReleaser 嵌入到持续交付系统中时将其作为管道的一个步骤。但是当这个选项不可用时,钩子将非常有用。我通常会添加:

rm -rf dist/

这在调试时很方便,因为构建目标目录应该是空的。

因此,该项目已准备好发布第一个版本。让我提醒您,作为本文的一部分,我将在 GitHub 上发布它。提交我们的.goreleaser.yaml. 不要忘记/dist添加. gitignore!这些工件文件并不完全属于存储库。现在您需要创建一个标签并立即推送它。

git tag v0.1.0
git push --tags origin master

GoReleaser 需要 GitHub 令牌才能使用 GitHub API 创建和编辑发布。您可以在您的设置中创建它。您只需要在公共存储库的管理上打上标志Access public repositories

export GITHUB_TOKEN=xxx

最后,在没有附加标志的情况下开始构建

日志在 GitHub 存储库中添加了有关发布创建的信息。

• publishing
      • scm releases
         • creating or updating release repo=antgubarev/pet tag=v0.1.0
         • release updated           url=https://github.com/antgubarev/pet/releases/tag/v0.1.0
         • uploading to release      file=dist/checksums.txt name=checksums.txt
         • uploading to release      file=dist/pet_cli_0.1.0_Linux_x86_64.tar.gz name=pet_cli_0.1.0_Linux_x86_64.tar.gz
         • uploading to release      file=dist/pet_srv_0.1.0_Windows_i386.tar.gz name=pet_srv_0.1.0_Windows_i386.tar.gz
         • uploading to release      file=dist/pet_srv_0.1.0_Linux_x86_64.tar.gz name=pet_srv_0.1.0_Linux_x86_64.tar.gz
         • uploading to release      file=dist/pet_srv_0.1.0_Linux_i386.tar.gz name=pet_srv_0.1.0_Linux_i386.tar.gz
         • uploading to release      file=dist/pet_srv_0.1.0_Darwin_x86_64.tar.gz name=pet_srv_0.1.0_Darwin_x86_64.tar.gz
         • uploading to release      file=dist/pet_cli_0.1.0_Linux_i386.tar.gz name=pet_cli_0.1.0_Linux_i386.tar.gz
         • uploading to release      file=dist/pet_srv_0.1.0_Windows_arm64.tar.gz name=pet_srv_0.1.0_Windows_arm64.tar.gz
         • uploading to release      file=dist/pet_cli_0.1.0_Darwin_arm64.tar.gz name=pet_cli_0.1.0_Darwin_arm64.tar.gz
         • uploading to release      file=dist/pet_cli_0.1.0_Windows_i386.tar.gz name=pet_cli_0.1.0_Windows_i386.tar.gz
         • uploading to release      file=dist/pet_srv_0.1.0_Linux_arm64.tar.gz name=pet_srv_0.1.0_Linux_arm64.tar.gz
         • uploading to release      file=dist/pet_cli_0.1.0_Windows_arm64.tar.gz name=pet_cli_0.1.0_Windows_arm64.tar.gz
         • uploading to release      file=dist/pet_srv_0.1.0_Darwin_arm64.tar.gz name=pet_srv_0.1.0_Darwin_arm64.tar.gz
         • uploading to release      file=dist/pet_cli_0.1.0_Windows_x86_64.tar.gz name=pet_cli_0.1.0_Windows_x86_64.tar.gz
         • uploading to release      file=dist/pet_cli_0.1.0_Darwin_x86_64.tar.gz name=pet_cli_0.1.0_Darwin_x86_64.tar.gz
         • uploading to release      file=dist/pet_cli_0.1.0_Linux_arm64.tar.gz name=pet_cli_0.1.0_Linux_arm64.tar.gz
         • uploading to release      file=dist/pet_srv_0.1.0_Windows_x86_64.tar.gz name=pet_srv_0.1.0_Windows_x86_64.tar.gz
   • announcing
   • release succeeded after 7.08s

去发布页面看看

万事如意,万事如意。除了仍然创建的所有档案和更改日志,其中包含从前一个标签进行的提交。这将帮助用户了解发生了什么变化,并帮助开发人员从常规手动描述它。

这就是我在很短的时间内所做的那种简单的事情来组织项目的发布。当然,使用 GitHub Workflow 将其自动化会更加方便。但它超出了这个话题。golangci-lint在以后的文章中,我将描述如何通过 GitHub 工作流和功能使开源项目更具吸引力。