Syft 发布全流程指南:从make release触发、多架构产物发布到版本撤回机制
【免费下载链接】syftCLI tool and library for generating a Software Bill of Materials from container images and filesystems项目地址: https://gitcode.com/GitHub_Trending/sy/syft
syft 是 Anchore 出品的开源 SBOM(软件物料清单)生成工具,其发布流程高度自动化:一次 Release 会同时产出 Git 标签、GitHub Release 归档二进制、多架构容器镜像以及 Homebrew Formula。本文以仓库根目录的 RELEASE.md 为主线,结合 .github/workflows/release.yaml、.goreleaser.yaml、.make/main.go 与 cmd/syft/main.go 等源码,完整讲解一次 syft 发布从触发、审批、构建、发布到出现问题后撤回的全过程,帮助维护者掌握 syft 的发布操作与底层流水线原理。
一次 syft Release 包含哪些产物
根据 RELEASE.md,一次 syft Release 由以下四部分构成:
- 新的语义化版本(semver)Git 标签:从
main分支当前最新提交(tip of main)打出一个新的 semver 标签。 - GitHub Release:包含一份变更日志(changelog)以及归档的二进制资产。
- 容器镜像:发布到
ghcr.io与 Docker Hub(docker.io),并附带多架构镜像清单(multi-architecture images + manifest)。 - Homebrew tap 更新:
anchore/homebrew-syft的 Formula 被更新,指向最新 GitHub Release 中的资产。
理想情况下,发布应当尽可能频繁、采用小增量方式。除非有破坏性变更阻塞发布,或没有合入任何修复/特性,否则推荐的发布节奏是每 1~2 周一次。
创建一次发布:两步操作
RELEASE.md 明确说明:发布流程本身应尽可能自动化,整个创建过程只有两步。
第 1 步:用make release触发新发布
在仓库根目录执行:
make release执行后终端会展示一份预览版 changelog:
- 如果你对 changelog 内容满意,按
y继续; - 如果不满意,可以中止发布,调整将要包含在发布中的 PR 与 issue 上的标签(labels),然后重新运行触发命令,让 changelog 重新生成。
这里“调整 labels”正是 changelog 生成的关键机制:syft 的 changelog 由 GitHub PR/issue 的标签驱动归类,调整标签即可改变 changelog 的条目与分组。
需要说明的是,make release并非一个传统意义上的静态 Makefile 目标。仓库根目录的 Makefile 是一个薄封装:
.DEFAULT: %: @go run -C .make . $@即任意make <target>都会转发到 .make/main.go 中定义的 go-make 任务集,其中包括来自goreleaser.Tasks()的release、snapshot、ci-release、changelog等任务(见 .make/main.go),底层对接 goreleaser 工具链。
第 2 步:Release 管理员在 GitHub Actions 流水线页面上审批
触发后,必须有 Release 管理员在 GitHub Actions 的 release pipeline 运行页面上批准这次发布。审批通过后,发布流水线才会生成全部资产并发布 GitHub Release。
这一步是一个人工确认闸门(approval gate),防止自动化的 changelog 或资产生成在未经授权的情况下直接对外发布。流水线的权限配置(environment: release)与人工审批环节可在 .github/workflows/release.yaml 中看到。
发布流水线的内部结构
从源码看,.github/workflows/release.yaml 定义的流水线远比“两步操作”复杂,理解它可以帮你判断发布卡在哪个环节。它由三个 job 组成:
1. 版本可用性检查(version-available)
jobs: version-available: uses: anchore/workflows/.github/workflows/check-version-available.yaml@...复用 Anchore 的共享工作流检查指定版本是否可用(是否已被占用),防止重复打 tag。
2. 检查门(check-gate)
check-gate: uses: anchore/workflows/.github/workflows/check-gate.yaml@... with: checks: '["Acceptance tests (Linux)", "Acceptance tests (Mac)", "Build snapshot artifacts", "CLI tests (Linux)", "Integration tests", "Static analysis", "Unit tests"]'发布前必须确认main分支上的全部质量检查已通过,包括单元测试、CLI 测试、集成测试、静态分析、接受测试与快照构建。其设计意图在源码注释中写得很清楚:“如果这些检查没有在 main 上验证通过,我们就不希望触发发布”。这些检查名称与 .github/workflows/validations.yaml 中的定义对应。可通过skip-checks输入跳过检查门(用于紧急修复场景),对应!inputs.skip-checks条件。
3. 发布 job(release)
发布 job 依赖前两个 job(needs: [check-gate, version-available]),并满足:
if: ${{ always() && needs.version-available.result == 'success' && !contains(fromJSON('["failure", "cancelled"]'), needs.check-gate.result) }}即:版本可用检查必须成功,检查门只要不是失败或取消即可(允许被跳过)。发布 job 使用 16+32 核、32+128GB 内存的 runs-on.com 大机器,并挂载 120GB 磁盘,以满足多架构镜像 + 二进制的并行构建需求。其实际执行步骤包括:
- checkout 完整历史(
fetch-depth: 0),便于 goreleaser 生成 changelog 与版本信息; - Bootstrap 环境:复用 .github/actions/bootstrap/action.yaml;
- 设置 Docker Buildx:多平台镜像需要一个
docker-container驱动的 builder(默认 runner 无法构建多平台镜像); - 登录 Docker Hub 与 GHCR:分别使用
ANCHOREOSSWRITE_DH_USERNAME/ANCHOREOSSWRITE_DH_PAT和GITHUB_TOKEN; - 执行
make ci-release:核心构建发布步骤,注入RELEASE_VERSION、TAG_TOKEN(推送 tag 用)、macOS 签名/公证所需的QUILL_*密钥(Apple Developer ID 证书链等)、GITHUB_TOKEN(创建 Release 用)与GITHUB_BREW_TOKEN(更新 Homebrew Formula 用); - 生成 SBOM 附件:调用
anchore/sbom-action对go.mod生成sbom.spdx.json作为发布资产(continue-on-error: true,失败不阻塞发布)。
4. 安装脚本发布 job(release-install-script)
release-install-script: needs: [release] if: ${{ always() && (needs.release.result == 'success' || github.event.inputs.phase == 'install-script-only') }}复用 Anchore 共享工作流把安装脚本同步到get.anchore.io等 CDN(Cloudflare R2 / AWS S3),保证curl ... | sh安装方式的脚本始终指向最新发布版本。仓库根目录的 install.sh 即是被发布的安装脚本,它支持通过VERIFY_SIGN开关启用 cosign 签名校验,并内置了VERIFY_SIGN_SUPPORTED_VERSION=v0.104.0(首个引入 cosign 签名的最低版本)与VERIFY_SIGN_FLAG_VERSION=v1.6.0(首个支持-v参数的最低版本)两个兼容性门槛。
产物矩阵:二进制、包管理器与多架构镜像
.goreleaser.yaml 定义了发布产物的完整构建矩阵,与 RELEASE.md 描述的“GitHub Release + 镜像 + Homebrew”一一对应。
跨平台二进制与版本信息注入
builds: - id: linux-build dir: ./cmd/syft goos: [linux] goarch: [amd64, arm64, ppc64le, riscv64, s390x] ldflags: | -w -s -extldflags '-static' -X main.version={{.Version}} -X main.gitCommit={{.Commit}} -X main.buildDate={{.Date}} -X main.gitDescription={{.Summary}}- Linux:amd64 / arm64 / ppc64le / riscv64 / s390x 五个架构,静态链接(
CGO_ENABLED=0,见 env 段); - macOS(darwin):amd64 / arm64,并带有 post hook 使用 quill 做签名与公证(notarize),快照构建时使用
--dry-run与--ad-hoc; - Windows:amd64 / arm64,归档格式为 zip(其余平台为 tar.gz)。
版本信息通过 ldflags 注入到 cmd/syft/main.go 中声明的四个变量:
var ( version = internal.NotProvided buildDate = internal.NotProvided gitCommit = internal.NotProvided gitDescription = internal.NotProvided )这些变量随后通过clio.Identification传入 CLI 应用(cmd/syft/main.go),即syft version命令输出内容的来源。默认值[not provided]定义在 cmd/syft/internal/constants.go。
系统包:deb 与 rpm
nfpms: - license: "Apache 2.0" maintainer: "Anchore, Inc" formats: [rpm, deb]通过 nfpm 同时产出.deb与.rpm两种系统包,与 test/install 目录下的安装验证脚本相配套。
Homebrew Formula 自动更新
brews: - repository: owner: anchore name: homebrew-syft token: "{{.Env.GITHUB_BREW_TOKEN}}" ids: [darwin-archives, linux-archives]goreleaser 会在发布时自动向anchore/homebrew-syft仓库提交 Formula 更新,使其指向本次 Release 的最新资产,这正是 RELEASE.md 中“Homebrew tap 更新”这一产物的实现位置。
四组 Docker 镜像
.goreleaser.yaml 的dockers_v2段一次构建四组镜像,覆盖两个仓库(anchore/syft与ghcr.io/anchore/syft)、五个平台:
| 镜像组 | Dockerfile | 标签 |
|---|---|---|
| production(root) | Dockerfile | latest、{{.Tag}} |
| nonroot | Dockerfile.nonroot | nonroot、{{.Tag}}-nonroot |
| debug(root) | Dockerfile.debug | debug、{{.Tag}}-debug |
| debug-nonroot | Dockerfile.debug-nonroot | debug-nonroot、{{.Tag}}-debug-nonroot |
值得注意的实现细节:
- 构建基础镜像使用
DEBIAN_VERSION: "13",注释说明 Debian 13(trixie)是首个提供 riscv64 distroless 基础镜像的版本,同时是 Debian 12 的超集; --provenance=false用于禁用 provenance 证明,保持镜像 manifest 干净;- 镜像的 SBOM 由独立步骤产生(
sbom: "false"),避免与归档产物的 SBOM 重复。
归档产物的 SBOM 与 cosign 签名
sboms: - artifacts: archive cmd: ../.tool/syft documents: - "{{ .Binary }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}.sbom" signs: - cmd: .tool/cosign args: - "sign-blob" ... artifacts: checksum每次发布会用自举的 syft 二进制对每个归档产物扫描生成独立的.sbom文件,并用 cosign 通过 Sigstore OIDC 对 checksum 进行无密钥(keyless)签名。签名产物(.sig与.pem证书)随 Release 一起发布,配合 install.sh 的VERIFY_SIGN校验能力,用户可以在安装时验证二进制完整性。
撤回一次发布(Retracting a release)
当某次发布被发现有问题时,RELEASE.md 给出了标准的撤回步骤:
- 删除 GitHub Release;
- 将
ghcr.io与docker.io注册表中的 docker 镜像取消打标签(untag); - 将
anchore/homebrew-syft的 brew Formula 回退到指向上一个发布版本; - 在
go.mod中为被撤回的版本新增一条retract条目。
Go 模块的retract指令是 Go 官方支持的撤回机制:在 go.mod(当前模块为github.com/anchore/syft)中写入形如retract vX.Y.Z的条目后,Go 工具链在解析该版本时会给出明确提示,引导用户避开有问题的版本。
为什么不能删除 Git 标签
RELEASE.md 特别强调了一个关键注意点:
不要从 git 仓库中删除 release 标签(tags)。
原因是:发布后的版本可能已经被 Go module proxy(如 proxy.golang.org)缓存并建立引用。如果删掉标签再重新打同一个版本号,新标签的 H1 hash(Go 模块校验哈希)将与代理缓存中的不一致,导致用户拉取新发布时出现校验警告与混淆。因此即使发布有问题,也应该保留 Git 标签,只通过“删除 GitHub Release + untag 镜像 + 回退 brew Formula + go.mod retract”这套组合拳来撤回。
发布相关文件索引
围绕 syft 发布流程,以下仓库文件值得深入阅读:
- RELEASE.md:发布流程的权威文档(本文主线);
- .github/workflows/release.yaml:GitHub Actions 发布流水线定义;
- .goreleaser.yaml:goreleaser 产物矩阵配置(二进制/包/镜像/brew/SBOM/签名);
- .make/main.go:
make release、make ci-release、make snapshot等任务的 go-make 实现(goreleaser 任务接入); - cmd/syft/main.go:版本信息的 ldflags 注入点;
- install.sh:随发布同步的安装脚本(含 cosign 校验逻辑);
- Taskfile.yaml:
snapshot-smoke-test、SNAPSHOT_DIR等发布相关辅助任务与变量; - Makefile:将 make 目标转发到 go-make 的入口。
小结
syft 的发布流程设计遵循“自动化为主、人工把关”的原则:make release一键生成预览 changelog,管理员在 GitHub Actions 上审批放行,随后由 release 流水线完成跨平台二进制、deb/rpm 包、四组多架构镜像、Homebrew Formula、SBOM 与 cosign 签名等全部产物的构建与发布。同时,流程对“失败后的撤回”也做了完整的预案:通过删除 GitHub Release、untag 镜像、回退 brew Formula 与go.mod的retract条目组合完成撤回,并明确告诫不要删除 Git 标签,以免与 Go module proxy 的缓存哈希产生冲突。
【免费下载链接】syftCLI tool and library for generating a Software Bill of Materials from container images and filesystems项目地址: https://gitcode.com/GitHub_Trending/sy/syft
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考