Go 版本发布说明(Release Notes)工作流:从 doc/next 片段到发布文档的完整指南
本文基于 Go 官方仓库中的 doc/README.md 展开,讲解 Go 项目如何组织、撰写和生成每个版本的发布说明:doc/initial 与 doc/next 两级目录的分工、开发者如何为变更提交(CL)补充发布说明片段、api/next API 变更文件与说明片段的强制对应关系、发布说明的 Markdown 书写规范,以及发布团队如何用 relnote 工具完成合并与生成。读完本文,你能够独立完成一次标准的发布说明编写流程,并理解 Go 发布文档从“碎片化片段”到“单一发布文档”的自动化管线。
doc 目录的发布说明体系:initial 与 next 的分工
doc/README.md 开篇指出,doc 目录下的 initial 与 next 两个子目录专门用于存放发布说明(release notes):
- doc/initial:保存的是模板骨架。例如 doc/initial/1-intro.md 的标题写作 “DRAFT RELEASE NOTES — Introduction to Go 1.N”,并带有占位符 “{Month} {Year}”,表示这是一个尚未填充版本信息的初始模板;
- doc/next:保存的是当前开发周期正在累积的发布说明。当前仓库中 doc/next/1-intro.md 的标题为 “DRAFT RELEASE NOTES — Introduction to Go 1.28”,并声明 “Go 1.28 is not yet released … expected to be released in February 2027”,说明本仓库正处于 Go 1.28 的开发周期。
next 目录的顶层文件采用 数字-主题.md 的命名方式,当前包括:
| 文件 | 对应章节 |
|---|---|
| 1-intro.md | 版本简介 |
| 2-language.md | 语言变更 |
| 3-tools.md | 工具变更 |
| 4-runtime.md | 运行时变更 |
| 5-toolchain.md | 工具链变更 |
| 6-stdlib/ | 标准库变更(目录,见下文) |
| 7-ports.md | 平台移植 |
这种命名并非随意:doc/README.md 说明,开发周期结束时,这些文件会**按文件路径名的排序顺序拼接(concatenated in sorted order by pathname)**合并为最终文档。数字前缀正是为了保证拼接后章节顺序稳定、可预期——这是理解整个发布说明体系的第一个关键机制。
开发者规则:发布说明必须写入 next,而不是注释
doc/README.md 对开发者的第一条硬性规定是:
Do not add RELNOTE=yes comments in CLs. Instead, add a file to the CL (or ask the author to do so).
也就是说,Go 项目放弃了早期“在代码注释里标记 RELNOTE=yes、由机器人代为补写说明”的做法,改为由提交者在 CL 中直接附带(或要求作者附带)一个说明文件。这使发布说明与代码变更在同一个变更中原子地落地,避免了说明滞后或缺失。
stdlib 变更的特殊目录:*stdlib/*minor
并非所有说明文件都放在顶层。doc/README.md 规定:匹配 *stdlib/*minor 这个 glob 的目录中的文件会被特殊处理——
- 文件必须放在与标准库包路径对应的子目录中;
- 这些包路径的标题(headings)会自动生成,撰写者无需手写。
当前 doc/next/6-stdlib/99-minor/ 目录是这一规则的活样本,其结构完整镜像了标准库的包树:
doc/next/6-stdlib/99-minor/
├── 0-heading.md # 自动生成的标题锚点
├── README # 目录用途说明
├── embed/80822.md
├── encoding/base32/20235.md
├── encoding/base64/20235.md
├── flag/65675.md
├── go/parser/79802.md
├── net/29678.md
├── net/http/79040.md
├── net/http/79656.md
├── net/http/80058.md
├── net/http/url/79946.md
├── syscall/68595.md
└── testing/fstest/80822.md
testing/synctest/77320.md
其中 doc/next/6-stdlib/99-minor/README 一句话点明该目录的定位:“API changes and other small changes to the standard library go here.”(标准库的 API 变更和其他小型变更写在这里)。
标题自动生成的实现细节体现在两个 0-heading.md 文件中:doc/next/6-stdlib/0-heading.md 提供章节级标题 ## Standard library {#library},doc/next/6-stdlib/99-minor/0-heading.md 提供小节标题 ### Minor changes to the library {#minor_library_changes},末尾的 {#anchor} 是供自动生成的包路径标题做锚点链接用的。
文件名 = 提案/问题编号
观察上面的文件名(80822、20235、65675、79040……)可以发现,文件名就是 API 提案或 issue 的编号,这直接服务于下一条规则。
api/next 与 doc/next 的强制对应关系
doc/README.md 给出了本项目发布说明体系中最严格的一条规则:
Files in this repo's
api/nextdirectory must have corresponding files indoc/next/*stdlib/*minor. The files should be in the subdirectory for the package with the new API, and should be named after the issue number of the API proposal.
即:api/next 中的每个 API 变更文件,都必须在 doc/next 的 stdlib minor 目录下有对应的说明文件;对应文件放在新增 API 所在包的子目录下,并以提案 issue 编号命名。README 中给的例子是:若存在 6-stdlib/99-minor 目录,则 api/next 中的
pkg net/http, function F #12345
必须对应一个 doc/next/6-stdlib/99-minor/net/http/12345.md。该文件至少要包含一个完整句子或一个 TODO,理想情况下注明负责补全说明的人。
当前仓库里就有完全符合这一规则的成对实例。api/next/65675.txt 的内容为:
pkg flag, func All() iter.Seq[*Flag] #65675
pkg flag, method (*FlagSet) All() iter.Seq[*Flag] #65675
pkg flag, type Flag struct, IsSet bool #65675
它对应 doc/next/6-stdlib/99-minor/flag/65675.md:
The new [FlagSet.All] method returns an iterator over all the flags in the FlagSet;
the function [All] does the same for the global [CommandLine] flag set.
The new [Flag.IsSet] field indicates whether the flag has been set.
可以看到三行 pkg flag … API 描述与一个 flag/65675.md 说明片段一一对应,而说明片段使用的正是下面一节介绍的符号链接写法([FlagSet.All]、[All]、[Flag.IsSet])。
这条规则有自动化测试兜底
对应关系并非仅靠约定,仓库内有专门的检查代码。src/cmd/relnote/relnote_test.go 中的 TestCheckAPIFragments(src/cmd/relnote/relnote_test.go#L21-L39)会遍历 api/next/*.txt 中所有文件,调用 relnote.CheckAPIFile(rootFS, apiFile, docFS, "doc/next") 逐一验证每个 API 文件在 doc/next 下存在对应说明片段:
// Check that each file in api/next has corresponding release note files in doc/next.
func TestCheckAPIFragments(t *testing.T) {
...
files, err := fs.Glob(rootFS, "api/next/*.txt")
...
for _, apiFile := range files {
if err := relnote.CheckAPIFile(rootFS, apiFile, docFS, "doc/next"); err != nil {
t.Errorf("%s: %v", apiFile, err)
}
}
}
从源码结构看,该检查以 -check 标志启用(见 src/cmd/relnote/relnote_test.go#L18),依赖外部包 golang.org/x/build/relnote 提供的 CheckAPIFile 函数完成实际校验。这意味着开发者如果提交了 api/next 文件却忘记附 doc/next 片段,检查会直接报错——README 中的对应关系要求是机器可执行的。
关联提案:/issue/NUMBER 写法与自动 TODO 标记
doc/README.md 还规定:如果你的 CL 实现的是一个已被接受的提案(accepted proposal),必须在发布说明中以 /issue/NUMBER 的形式提及提案的 issue 编号,渲染后会在文本中生成指向该 issue 的链接。如果不想在正文中出现该编号,可以改为 HTML 注释形式:
<!-- go.dev/issue/12345 -->
同时,doc/README.md 提醒:如果某个已被接受的提案被 CL 提及却没有出现在发布说明中,自动化工具会将其标记为 TODO——即使该提案只是新增 API 也不例外。这与上一节的 relnote 工具链(见“发布团队流程”)相呼应:未完成的说明工作会在周期收尾前被系统性揪出来。
发布说明的 Markdown 书写规范
doc/README.md 给出了一套在发布说明 Markdown 中可用的链接/引用形式:
[http.Request] # symbol documentation; auto-linked as in Go doc strings
[Request] # short form, for symbols in the package being documented
[net/http] # package link
#12345 # GitHub issues
CL 6789 # Gerrit changelists
逐条解读:
| 形式 | 含义 |
|---|---|
[http.Request] |
符号文档链接,自动生成,行为与 Go doc 注释中的自动链接一致 |
[Request] |
短形式,用于指代“正在被说明的那个包”内部的符号 |
[net/http] |
包链接 |
#12345 |
issue 链接 |
CL 6789 |
Gerrit 变更列表(changelist)链接 |
实际片段中这套写法随处可见。例如 doc/next/6-stdlib/99-minor/net/http/79040.md 写道:
The long-deprecated [Transport.CancelRequest] now does nothing. Use [NewRequestWithContext] (available since Go 1.13) to create a cancellable request.
[Transport.CancelRequest] 与 [NewRequestWithContext] 都会按 Go doc 字符串的规则自动链接到对应的符号文档;而 doc/next/6-stdlib/99-minor/testing/synctest/77320.md 则用 [testing.(*T).Run] 这种限定形式引用了其他包中的方法。注意:这些链接在 next 阶段只是纯文本标记,由合并/生成阶段解析为真实文档链接。
本地预览:在本地站点查看合并后的 next 内容
doc/README.md 提供了在本地预览“合并后”效果的命令。在仓库根目录下运行:
go run golang.org/x/website/cmd/golangorg@latest -goroot=..
然后访问 http://localhost:6060/doc/next,编辑文件后刷新页面即可看到最新效果。这里 -goroot=.. 指向 Go 源码仓库根目录,本地站点工具会直接读取 doc/next 下全部片段,按前文所述的排序规则拼接并渲染出完整的草稿文档——相当于在不触发正式发布流程的情况下,提前看到 relnote generate 的产物。
发布团队流程:relnote 工具的三步工作流
doc/README.md 的“For the release team”一节描述了周期收尾阶段的操作,核心工具是 relnote(位于外部仓库 golang.org/x/build/cmd/relnote),它直接操作 doc/next 中的文件:
- 收尾前检查:运行
relnote todo,列出所有未完成的发布说明工作(即上文提到的被自动标记的 TODO,包括缺失的说明片段、未提及的已接受提案等); - 生成发布文档:运行
relnote generate,将next中所有.md文件合并为单一文件;随后(尽量)原子地完成两件事——把生成的文件加入 website 仓库的_content/doc目录,同时删除本仓库的doc/next目录; - 开启下一个周期:用
initial的内容填充新的next。在仓库根目录下:
> cd doc
> cp -R initial/ next
然后编辑 next/1-intro.md,把其中的 “Go 1.N / {Month} {Year}” 占位符改为下一版本号与发布日期。
这正好解释了 initial 目录的存在意义:它是每个周期 next 的初始快照。以当前周期为例,initial/1-intro.md 中的 “Go 1.N … {Month} {Year}” 占位符,在 Go 1.28 周期中被替换成了具体的 “Go 1.28 … February 2027”(见 doc/next/1-intro.md),印证了第 3 步的实际执行结果。
小结:一次完整的发布说明生命周期
把 doc/README.md 的规则与仓库现状结合,一次发布说明的完整生命周期是:
- 周期开始:
cp -R initial/ next,更新next/1-intro.md的版本号与日期; - 日常开发:每个 CL 按归属写入
next对应位置——语言/工具/运行时变更进顶层编号文件,标准库小型变更与 API 变更进6-stdlib/99-minor/<包路径>/<issue号>.md;若 CL 触碰了api/next(如 api/next/77320.txt 中pkg testing/synctest, func Subtest(*testing.T, string, func(*testing.T)) #77320),必须有同名编号的说明片段(如 doc/next/6-stdlib/99-minor/testing/synctest/77320.md),且 TestCheckAPIFragments 会持续保证这一约束; - 书写规范:使用符号自动链接、
/issue/NUMBER、issue 与 CL 链接等固定形式,让说明在生成阶段能正确链接到文档与提案; - 周期收尾:
relnote todo清理欠账 →relnote generate合并 → 产物进入 website 仓库,doc/next归档移除; - 本地验证:随时可用本地网站实例在
localhost:6060/doc/next预览合并效果。
整个体系的设计要点在于:以文件路径为唯一事实来源——排序决定章节顺序,目录镜像决定包标题,文件名决定 API 关联,配合自动化检查(CheckAPIFile)与生成工具(relnote),把“发布说明”从人为维护的文档变成了可校验、可合并、可重复执行的工程化流程。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00