fzf 发布流程全解:版本一致性校验、签名标签与 GitHub Actions 自动构建发布
【免费下载链接】fzf:cherry_blossom: A command-line fuzzy finder项目地址: https://gitcode.com/GitHub_Trending/fz/fzf
本文以 fzf 仓库的发布说明 RELEASE.md 为主线,完整拆解 fzf 从版本号同步、make tag一致性校验,到标签推送触发 GitHub Actions 发布工作流 的构建、签名、公证与上传全过程。读完后你可以独立执行一次完整的 fzf 版本发布,也能在不真正发布的情况下演练并验证整套发布流水线。
发布流程总览
fzf 的构建、签名、公证(notarization)与发布(publishing)全部由 .github/workflows/release.yml 处理,工作流的触发条件是tag push——即本地签好标签并推送到远端。完整流程分为四步:
- 在
master分支上更新六个文件中的版本号并提交; - 用
make tag VERSION=0.74.3校验文件一致性、签名并推送标签; - 在 Actions 中批准
release环境门禁,让工作流继续执行; - GitHub Release 发布完成后,将
master快进推送到远端。
之所以采用"先推 tag、后推 master"的顺序,RELEASE.md 中给出了明确原因:只有标签被推送,origin上的master仍指向旧版本,因此在发布窗口期内/master/install始终解析到已存在的旧版二进制,安装脚本不会因版本与制品不匹配而失败。
第一步:同步更新版本号
发布前需要把新版本号写入以下文件,并在master上提交(以0.74.3为例):
- CHANGELOG.md:新版本条目必须位于文件最顶部,如
0.74.3小节; - main.go:定义
var version = "0.74"的默认值(见 main.go#L14-L15),正式构建时会通过 ldflags 覆盖; - install:脚本第 5 行
version=0.74.3硬编码了要下载的版本; - install.ps1:Windows 安装脚本第 1 行
$version="0.74.3"; - man/man1/fzf.1 与 man/man1/fzf-tmux.1:两个 man 页面中均需出现
"fzf <版本>"字样。
这些文件各自承担不同角色,main.go 的version变量是fzf --version的输出来源之一;install 和 install.ps1 则按硬编码版本从 release 制品中下载对应二进制,install.ps1 下载后还会执行二进制并比对--version输出,不一致即报 "Invalid version"(见 install.ps1#L13-L15)。因此任何一处漏改都会直接破坏用户侧的安装链路,这正是后续一致性校验存在的原因。
第二步:make tag —— 一致性校验、签名与推送标签
完成版本提交后,执行:
make tag VERSION=0.74.3make tag会先执行prerelease目标,通过一组grep检查版本是否已出现在全部约定位置,全部通过后才签名并推送标签。对照 Makefile#L130-L141 可以看到实际检查项:
prerelease: # Check if version numbers are properly updated grep -q ^$(VERSION_REGEX)$$ CHANGELOG.md grep -qF '"fzf $(VERSION_TRIM)"' man/man1/fzf.1 grep -qF '"fzf $(VERSION_TRIM)"' man/man1/fzf-tmux.1 grep -qF $(VERSION) install grep -qF $(VERSION) install.ps1 @echo "OK: all files consistent at $(VERSION)" tag: prerelease git tag -s v$(VERSION) -m v$(VERSION) git push origin v$(VERSION)几个值得注意的细节:
VERSION_REGEX由VERSION_TRIM(去掉v前缀和-prerelease后缀,见 Makefile#L26-L27)逐点转义后生成,因此CHANGELOG.md中的标题必须整行精确等于版本号;man 页面的检查则要求带引号的形式"fzf 0.74.3"。- 标签使用
git tag -s签名(GPG/SSH 签名),保证发布标签可被验证;未通过prerelease时标签根本不会被创建和推送。 - 版本号若未显式给出
VERSION,Makefile 默认通过git describe --abbrev=0从最新标签推导(见 Makefile#L18-L25),所以直接make tag会作用于最新标签版本,显式传参更稳妥。
此步骤结束时的状态:远端多了一个签名的v0.74.3标签,master尚未推送。
第三步:工作流触发与 release 环境门禁
标签推送到远端后,.github/workflows/release.yml 立即触发。该工作流的触发与运行配置为:
on: push: tags: - 'v*' workflow_dispatch: inputs: version: description: 'Version to validate (e.g. 0.73.0).' type: string required: true permissions: contents: write jobs: release: runs-on: macos-latest environment: release要点:
- 仅
v开头的标签推送会触发真实发布;workflow_dispatch(手动运行)则用于演练,见后文测试章节。 - 作业运行在
macos-latest上,因为需要执行 macOS 签名与公证;environment: release使运行暂停在环境门禁,需要在 Actions 界面批准后才继续(对应 RELEASE.md 第 3 步"Approve it in the Actions tab to release")。 permissions: contents: write只授予写 release 内容所需的最小权限。
工作流内部的实际步骤
标签推送触发后,工作流依次执行以下阶段(完整定义见 .github/workflows/release.yml):
确定版本号:标签触发时从
GITHUB_REF_NAME去掉v前缀;手动触发时取用户输入的version参数。版本一致性复核:在 CI 侧重新执行与
make prerelease相同的五条grep检查(release.yml#L41-L50),确保检出的代码树确实对应该版本,防止"tag 指错了提交"这类事故。提取 Release Notes:用
sed -n "/^${R}$/,/^[0-9]/p"从 CHANGELOG.md 中截取当前版本小节,经反转、去空行、去前两行处理后写入tmp/release-note(release.yml#L52-L60),作为 GitHub Release 的说明文字。运行 goreleaser:根据触发方式选择不同参数(release.yml#L62-L76):
触发方式 goreleaser 参数 效果 标签推送(真实发布) release --clean --release-notes tmp/release-note构建全部制品并创建 GitHub Release 手动运行(演练) release --snapshot --clean --skip=publish构建 + 签名 + 公证,仅跳过 release 上传 所需凭据全部来自仓库 Secrets:
RELEASE_PAT(创建 release 的 token)、MACOS_SIGN_P12/MACOS_SIGN_PASSWORD(签名证书)、MACOS_NOTARY_ISSUER_ID/MACOS_NOTARY_KEY_ID/MACOS_NOTARY_KEY(App Store Connect 公证密钥)。
构建与公证:.goreleaser.yml 中的实现
goreleaser 的具体行为由根目录的 .goreleaser.yml 定义,与发布流程直接相关的部分包括:
- 交叉编译矩阵:覆盖
darwin/linux/windows/freebsd/openbsd/android×amd64/arm/arm64/loong64/ppc64le/s390x/riscv64,并排除若干不支持的组合(如freebsd/arm、openbsd/riscv64,见 .goreleaser.yml#L9-L48); - 版本注入:
ldflags通过-X main.version={{ .Version }} -X main.revision={{ .ShortCommit }}将版本与提交号写死进 main.go 的version/revision变量(.goreleaser.yml#L30-L33),这也解释了为什么main.go中的默认值只是占位; - macOS 签名 + 公证:
notarize.macos段以enabled: "{{ not .IsSnapshot }}"控制——快照构建自动跳过公证,真实发布则先用MACOS_SIGN_P12证书签名,再用 App Store Connect 密钥执行公证且wait: true阻塞至完成(.goreleaser.yml#L51-L84); - 制品格式:非 Windows 平台打
tar.gz,Windows 打zip;另通过nfpms生成含 man 页面与 LICENSE 的deb包(.goreleaser.yml#L86-L116); - 发布设置:
prerelease: auto使带预发布后缀的 tag 自动标记为预发布版本;快照版本号模板为{{ .Version }}-devel(.goreleaser.yml#L118-L126)。
第四步:快进 master
GitHub Release 发布完成后,执行:
git push origin master把master快进到包含新版本号的提交。至此发布窗口结束,/master/install开始指向新版本,install脚本中的版本与 master 分支源码保持一致。
演练发布流程:不产生真实 Release 的测试方法
RELEASE.md 专门给出了在不触发真实发布的前提下验证流水线的方法,非常适合在修改工作流 YAML 或轮换签名凭据后执行:
- 在 Actions 标签页点击Release→Run workflow(即
workflow_dispatch); - 选择一个分支,并输入该分支当前对应的版本号——版本一致性检查要求输入值与检出代码树中的文件内容匹配(例如 CHANGELOG.md 顶部、install 中的
version=行); - 按提示批准
release环境门禁; - 由于事件不是标签推送,goreleaser 以
release --snapshot --clean --skip=publish运行:快照模式下notarize的enabled: "{{ not .IsSnapshot }}"会跳过公证,但构建、签名步骤照常执行,仅跳过 GitHub release 上传。
该方法可以一次性验证四件事:工作流 YAML 语法、版本提取逻辑、macOS runner 环境,以及签名/公证凭据是否有效——是发布前成本最低的回归手段。
关键文件索引
| 文件 | 在发布流程中的角色 |
|---|---|
| RELEASE.md | 发布流程的操作手册(本文主体) |
| Makefile | prerelease/tag目标的本地实现:一致性 grep 检查、签名并推送标签 |
| .github/workflows/release.yml | 标签触发的 CI:版本复核、release notes 提取、goreleaser 调用与环境门禁 |
| .goreleaser.yml | 多平台交叉编译、ldflags 版本注入、macOS 签名与公证、制品打包 |
| main.go | version/revision变量,构建期由 ldflags 覆盖 |
| install / install.ps1 | 硬编码待下载版本,是版本一致性检查的对象 |
| CHANGELOG.md | 版本条目来源,同时被裁剪为 GitHub Release 说明 |
| BUILD.md | 本地构建与测试说明(make、make build等),与发布流程互为补充 |
适用前提提示:上述流程假定开发者持有仓库的推送权限、GPG/SSH 签名密钥,以及配置在仓库 Secrets 中的 macOS 签名与公证凭据;普通贡献者只需关注提交进入master后版本号相关文件的规范即可。
【免费下载链接】fzf:cherry_blossom: A command-line fuzzy finder项目地址: https://gitcode.com/GitHub_Trending/fz/fzf
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考