GoReleaser工具与Go构建技巧

GoReleaser

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

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

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

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

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

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

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

原本地址

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

使用

1. 安装
2. 项目初始化GoReleaser
# 安装goreleaser cli工具 
goreleaser init
3. 修改配置。GoReleaser配置文档

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

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. 构建或发布
# 构建
goreleaser build

# 发布
goreleaser release

Go 构建技巧

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

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

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

参考对象:ArgoCD common/version.go

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>"在编译时候写入参数

推荐阅读

本文参与了1024 程序员节活动,欢迎正在阅读的你也加入。

inight
503 声望100 粉丝

涉及到的技术栈欢迎提问、尽我所能回答。