- 后端
- 数据库客户端
- 缓存
【免费下载链接】go-redis
Redis Go client
版本发布是开源项目最需要纪律的环节之一。本文基于 go-redis 仓库中内置的prepare-release技能文档,系统讲解在本地完整准备一次 go-redis(vX.Y.Z)发布的六步流程:确定下一个语义化版本、收集自上次发布以来的变更、编写RELEASE-NOTES.md条目、通过 scripts/release.sh 提升版本、用 scripts/tag.sh 的 dry-run 模式验证,最后将发布步骤交接给维护者。读完本文,你将掌握 go-redis 官方发布流程的完整操作手册,以及每个命令背后仓库脚本的真实校验逻辑。
核心原则:准备一切,但绝不发布
go-redis 的发布流程刻意将「准备」与「发布」分离。prepare-release技能的边界非常明确:
- 绝不运行
scripts/tag.sh ... -t—— 该-t标志会真正创建并推送 git tag; - 绝不执行
git push(既不推 commit 也不推 tag); - 绝不在维护者明确要求之前代替他们提交 commit。
整个流程的交付物是一份可审阅的 diff:提升后的版本号加上一段新的RELEASE-NOTES.md条目。所有「真正对外生效」的动作——提交、合并 PR、打 tag、推送——都由人类维护者审阅后手动完成。
这种设计把「人为失误风险最高的写操作」隔离在自动化脚本之外:脚本只负责可重复、可校验的文本改写(版本号、go.mod 依赖),而不可逆的网络写操作保留给人工确认。这也正是为什么后面的每个步骤都反复出现「dry-run」「不 commit」「不 push」的约束。
第一步:选择下一个版本号(语义化版本)
版本的唯一事实来源是仓库根目录的 version.go。当前仓库中该文件的内容为:
package redis // Version is the current release version. func Version() string { return "9.23.0-beta.1" }用一条 grep 即可确认当前版本:
grep 'return' version.go # e.g. return "9.21.0"下一步是依据自上次发布以来合入的变更,按语义化版本(semver)规则确定下一个vX.Y.Z:
- patch(Z 位)—— 仅含 bug 修复,是零成本的 drop-in 升级;
- minor(Y 位)—— 包含新特性,但无破坏性变更,同样是 drop-in 升级;
- major(X 位)—— 存在破坏性变更。
如果变更集的性质有歧义(例如既有新特性又疑似破坏性改动),应与维护者确认再决定版本级别,而不是自行猜测。从仓库现状看,version.go 中的版本为9.23.0-beta.1,是一个带预发布后缀(-beta.1)的版本,这也提示读者:go-redis 的 tag 体系是支持语义化版本预发布/构建元数据后缀的(见后文 scripts/release.sh 的 TAG 正则校验)。
第二步:定位上次发布并收集变更
go-redis 是一个多模块仓库(monorepo with submodules)。关键陷阱在于:scripts/tag.sh 会为每一个公共子模块单独打 tag,例如extra/redisotel/v9.21.0、extra/redisprometheus/v9.21.0等。因此,一个朴素的git describe很可能返回的是子模块 tag 而不是根模块 tag。必须显式匹配「根 tag」——即只匹配以v开头且紧跟数字的 tag:
LAST=$(git describe --tags --abbrev=0 --match 'v[0-9]*') # e.g. v9.21.0 git log "$LAST"..HEAD --oneline gh pr list --state merged --limit 100 \ --json number,title,author,mergedAt,url --search "merged:>=<last-release-date>"--match 'v[0-9]*'之所以能避开子模块 tag,是因为子模块 tag 形如extra/redisotel/vX.Y.Z,其前缀是目录名而非v加数字。git log "$LAST"..HEAD给出上次发布之后的全部提交;gh pr list则拉取自上次发布日期以来合入的 PR 清单(含编号、标题、作者、合入时间与 URL),用于编写 release notes。
拿到 PR 清单后,需要将其分类到后续 release notes 的各小节中:
- highlights—— 对用户影响最大的变更;
- new features—— 新特性;
- bug fixes—— bug 修复;
- performance—— 性能改进;
- testing/infrastructure—— 测试与基础设施。
同时要执行严格的排除规则:dependabot 依赖升级、仅改 typo 的文档修复、对用户无可见影响的内部重构,都应从 release notes 中剔除;dependabot[bot]也不能出现在贡献者名单中。这些排除项与.github/RELEASE_NOTES_TEMPLATE.md中「What to Exclude」一节完全一致(详见下文第三步)。
第三步:编写 RELEASE-NOTES.md 条目
追加位置与模板
在 RELEASE-NOTES.md 的顶部插入新的# X.Y.Z (YYYY-MM-DD)小节(新版本在最上方,旧条目保持不动)。格式必须严格遵循 .github/RELEASE_NOTES_TEMPLATE.md(注意该文件位于仓库根目录的.github/下,不是 skill 文档所在目录):
- 固定的小节顺序:
🚀 Highlights→✨ New Features→🐛 Bug Fixes→⚡ Performance→🧪 Testing & Infrastructure→👥 Contributors; - 每个条目使用
(#PR) by @user的 PR 与贡献者链接格式; - 结尾必须包含
**Full Changelog**对比链接,形如${LAST}...vX.Y.Z(即上次发布与本次发布的 tag 对比); - 首行(lead line)要说明发布类型以及是否属于 drop-in upgrade,并与既有条目保持一致的措辞风格。
仓库中 RELEASE-NOTES.md 的当前最新条目即是一个可直接对照的实例,例如# 9.23.0-beta.1 (2026-09-11)开头写「This is abetarelease. You can upgrade without changes to your code.」,随后是 Highlights、New Features、Bug Fixes、Performance、Testing & Infrastructure 与 Contributors 各节。
模板本身还携带了完整的「应排除内容」与格式细则,技能文档明确要求读取模板而不是重新发明规则——模板里还给出了获取 PR 信息的推荐方式(gh pr list --state merged --limit 50 --json number,title,author,mergedAt,url)以及一条「识别 3-5 个最重要的变更作为 Highlights」的工作流建议。
RELEASE-NOTES.md 与 release-drafter 的分工
仓库中存在两套 release 记录机制,职责不同:
- RELEASE-NOTES.md是人工精修(curated)的发布记录,是发布准备流程中唯一需要编辑的文件;
- .github/release-drafter-config.yml驱动 GitHub 的 release-drafter 工具,按 PR 标签自动草拟一份 GitHub Release。该配置定义了
autolabeler(如 bug 分支打bug标签、feature 分支打feature标签)、类别映射(Breaking Changes / Experimental Features / New Features / Bug Fixes / Maintenance)以及exclude-labels: skip-changelog、exclude-contributors: dependabot等排除规则; - .github/workflows/release-drafter.yml在每次 push 到
master分支时触发release-drafter/release-drafter@v7action,引用上述配置文件自动更新草稿。
也就是说:自动草稿可以快速生成,但最终对外发布的手写精修记录是RELEASE-NOTES.md。编写时不要被自动草稿带偏,以模板和人工整理为准。
第四步:提升版本号(运行 release.sh)
编写好 release notes 后,运行仓库自带的版本提升脚本:
TAG=vX.Y.Z ./scripts/release.shscripts/release.sh 的实际行为可以从源码确认,它做三件事:
- 校验 TAG:
TAG环境变量必填;随后用正则^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(\-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?(\+[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$校验格式(支持预发布后缀-beta.1与构建元数据+...);再通过git tag --list ${TAG}检查该 tag 是否已存在,存在则直接退出。 - 批量更新所有子模块的 go.mod:脚本用
find . -mindepth 2 -type f -name 'go.mod'找出全部子模块(如extra/redisotel、example/autopipeline等),对每个go.mod用 sed 把其中github.com/redis/go-redis...相关依赖的版本号统一替换为新 TAG,随后在子模块目录内执行go mod tidy -compat=1.24。 - 更新 version.go:用 sed 把
return "旧版本"改写为return "新版本"(注意去掉 TAG 的前导v)。
脚本结尾会打印Done. Review changes with 'git diff' and commit when ready.,并明确不 commit、不 push、不切换分支。你可以对照 extra/redisotel/go.mod 看到替换后的真实形态:require中同时出现github.com/redis/go-redis/extra/rediscmd/v9 v9.23.0-beta.1与github.com/redis/go-redis/v9 v9.23.0-beta.1,且保留指向仓库根目录的replace github.com/redis/go-redis/v9 => ../..本地替换指令。
顺带一提,仓库还提供 scripts/bump_deps.sh,其职责不同:它在每个子模块内执行go get -d && go mod tidy,用于日常同步依赖,而不是发布版本号,注意不要混淆。
第五步:验证(仅 dry-run)
版本提升完成后,运行 scripts/tag.sh 的dry-run模式做发布前检查。该脚本默认就是 dry-run(DRY_RUN=1),只有显式传入-t才会真正执行 git 命令:
./scripts/tag.sh vX.Y.Z # DRY RUN — must pass cleanly; never add -t here make build git diff --stat # review version.go, submodule go.mod, RELEASE-NOTES.md从 scripts/tag.sh 源码看,dry-run 阶段会执行两类硬校验:
- version.go 校验:用
grep -Fq "\"${TAG#v}\"" version.go检查version.go是否已包含去掉v前缀的目标版本,不匹配则报错退出; - 所有 go.mod 校验:遍历仓库内每一个
go.mod,用 awk 解析require块中所有github.com/redis/go-redis开头的依赖,逐一比对版本号是否等于${TAG},任何不一致都会计数并最终exit 1。
校验通过后,脚本计算需要打 tag 的公共包目录(PACKAGE_DIRS,用grep -E -v "example|internal"显式排除 example 与 internal 目录),然后以 dry-run 方式逐条打印「DRY-RUN: Would execute: git tag ...」与「DRY-RUN: Would execute: git push origin ...」,覆盖根 tag 以及每个公共子模块 tag(如extra/redisotel/vX.Y.Z)。
随后运行make build(Makefile 中的build目标执行go build .)确认工程可编译,最后用git diff --stat审阅改动面:应恰好覆盖 version.go、各子模块go.mod与 RELEASE-NOTES.md。dry-run 指出的任何问题都要在交接前修复。
第六步:交接给维护者
技能在完成上述全部准备(即第五步)后停止,并向维护者报告「发布已暂存(staged)」以及剩余的发布步骤清单,由维护者亲自执行:
- 审阅
git diff,然后提交,提交信息形如chore(release): vX.Y.Z(遵循仓库的 commit-style 约定,且不带 AI-attribution 尾注); - 打开并合并发布 PR;
- 合并后打 tag 并推送:
./scripts/tag.sh vX.Y.Z -t(此刻才动用-t标志); release-drafter会自动发布 GitHub Release,如有必要再与RELEASE-NOTES.md条目对账。
这四步中的每一步都属于「发布(publish)」范畴,全部留给人类维护者;prepare-release的边界就是停在第五步之后,不 commit、不 tag、不 push。
仓库中的发布支撑设施一览
整个发布流程依赖仓库内以下几处真实文件,供读者深入核对:
| 用途 | 文件 | 说明 |
|---|---|---|
| 版本唯一事实来源 | version.go | Version()返回当前版本,当前为9.23.0-beta.1 |
| 版本提升脚本 | scripts/release.sh | 校验 TAG、批量更新子模块 go.mod 并go mod tidy、更新 version.go |
| 打 tag 脚本 | scripts/tag.sh | 默认 dry-run;校验 version.go 与全部 go.mod;-t才真正执行 |
| 发布记录 | RELEASE-NOTES.md | 人工精修、最新条目在最上方 |
| 发布模板 | .github/RELEASE_NOTES_TEMPLATE.md | 小节顺序、链接格式、排除规则、Full Changelog |
| 自动草稿配置 | .github/release-drafter-config.yml | 按 PR 标签自动生成 GitHub Release 草稿 |
| 自动草稿工作流 | .github/workflows/release-drafter.yml | push 到 master 时触发 release-drafter |
| 构建目标 | Makefile | make build用于发布前编译验证 |
| 子模块依赖示例 | extra/redisotel/go.mod | 展示 go-redis 依赖版本被统一改写后的形态 |
总结:一条可复核、可回退的发布前检查链
go-redis 的发布准备流程可以概括为一条「事事可校验」的链条:version.go是版本事实源 →git describe --match 'v[0-9]*'精确锁定根 tag → 模板约束 release notes 格式 →release.sh以正则与 tag 冲突检查守护版本改写 →tag.shdry-run 复核 version.go 与每个子模块 go.mod →make build保证可编译 → 人工审阅 diff 后交接。整个过程中所有自动化脚本都刻意不触碰「推送」这类不可逆操作,把发布权牢牢留在维护者手中——这正是大型开源客户端库把流程纪律内建到工具链中的典型做法。如果你需要维护自己的多模块 Go 项目,这套「准备与发布分离 + dry-run 校验 + 人工交接」的模式同样值得直接借鉴。
- 后端
- 数据库客户端
- 缓存
【免费下载链接】go-redis
Redis Go client
相关推荐
Open3D 版本发布全流程指南:从 CI 构建到 PyPI 与 GitHub Release
Open3D 版本发布全流程指南:从 CI 构建到 PyPI 与 GitHub Release 本篇指南基于 Open3D 仓库中的 docs/release.
计算机视觉图形学3D渲染科学计算Rook 版本发布全流程指南:从 Minor Release 分支创建到 Release Artifacts 发布
Rook 版本发布全流程指南:从 Minor Release 分支创建到 Release Artifacts 发布 本文基于 Rook 仓库 build/rel
云原生存储容器编排运维Wasmer 版本发布全流程指南:从 Release PR 到 crates.io 发布
Wasmer 版本发布全流程指南:从 Release PR 到 crates.io 发布 Wasmer 的版本发布流程已经高度自动化: make release
语言运行时JIT编译
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考