工程实践

GitHub 发布说明:先自动生成,再改到可读

阅读约 1 分钟

GitHub 发布说明,就是挂在仓库 Releases 页面上每个发布下面的那段描述,而 GitHub 可以根据上一个标签以来合并的 pull request,替你写出第一版草稿。草稿是一份按标签分组的 pull request 标题列表,外加一行贡献者和一个 compare 链接。它准确、完整,但面向的读者是合并这些 pull request 的人。要把它变成项目用户读得懂的东西,需要一个 .github/release.yml 文件和一轮简短的编辑,两者都在下文说明。

生成说明的方式在哪里运行得到什么
“Generate release notes” 按钮网页界面的发布表单上一个标签以来的 PR 标题,按 release.yml 的分类分组
gh release create v1.4.0 --generate-notesGitHub CLI,本地或 CI同一份草稿,一条命令连发布一起创建
POST /repos/{owner}/{repo}/releases/generate-notesREST API只返回草稿文本,不创建发布,供你自己的流水线使用
release-please、semantic-release、conventional-changelogGitHub Actions 或发布任务从提交信息而不是 PR 标题生成说明,外加版本号递增和一份 CHANGELOG.md
手写发布表单你输入什么就是什么,没有指向 pull request 的链接

如何在 GitHub 上自动生成发布说明?

打开发布表单,选择或新建标签,然后点击 “Generate release notes”。GitHub 会找到上一个发布标签,列出两个标签之间合并到目标分支的所有 pull request,每个 pull request 写一行,包含标题、作者和编号。在命令行里,gh release create <tag> --generate-notes 做的是同一件事,并且一步就把发布公开;--notes-start-tag 可以指定范围从哪个更早的标签开始。

这个功能的数据来源是 pull request,所以一个直接往 main 上提交、不走 pull request 的仓库,得到的是一份空草稿。Squash 合并没有问题,因为 pull request 依然存在。把一条长期分支通过合并提交并入 main,只会产生一行,对应那个负责合并的 pull request,之前合并进该分支的那些 pull request 一个都不会出现。如果范围看起来不对,常见原因是上一个标签缺失或顺序错乱;生成器用的是目标分支上最新的发布,一个没有关联发布的标签会被忽略。

GitHub 文档中关于自动生成发布说明的页面讲解了这个按钮,gh release create 手册则覆盖了 CLI 的各个参数。

release.yml 改变了什么?

.github/release.yml 告诉生成器哪些 pull request 要排除,其余的如何分组。没有它,所有 pull request 都挤在一个列表里,Dependabot 也不例外。有了它,pull request 会按标签归入各个分类,而带有特定标签或由机器人提交的噪音就消失了。

# .github/release.yml
changelog:
  exclude:
    labels:
      - skip-changelog
    authors:
      - dependabot
      - renovate
  categories:
    - title: Breaking changes
      labels:
        - breaking-change
    - title: New
      labels:
        - enhancement
    - title: Fixed
      labels:
        - bug
    - title: Other changes
      labels:
        - "*"

有三条规则值得记住。分类是按顺序匹配的,所以一个同时带有 breaking-change 和 bug 的 pull request,会归入第一个匹配的标题下。如果你希望没有标签的 pull request 也出现,"*" 这个兜底项是必需的;没有它,它们会被悄悄丢掉。另外,这个文件只影响生成器之后写出的内容,改动它不会碰已经存在的说明。已经为 CI 里的体验日志检查给 pull request 打过标签的团队,可以在这里复用同一套标签。

为什么自动生成的 GitHub 发布说明读起来很差?

因为 pull request 标题是写给审阅者的,写在改动完成之前,描述的是代码。“Refactor export worker to use streaming” 是一个好的 pull request 标题,却是一条没用的发布说明,因为读发布的人想知道的是,超过 100 MB 的导出不再超时了。

第二个原因是,每个合并的 pull request 都占同样的分量。一行改错别字的修复,和一套新的认证流程并排放着,顺序只取决于谁先合并。读者得扫完整个列表,才能找到真正和自己有关的那三行。

