GoReleaser工具与Go构建技巧

GoReleaser

GoReleaser 的创建是为了解决我们都曾遇到过的一个问题:发布软件很无聊而且容易出错。

为了解决这个问题,我们最终都创建了脚本来自动化工作,并取得了不同程度的成功。

一般来说,这些脚本往往不可重用,并且依赖于许多其他工具——这使得该进程很难在其他机器上运行。

GoReleaser 的目标是让所有这些脚本都过时:不再编写脚本,而是编写一个简单的 YAML 配置文件; 您(通常)只需要一个 goreleaser 二进制文件,而不是许多工具。

然后,您只需运行一个命令即可构建、存档、打包、签名和发布工件。

我们努力让您(我们的用户)轻松地为您的用户做最好的事情。 这就是为什么我们专注于提供易于使用的集成、良好的默认设置和许多教程以及有助于缓解供应链安全问题的工具、包管理器、go mod 代理等。

通过这种方式,可以轻松提供易于安装的软件包,例如带有签名的校验和、软件物料清单和可复制的二进制文件。 

原本地址

总结来说:GoReleaser的作用是让你可以用最少的步骤和时间,把你写的Go程序打包成各种格式,然后发布到网上,让别人可以下载和使用。

使用

1. 安装

2. 项目初始化GoReleaser

1
2
# 安装goreleaser cli工具 
goreleaser init

3. 修改配置。GoReleaser配置文档

下面我使用的配置,可以用作于生产使用。也可以参考:ArgoCD GoReleaser

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
before: # 前置构建配置,可以放包检测、单元测试、go generate等,同步运行,失败就停止发布。
  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:
  - id: id # 多个构建时候,id必填
    main: cmd/main.go # 默认为根目录main.go,没有需要自己配置入口文件地址
    env:
      - CGO_ENABLED=0
    flags: # go build <flags...> 
      - "-v"
      - "-tags=prod" # 自定义编译标签,通过标签与死码消除完成本地与线上开发优化。
    ldflags: # go build -ldflags "<ldflags...>"
      - "-s -w" # 压缩编译配置
      - -X <package>/common.version={{.Version}}
      - -X <package>/common.commit={{.FullCommit}}
      - -X <package>/common.date={{.Date}}
      - -X <package>/common.builtBy=goreleaser
      - -extldflags="-static"
    goos: # 编译平台
      - linux
      - darwin
      - windows
    goarch: # 编译平台支持架构
      - amd64
      - arm64
      - s390x
      - ppc64le
    ignore: # 忽略的编译架构,
      - goos: darwin
        goarch: s390x
      - goos: darwin
        goarch: ppc64le
      - goos: windows
        goarch: s390x
      - goos: windows
        goarch: ppc64le
      - goos: windows
        goarch: arm64

archives: # 制品
  - format: tar.gz # 制品格式
    # 制品名称模版,可以使用goreleaser变量与go template模版函数。[goreleaser变量](https://goreleaser.com/customization/templates/)
    # this name template makes the OS and Arch compatible with the results of uname.
    name_template: >-
      {{ .ProjectName }}_
      {{- title .Os }}_
      {{- if eq .Arch "amd64" }}x86_64
      {{- else if eq .Arch "386" }}i386
      {{- else }}{{ .Arch }}{{ end }}
      {{- if .Arm }}v{{ .Arm }}{{ end }}      
    # use zip for windows archives
    # 对一些额外平台做覆盖配置
    format_overrides:
    - goos: windows
      format: zip
checksum:
  name_template: 'checksums.txt'
snapshot: # 
  name_template: "{{ incpatch .Version }}-snapshot"
changelog: # 根据commit log自动生成Change Log
  sort: asc
  filters:
    exclude:
      - '^docs:'
      - '^test:'

# The lines beneath this are called `modelines`. See `:help modeline`
# Feel free to remove those if you don't want/use them.
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
# vim: set ts=2 sw=2 tw=0 fo=cnqoj

4. 构建或发布

1
2
3
4
5
# 构建
goreleaser build

# 发布
goreleaser release

Go 构建技巧

Go ldflags相关可以通过go tool link命令查看文档,也可以查看这篇介绍:Go gcflags/ldflags 的说明

通过ldflags在编译时写入编译环境与版本。

1. 提供一个可注入参数变量与包。

参考对象:ArgoCD common/version.go

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
package common

import (
	"fmt"
	"runtime"
)

// Version information set by link flags during build. We fall back to these sane
// default values when we build outside the Makefile context (e.g. go run, go build, or go test).
var (
	version        = "99.99.99"             // value from VERSION file
	buildDate      = "1970-01-01T00:00:00Z" // output from `date -u +'%Y-%m-%dT%H:%M:%SZ'`
	gitCommit      = ""                     // output from `git rev-parse HEAD`
	gitTag         = ""                     // output from `git describe --exact-match --tags HEAD` (if clean tree state)
	gitTreeState   = ""                     // determined from `git status --porcelain`. either 'clean' or 'dirty'
	kubectlVersion = ""                     // determined from go.mod file
	extraBuildInfo = ""                     // extra build information for vendors to populate during build
)

// Version contains Argo version information
type Version struct {
	Version        string
	BuildDate      string
	GitCommit      string
	GitTag         string
	GitTreeState   string
	GoVersion      string
	Compiler       string
	Platform       string
	KubectlVersion string
	ExtraBuildInfo string
}

func (v Version) String() string {
	return v.Version
}

// GetVersion returns the version information
func GetVersion() Version {
	var versionStr string

	if gitCommit != "" && gitTag != "" && gitTreeState == "clean" {
		// if we have a clean tree state and the current commit is tagged,
		// this is an official release.
		versionStr = gitTag
	} else {
		// otherwise formulate a version string based on as much metadata
		// information we have available.
		versionStr = "v" + version
		if len(gitCommit) >= 7 {
			versionStr += "+" + gitCommit[0:7]
			if gitTreeState != "clean" {
				versionStr += ".dirty"
			}
		} else {
			versionStr += "+unknown"
		}
	}

	return Version{
		Version:        versionStr,
		BuildDate:      buildDate,
		GitCommit:      gitCommit,
		GitTag:         gitTag,
		GitTreeState:   gitTreeState,
		GoVersion:      runtime.Version(),
		Compiler:       runtime.Compiler,
		Platform:       fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH),
		KubectlVersion: kubectlVersion,
		ExtraBuildInfo: extraBuildInfo,
	}
}

2. 通过-ldflags="-X <package>/common.<variable>=<value>"在编译时候写入参数

推荐阅读