SuperAPI AI 工具集

go-build-release

部署与运维

查看项目

更新于 10-16 19:14

技能介绍

用途

本技能面向 Go 项目的构建与发布环节,目标是把“发布”做成一个可被验证的承诺:用户运行的二进制就是源码与流水线产出的那一个。核心思路是先做到可复现(任何人都能重新构建出相同结果),再做到可验证(任何人都能核对),最后才自动化发布流程。

参考资料按需加载:references/goreleaser.md 提供完整的 .goreleaser.yaml 和由 tag 触发的发布工作流;references/supply-chain.md 涵盖 SBOM、cosign 签名与验证、来源证明(provenance attestations)以及容器镜像签名。仅在摘要不足以解决问题时再读取对应参考文件。

一、版本标记

优先使用构建信息(build info),它不需要额外标志,也不会与源码产生偏差。Go 1.24+ 从 git 检出构建时,Main.Version 为 tag(如 v1.4.0)或伪版本,vcs.* 设置携带提交哈希与 dirty 标记:

info, _ := debug.ReadBuildInfo()
fmt.Println(info.Main.Version, info.GoVersion)

检查某个二进制由什么构建而来:

go version -m ./bin/api

当发布工具从没有 .git 的归档中构建时,需要注入这些值:-ldflags "-X main.version=v1.4.0 -X main.commit=$SHA"。

不要写入墙上时钟的构建时间——那会让每次构建都唯一。如果需要日期,使用提交时间。

二、可复现构建

相同源码、工具链和标志必须产生逐字节一致的二进制:

CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o bin/api ./cmd/api
sha256sum bin/api   # 在干净检出中重新构建并比对

-trimpath 会从二进制中移除本机路径。

固定工具链:go.mod 中的 go 与 toolchain 行、CI 中相同的镜像或 setup-go 版本。Go 自 1.21 起其自身工具链是可复现的。

除非确实需要 cgo,否则使用 CGO_ENABLED=0——C 工具链是又一个在不同机器间变化的输入。

构建前执行 go mod download 和 go mod verify,使被篡改的模块缓存导致构建失败。

三、交叉编译

列出全部 GOOS/GOARCH 组合:

go tool dist list

示例:

GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build -trimpath -o bin/api-linux-arm64 ./cmd/api
GOOS=windows GOARCH=amd64 CGO_ENABLED=0 go build -trimpath -o bin/api.exe ./cmd/api

纯 Go 可在任意宿主机上交叉编译。使用 cgo 时,每个目标平台都需要对应的 C 交叉编译器;当需要面向多个平台发布时,优先选择纯 Go 依赖(例如纯 Go 的 SQLite 驱动)。只有在所有目标 CPU 都支持时才提升 GOAMD64(v2/v3)——否则二进制在较老的 CPU 上将无法启动。

四、用 GoReleaser 自动化发布

一份配置即可构建所有目标、归档、校验和、SBOM 与签名:

# .goreleaser.yaml(节选——完整文件见 references/goreleaser.md)
version: 2
builds:
  - main: ./cmd/api
    env: [CGO_ENABLED=0]
    flags: [-trimpath]
    ldflags: ["-s -w -X main.version={{.Version}} -X main.commit={{.Commit}}"]
    mod_timestamp: "{{ .CommitTimestamp }}" # 可复现的文件时间
    goos: [linux, darwin, windows]
    goarch: [amd64, arm64]
checksum:
  name_template: checksums.txt

校验配置与完整干跑:

goreleaser check                         # 校验配置
goreleaser release --snapshot --clean    # 完整干跑,不发布任何内容

在 CI 中由 tag 推送(v1.4.0)触发发布,绝不要在个人笔记本上发布:流水线是唯一记录输入来源的地方。

五、供应链工件

各工件回答的问题与产出方式:

工件 回答的问题 产出方式
checksums.txt 我的下载是否完整无损? GoReleaser checksum
SBOM(SPDX 或 CycloneDX) 这个二进制里有什么? syft、GoReleaser sboms
签名包(.sigstore.json) 是否由本项目的流水线发布? cosign sign-blob --bundle,CI 中 keyless
来源证明(provenance attestation) 由哪个工作流、提交和 runner 构建? actions/attest(SLSA provenance)

对校验和文件签名,而不是给每个归档单独签名——一个签名即可覆盖全部。Keyless 签名使用 CI 任务的 OIDC 身份,因此没有私钥可泄露或轮换。在发布说明中公布确切的验证命令;没人验证的签名保护不了任何人。具体命令见 references/supply-chain.md。

六、容器镜像

纯 Go 服务使用 ko:无需 Dockerfile,直接从 Go 源码构建,基于 distroless 基础镜像,多架构,带 SBOM,且可复现。

KO_DOCKER_REPO=ghcr.io/acme ko build --bare --platform=linux/amd64,linux/arm64 ./cmd/api

其他情况使用多阶段 Dockerfile:在 golang:<version> 中构建,把二进制复制到 gcr.io/distroless/static:nonroot,以非 root 运行。

在部署中以 digest 引用镜像,并用 cosign 为其签名。镜像体积由 go-binary-size 技能负责。

七、版本管理

tag 使用带 v 前缀的语义化版本:v1.4.0。绝不要移动或删除已发布的 tag——模块代理已缓存它,用户的 go.sum 也固定了其哈希。

对库的破坏性变更应作为新的主版本并使用 /v2 模块路径,而不仅仅是打一个 v2.0.0 的 tag。

有问题的发布通过 go.mod 中的 retract 撤回(见 go-dependency-audit 技能),随后在新的补丁版本中修复。

由 Conventional Commits 生成发布说明(见 git-commit 技能);GoReleaser 的 changelog.groups 会将其归类为功能与修复。

验证清单

  • 对发布二进制执行 go version -m,能看到 tag 与 commit
  • 同一 tag 的两次干净构建产生相同的 SHA-256
  • 使用 -trimpath、CGO_ENABLED=0(或有正当理由的 cgo 构建)、固定工具链
  • 无构建时间戳;文件时间来自提交
  • 发布来自 tag 触发的 CI 任务,绝不在个人笔记本上
  • checksums.txt 已签名;SBOM 已发布;来源已证明
  • 发布说明包含 cosign verify-blob / gh attestation verify 命令
  • 镜像用 ko 或多阶段构建于 distroless、非 root、以 digest 引用
  • 已发布的 tag 绝不移动;有问题的发布予以撤回