第三个原因是草稿没有开头。四十行列表上面没有一句话,没人能看出这个版本是否可以放心升级、是否需要迁移,还是只为了锁定一个依赖而存在。

编辑这一步应该改什么?

保留生成的列表并往上添加,而不是从头重写。这份列表是审计线索;pull request 编号是别人从某一行追溯到 diff 的途径。编辑这一步补上的,是生成器无从知道的部分。

  1. **在顶部写两三句话。**这个版本是为了什么,升级是否需要做什么,先读哪一部分。如果有破坏性变更,第一行就说。
  2. 把读者会在意的那几行改成读者视角:消失的症状、现在能做的事、挪了位置的设置。行末保留 pull request 编号。如何写发布说明讲了完整的形态;缺陷修复发布说明专门讲修复条目怎么写。
  3. 把读者必须做的事单独放一节,附上确切的命令或设置。一条埋在 “Other changes” 列表里的破坏性变更,一周后就是一张支持工单。
  4. **生成的列表一条都不要删。**太长的话可以折叠到一个 “Full list of changes” 标题下,但要留着,因为项目自己的贡献者会用它。

照这样做,一个常规版本的 GitHub 发布说明大约十分钟就能编辑完,而且两类读者都照顾到了:顶部的摘要给用户,下面的列表给贡献者。

GitHub 发布应该当作你的体验日志吗?

只有当每个需要了解变化的人都已经在读 Releases 页面时才可以。对于开发者通过包管理器使用的库,这通常成立,Releases 页面加上仓库里的一份 CHANGELOG.md 就够了。对于用户从不打开 GitHub 的产品则不成立:发布页面写在错误的层级上,藏在一个面向开发者的 URL 后面,而没有 GitHub 账号的人也没法订阅它。

站得住的模式是,把 GitHub 发布当作面向开发者的记录,把产品体验日志当作面向读者的记录,并让发布链接到对应的体验日志条目。Git 标签、发布与体验日志讲了这三份记录如何对齐。如果产品体验日志也是从同一批提交生成的,体验日志生成器能为那一侧也给出一份草稿,而体验日志与发布说明的区分则说明了哪些细节该放在哪一边。

发布说明本身怎么做版本管理?

GitHub 上一个已经公开的发布随时可以编辑,而且页面上看不到任何编辑历史。对大多数项目来说这没问题。对于说明是合规部门或客户所依赖的记录的项目,两个习惯会有帮助:把权威文本放在仓库里(CHANGELOG.md,或者一个 docs/releases/ 目录),让发布正文指向它;如果你的套餐支持,就为仓库启用不可变发布,这样标签和附件在公开后就会被锁定。GitHub 文档中关于管理发布的页面介绍了草稿、预发布和 “latest” 标记,后者默认按语义化版本顺序分配,除非你手动设置。

FAQ

GitHub 发布说明可以自动生成吗? 可以。发布表单上的 “Generate release notes” 按钮、gh release create 的 --generate-notes 参数,以及 generate-notes 这个 REST 端点,都会根据上一个发布标签以来合并的 pull request 生成一份草稿。.github/release.yml 文件控制分组和排除规则。

为什么我生成的发布说明是空的? 生成器列出的是 pull request,所以直接提交到分支的改动不会出现,而且范围的起点是目标分支上最新的发布,而不是最新的标签。请确认上一个发布存在,并且改动是通过 pull request 合并进来的。

release.yml 对已经存在的发布生效吗? 不生效。它只影响文件就位之后生成的说明。已有发布的正文会保持原样,直到有人去编辑它。

我该用 GitHub 发布说明还是 CHANGELOG.md? 如果项目既有贡献者又有用户,两个都要。发布页面是开发者查看的地方,也是附件所在的地方;仓库里的文件在迁离 GitHub 之后依然存在,而且可以离线阅读。生成发布说明,保持文件更新,并让两者互相链接。


本文的技术内容未经独立审核。如有错误,请告诉我们,我们会更正。

changeloop 相关页面: changelog 生成器, 开发者文档

changeloop
打造闭环 changelog 的团队。用户提出需求,你的团队交付,提出需求的人得知结果。