Clair 发布(Release)流程实战指南:Minor/Patch 版本切割与制品自动化分发
2026/9/23 10:55:21 网站建设 项目流程
  • 网络安全
  • 应用安全
  • 云原生
  • 后端

【免费下载链接】clair

Vulnerability Static Analysis for Containers

项目地址:https://gitcode.com/gh_mirrors/cl/clair
点击查看免费下载

Clair 是面向容器的漏洞静态分析工具,其 v4 系列采用"约每三个月发布一次新版本、每个版本积极维护六个月"的节奏。本文以仓库内 Documentation/contribution/releases.md 为骨架,结合.github/workflows下的自动化流水线、Makefile 与 etc 目录下的构建规则,完整还原 Clair 从打 Tag、创建发布分支、生成变更日志,到自动产出源码归档、跨平台二进制与多架构容器镜像的端到端流程。读完本文,你将掌握作为维护者如何亲手切割 Minor/Patch 版本,也能理解发布制品的每一环在仓库中由哪段代码或配置驱动。

发布节奏与维护策略

Clair 的发布策略非常明确:

  • 发布周期:新版本(minor release)大约每三个月切割一次;
  • 维护窗口:每个发布的版本被积极维护六个月;
  • Bugfix 回流:修复类提交应先落在master(如果适用),然后标记为需要 backport 到某个 minor 版本的发布分支(release branch)。

需要指出的是,文档明确说明"为 release 分支挑选并移植修复提交"的过程目前尚未完全形式化(原文:"The process for doing this is not yet formalized"),这意味着 backport 依赖维护者的判断与人工操作,仓库当前没有对应的自动化机制。这一点从仓库现有 GitHub Actions 列表(.github/workflows)中也能印证:其中只有fast-forward.ymlcheck-fast-forward.yml这类分支管理辅助工作流,并不存在自动 backport 的 workflow。

版本切割:Minor 与 Patch 两条路线

Clair 的版本号遵循语义化版本(semver)习惯,v4.x.0表示 minor 版本,v4.x.y(y>0)表示 patch 版本。两类版本的操作流程共用同一套工具链,区别只在于是否创建新的发布分支

Minor 版本:打 Tag + 建分支

切割一个新的 minor 版本需要同时完成两件事:创建带注释的签名 Tag创建发布分支。文档给出的标准操作如下:

git tag -as v4.x.0 HEAD git push upstream HEAD:release-4.x tag v4.x.0

拆解来看:

  • git tag -as会创建一个 annotated(-a)且带 GPG 签名(-s)的标签,标签消息中应记录版本号与简要说明。使用 annotated 而非 lightweight tag,是为了保证标签对象携带作者、日期与消息,便于工具链(如git describe)正确解析版本信息;
  • 第二条命令同时推送两条 ref:HEAD:release-4.x把当前提交推送到名为release-4.x的发布分支(例如release-4.9),tag v4.x.0推送刚创建的标签。

推送完成后,还需要在 GitHub UI 中基于该 Tag 手动创建一个 Release。这一步不可省略,因为后续所有制品发布流程都以此为触发器(见下文"制品自动化"一节)。

Patch 版本:只在发布分支上打 Tag

Patch 版本与 minor 版本的唯一区别是:minor 版本对应的 Tag 只应出现在发布分支上(即release-4.x),且 patch 发布不需要新建分支。操作序列为:

git checkout release-4.x git tag -as v4.x.1 HEAD git push upstream tag v4.x.1

注意第三步只推送 Tag,不再推送分支。随后同样需要在 GitHub UI 基于新 Tag 创建 Release。

这里有一个值得强调的约束:minor 版本 Tag(v4.x.0)应当只存在于release-4.x分支上,而不是master/main主线上。结合 prepare-release.yml 中"release 分支只筛选同 minor 版本的提交生成 changelog"的逻辑(见下文),这种分支组织方式保证了每个 minor 系列的历史变更记录干净、独立。

变更日志(Changelog)的生成:Release 之前的准备工作

仓库中存在一个手动触发的辅助工作流 .github/workflows/prepare-release.yml,它在正式打 Tag 之前为发布准备CHANGELOG.md。该工作流通过workflow_dispatch接收两个输入:

  • branch:准备发布所基于的分支(默认main);
  • tag:将要发布的 Tag 名称。

工作流使用git-chglog工具按指定标签范围生成变更日志,核心逻辑是:

filter_tag="--tag-filter-pattern v4" branch=<输入的分支名> if [[ ${branch%-*} == "release" ]]; then filter_tag="--tag-filter-pattern v${branch#release-}" fi

也就是说,如果目标分支是release-4.x形式的发布分支,则只筛选该 minor 版本范围内的提交(例如v4.9);否则在主线只过滤v4前缀的完整历史。生成完毕后,工作流通过peter-evans/create-pull-request自动提交一个标题形如<tag> Changelog Bump的 PR,分支名为ready-<tag>,并启用 sign-off。仓库根目录的 CHANGELOG.md 即这一机制的产物,其头部记录着如v4.9.0 - 2025-12-08的版本条目,并按AllAmqpBuild(Deps)ChoreChore(Deps)等类别分组列出提交。

制品自动化:一条由 GitHub Release 驱动的流水线

Clair 的制品发布过程完全自动化,由 GitHub 上的 Release 事件驱动。也就是说,维护者在 UI 中点击"Publish release"的那一刻,后续的源码归档生成、容器镜像构建与推送会全部自动完成。

驱动的核心是 .github/workflows/cut-release.yml。该工作流的触发条件有两类:

on: push: tags: - v4.* workflow_dispatch: {}
  • 推送v4.*形式的 Tag(注意分支推送不会触发,只有 Tag push 才会);
  • 手动触发(workflow_dispatch,用于演练或补发)。

整个工作流由 7 个 Job 组成,形成一条清晰的依赖链:

config └─→ release-archive ──→ release-binaries ──→ release ──→ publish-container │ (推送 quay 镜像) └─→ publish-binaries(上传 clairctl) └─→ deploy-documentation

1. config:集中计算发布元数据

第一个 Job 在quay.io/projectquay/golang:1.25容器中运行,从GITHUB_REF推导出所有下游需要的变量:

  • version:即 Tag 本身(如v4.9.0);
  • tar_prefixclair-<tag>/,用于源码归档内的顶层目录前缀;
  • is_prerelease:当 Tag 包含alphabetarc时判定为预发布;
  • image_tag:去掉v前缀后的镜像 Tag(如4.9.0);
  • image_repo:目标仓库(上游自动映射为projectquay/clair);
  • build_go_version/cache_key:由容器内go version推导的构建版本与缓存键。

这些输出通过needs: [config]被后续所有 Job 消费。

2. release-archive:构建完整源码归档

该 Job 先以fetch-depth: 0完整检出仓库,然后执行:

git fetch origin "+${GITHUB_REF}:${GITHUB_REF}" # 修复 checkout action 覆盖 tag 的问题 git archive --prefix 'clair-<tag>/' -o clair.tar "${GITHUB_REF}" go mod vendor tar -rf clair.tar --transform 's,^,clair-<tag>/,' vendor gzip clair.tar mv clair.tar.gz clair-<tag>.tar.gz

关键点在于:Clair 的源码归档由git archive生成,而不是把工作目录直接打包——这保证了归档内容严格等于 Tag 指向的提交状态,不包含任何本地未提交的脏文件。由于go mod vendor生成的vendor目录默认不被 git 跟踪,Job 会在归档后把 vendor 目录追加进 tar 包并放到对应的clair-<tag>/前缀下,从而让归档成为一个自包含、可离线构建的完整源码包。该 Job 还会调用git-chglog生成 changelog(手动触发时生成空文件占位),并把clair-<tag>.tar.gzchangelog作为clair-releaseartifact 上传。

3. release-binaries:跨平台编译 clairctl

config产出的 Go 构建镜像中,该 Job 通过 matrix 组合goarch × goosarm64/amd64/386/ppc64le/s390x×linux/windows/darwin,并排除不支持的组合如 darwin-386、windows-ppc64le 等),对./cmd/clairctl执行交叉编译:

go build -trimpath -ldflags="-s -w" -buildvcs=false \ -o "clairctl-<goos>-<goarch>" ./cmd/clairctl

-trimpath去除构建路径信息,-ldflags="-s -w"剥离符号表与 DWARF,-buildvcs=false禁用 VCS 信息注入(注释指出版本信息应由git archive过程负责)。产物按clairctl-<goos>-<goarch>命名并作为独立 artifact 上传。

4. release:创建 GitHub Release

该 Job 在push事件下执行,通过ncipollo/release-action创建 Release:

  • 标题为<version> Release
  • Release 正文(bodyFile)使用 changelog;
  • prerelease标志来自configis_prerelease输出;
  • clair-*归档附加为 Release 资产。

upload_url作为输出暴露给下游,供publish-binaries上传二进制使用。

5. publish-container:构建并推送多架构容器镜像

这是制品分发的重头戏。该 Job 依次执行:

  1. 下载clair-releaseartifact 并解包到临时目录作为构建上下文;
  2. docker/setup-qemu-actiondocker/setup-buildx-action配置多平台构建能力;
  3. 使用docker/login-actionQUAY_USER/QUAY_TOKEN登录quay.io
  4. 通过docker/build-push-action一次构建linux/amd64、linux/arm64、linux/ppc64le、linux/s390x四个平台的镜像并推送:
platforms: linux/amd64,linux/arm64,linux/ppc64le,linux/s390x push: true tags: | quay.io/<image_repo>:<image_tag>

即最终镜像会被推送到quay.io/projectquay/clair仓库,Tag 为去掉v的版本号(如4.9.0),与文档中"container is pushed to the quay.io/projectquay/clair repository"的描述一致。

  1. is_prerelease == true时,额外调用.github/actions/set-image-expiration为预发布镜像设置过期时间(QUAY_API_TOKEN),避免 alpha/beta/rc 镜像长期滞留。

6. publish-binaries 与 deploy-documentation

  • publish-binaries将上一步 matrix 产出的所有clairctl-*二进制通过upload-release-asset追加上传到刚创建的 Release,作为独立的发布资产;
  • deploy-documentationrelease完成后触发.github/actions/documentation,把文档部署上线——这也解释了仓库中 Documentation 目录作为 mdBook(见 etc/doc.mk 的booktarget)发布的内容如何随版本更新。

本地复现制品构建:make dist 与 make dist-container

除了完全自动化的 CI 路径,维护者也可以在本地手工构建发布制品。文档明确给出了两个命令及其配置来源:

  • make dist:生成完整的源码归档;
  • make dist-container:基于该归档生成对应的容器镜像;
  • 控制这些 target 行为的变量文档化在 etc/config.mk。

make dist 的底层逻辑

查看 etc/dist.mk 可以看到dist的真实定义:

dist: clair-$(VERSION).tar.gz clair-%.tar.gz: vendor/modules.txt tarball=$(subst .gz,,$@) prefix=$(subst .tar.gz,/,$@) $(git_archive) --format tar --prefix "$$prefix" --output "$$tarball" $* ... tar --append --file "$$tarball" --transform "s,^,$${prefix}," ... vendor gzip -n -q -f "$$tarball"

它与 CI 中release-archiveJob 的做法完全同构:git archive打出干净源码树,再追加vendor目录,最后 gzip 压缩。VERSION的默认值定义在 etc/config.mk:

VERSION ?= $(shell git describe --match 'v*' --long | sed 's/\(.\+\)-\([0-9]\+-g[a-f0-9]\)/\1+\2/')

即默认从git describe推导形如v4.9.0-3-gabcdef1的版本(处理成v4.9.0+3-gabcdef1),也可以通过环境变量VERSION显式覆盖。

make dist-container 与容器构建

etc/container.mk 定义了容器相关 target:

  • container:基于当前工作树构建clair.oci
  • container-build:构建并把镜像docker load进本地容器引擎;
  • dist-container:即clair-$(VERSION).oci,它依赖clair-$(VERSION).tar.gz,先解包归档再调用 buildctl 构建,保证"从发布归档构建"而非"从工作树构建";
  • dist-clairctl:构建所有上游支持平台的 clairctl 二进制。

所有容器构建都经由buildctl(BuildKit 客户端)完成,etc/config.mk中相关的可调变量包括:

变量默认值说明
docker自动探测 podman/docker容器引擎命令
buildctl自动探测或go run兜底BuildKit 客户端
VERSIONgit describe推导归档/镜像使用的版本
IMAGE_NAMElocalhost/clair:latestbuildctl 输出镜像名,可用逗号分隔多名称
CONTAINER_PLATFORMSamd64 arm64 ppc64le s390x构建的架构(OCI 记法,OS 恒为 linux)
CLAIR_VERSION/GO_VERSION/GOTOOLCHAIN/SOURCE_DATE_EPOCH未设置时跳过透传给 buildctl 的构建参数,前三个用于 Dockerfile

container.mk还会把--opt platform=linux/<arch>展开为CONTAINER_PLATFORMS各架构,并在GITHUB_ACTIONS环境下启用type=gha的构建缓存。Dockerfile 方面,Dockerfile 采用多阶段构建:build阶段在quay.io/projectquay/golang镜像中交叉编译./cmd/...ctl阶段从 scratch 导出 clairctl,最终阶段基于ubi8/ubi-minimalnobody:nobody用户运行,默认ENTRYPOINT ["/usr/bin/clair"]EXPOSE 6060、环境变量CLAIR_CONF=/config/config.yamlCLAIR_MODE=combo

实操核对清单:一次完整的 Minor 发布

把文档与仓库实现串起来,一次完整的 minor 发布应包含以下步骤:

  1. 在主线完成本周期功能与修复(提交遵循仓库的提交规范,可参考 Documentation/contribution/commit_style.md);
  2. (可选)手动触发 prepare-release.yml,输入分支与目标 Tag,合并它生成的 Changelog PR,让 CHANGELOG.md 就绪;
  3. 在最新提交上打签名标签并推送分支与标签:
    git tag -as v4.x.0 HEAD git push upstream HEAD:release-4.x tag v4.x.0
  4. 在 GitHub UI 基于v4.x.0创建 Release(正文即 changelog);
  5. 观察 cut-release.yml 自动执行:生成clair-v4.x.0.tar.gz、交叉编译全部 clairctl 二进制、推送quay.io/projectquay/clair:4.x.0多架构镜像、部署文档;
  6. 之后的六个月维护期内,修复提交先落主线,再人工挑选移植到release-4.x分支,按 Patch 流程滚动发布。

Patch 发布则简化为:git checkout release-4.xgit tag -as v4.x.1 HEADgit push upstream tag v4.x.1→ UI 创建 Release,其余全部交给流水线。

结语

Clair 的发布体系呈现出"人工只负责打 Tag 与点按钮、其余全部自动化"的设计思路:git tag与 UI Release 是唯一的两个人工动作,其后源码归档、changelog 生成、跨平台二进制、多架构容器镜像与文档发布均由 cut-release.yml 串联完成;而 etc/config.mk、etc/dist.mk、etc/container.mk 与 Makefile 共同保证了 CI 与本地行为的一致性——无论是想参与发布,还是只想本地复现一份与线上完全相同的发布归档,make distmake dist-container都是最直接的入口。发布本身的技术门槛很低,真正的功夫在于维护期内对 release 分支的持续、规范的 bugfix 回流管理。

  • 网络安全
  • 应用安全
  • 云原生
  • 后端

【免费下载链接】clair

Vulnerability Static Analysis for Containers

项目地址:https://gitcode.com/gh_mirrors/cl/clair
点击查看免费下载

相关推荐

上一篇:【亲测免费】 CMSIS-SVD 解析器项目教程
下一篇:Boss直聘时间可视化插件:3步解决求职信息滞后难题

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询