Keep a Changelog 与发布工程

共 18 题
📑 题目列表 18 题
#
★★★

1. Keep a Changelog 1.1 的六种变更类型中 Added、Changed、Deprecated、Removed、Fixed、Security

请说明 Keep a Changelog 1.1 规范中定义的六种变更类型:Added、Changed、Deprecated、Removed、Fixed、Security?

  • 六种类型的含义
  • 各自的适用场景
  • 与 Conventional Commits 的映射

Keep a Changelog 1.1 定义了 CHANGELOG 中每个版本下的标准分类标题:

  • Added:新增的功能。
  • Changed:对现有功能的变更。
  • Deprecated:很快将被移除的功能。
  • Removed:此版本中移除的功能。
  • Fixed:修复的缺陷。
  • Security:安全相关修复。 这些分类让读者能快速判断某版本对自身的影响。它们与 Conventional Commits 的 type 有对应关系(如 feat→Added、fix→Fixed、refactor→Changed),但 CHANGELOG 分类更偏向"面向用户的可读变更",而提交类型更偏向开发过程。

六种分类是 CHANGELOG 的"统一语言",让维护者以一致的结构记录变更,让读者快速定位。工具(如 release-please、standard-version)可自动将提交分类映射到这些标题下,生成结构化 CHANGELOG。保持分类严格一致是 CHANGELOG 可读性的关键。

#
★★★

2. Keep a Changelog 的"Unreleased"段落中当前开发中的变更累积

请说明 Keep a Changelog 中"Unreleased"段落的作用?

  • Unreleased 段落的位置与含义
  • 如何累积当前开发中的变更
  • 与正式发布的关系

Keep a Changelog 规定 CHANGELOG 顶部应有一个名为 Unreleased 的段落,用于累积当前开发周期中尚未发布的所有变更。开发者在合入改动时,将变更记录到 Unreleased 下的对应分类中;当正式发布新版本时,把 Unreleased 段落正式命名为新版本号(如 1.2.0)、标注日期,并开启一个新的空 Unreleased 段落。这样既保证了"当前开发中发生的变更"始终有据可查,又避免发布时遗漏。

Unreleased 段落本质上是"变更日志的暂存区",它把"发布后一次性回忆"的成本摊薄到每次变更,保证 CHANGELOG 的完整性与时效性。它与 Conventional Commits 结合时,可由工具自动从提交历史累积 Unreleased 内容,人工只需在发布时确认并命名版本。

#
★★★

3. Keep a Changelog 的"YANKED"(撤回版本)标记与原因

请说明 Keep a Changelog 中"YANKED"(撤回版本)标记的含义与使用原因?

  • YANKED 标记的格式
  • 使用场景与原因
  • 与发布的协同

"YANKED"(撤回)标记用于表示某个版本虽然已发布但应被撤回,通常是因为该版本存在严重缺陷、安全漏洞或构建问题,不应被用户使用。写法是在版本标题后标注 [YANKED],例如 ## [1.2.0] - 2024-01-01 [YANKED]。被撤回的版本不会从 CHANGELOG 中删除(保留历史记录与原因说明),但会明确告知用户不要使用该版本。通常还会在此后发布一个修复版本或说明撤回归因。

YANKED 标记的价值在于"保留历史 + 明确警告"。删除版本记录会破坏历史可追溯性;而标注 YANKED 既保留版本存在的证据,又旗帜鲜明地告知用户规避。它与 npm 的 npm deprecate、Cargo 的 yank 等生态机制呼应,是发布工程中处理"坏版本"的规范做法。

#
★★

4. Keep a Changelog 的"语义版本对齐"(semantic versioning alignment)

请说明 Keep a Changelog 与语义版本(SemVer)对齐的原则?

  • CHANGELOG 与版本号的对应关系
  • 如何通过 CHANGELOG 判断版本升级类型
  • 对齐的工程价值

Keep a Changelog 与 SemVer 对齐意味着 CHANGELOG 中记录的变更类型应与版本号升级一致:若 CHANGELOG 中有 Added 新功能,版本应递增 MINOR;若有 Fixed 修复,递增 PATCH;若有 Deprecated/Removed 或 Breaking Changes,递增 MAJOR。反过来说,CHANGELOG 是版本号的有力依据——读者可通过某版本 CHANGELOG 中的分类判断该版本属于哪类升级。规范化工具通过解析提交内容同步更新 CHANGELOG 与版本号,实现两者天然对齐。

对齐的好处是版本号、CHANGELOG、提交历史三者互为印证,消除"版本号说 MINOR、CHANGELOG 却有破坏性变更"的矛盾。当 CHANGELOG 由工具从 Conventional Commits 自动生成时,对齐是天然成立的。工程上应避免手工维护 CHANGELOG 与版本号不同步。

#
★★

5. Keep a Changelog 在 monorepo 的多包协同

请说明 Keep a Changelog 在 monorepo 多包场景下的协同方式?

  • monorepo 中多个包的 CHANGELOG 组织
  • 统一 vs 分包的 CHANGELOG
  • 与发布工具的结合

在 monorepo 中,多个包(package/subpackage)共享同一仓库,CHANGELOG 的组织有两种策略:一是每个包维护自己的 CHANGELOG(package-level),二是仓库层面维护统一 CHANGELOG,或两者结合。包级 CHANGELOG 更精确,便于用户按需查看某个包的变更;仓库级 CHANGELOG 便于统筹全局。工具如 release-please 支持 monorepo 模式,为每个包独立管理版本号与 CHANGELOG,并生成独立的发布 PR。monorepo 通常结合 changesets 等工具,让每个变更附上影响包声明,自动为对应包更新 CHANGELOG 与版本。

多包协同的核心是"变更归属到具体包"。如果变更未声明影响哪个包,工具无法正确更新对应包的 CHANGELOG 与版本。因此需要通过在提交中标注 scope(如 feat(pkg-a): ...)或使用 changesets 的变更文件来建立"变更→包"的映射。这样每个包都能独立发布、独立记录变更。

#
★★

6. Keep a Changelog 的"升级指南"(Upgrade Guide)章节

请说明 Keep a Changelog 中"升级指南"(Upgrade Guide)章节的作用?

  • Upgrade Guide 的作用
  • 与 CHANGELOG 的关系
  • 对用户迁移的价值

升级指南(Upgrade Guide)是一个用于指导用户从旧版本迁移到新版本的章节,通常针对 MAJOR 升级或包含破坏性变更的版本。它详细说明"需要修改哪些代码、如何迁移、替代方案是什么",比 CHANGELOG 中简短的变更条目更深入。Keep a Changelog 本身聚焦于"变更的事实记录",而升级指南聚焦于"如何应对这些变更",两者互补。CHANGELOG 中可链接到升级指南,或直接在 CHANGELOG 中为破坏性变更提供详细说明。

单独的 CHANGELOG 条目(如"移除 X API")不足以让用户完成迁移,升级指南提供了可操作的步骤。良好实践是:在发布 MAJOR 版本时同时提供升级指南文档,并在 CHANGELOG 中链接。这既能降低用户升级成本,也体现维护者对破坏性变更的负责。

#
★★

7. GitHub Releases 与 GitLab Releases 的发布产物管理

请说明 GitHub Releases 与 GitLab Releases 的发布产物管理方式?

  • 两者的功能
  • 发布产物(二进制、附件)的管理
  • 与 git tag 的关系

GitHub Releases 和 GitLab Releases 都是基于 git tag 的发布功能,用于发布说明(release notes)与可下载的发布产物(二进制文件、安装包等)。GitHub Releases 允许为 tag 创建发布说明,支持上传附件(如 .zip、.exe、数据库镜像),并可标记为 pre-release 或 draft;标记后会在仓库页面醒目展示。GitLab Releases 类似,支持通过 CI/CD 流水线自动创建 Release,并可用 release-cli 上传产物。两者都支持通过 API 或自动化工具(如 semantic-release、release-please)创建 Release。

Release 功能把"git tag(代码标记)+ 变更说明(CHANGELOG 内容)+ 发布的二进制产物"整合到一处,形成面向用户的发布门户。相比仅打 tag,Release 提供了更完整的发布载体与分发渠道。工程上常将版本号、CHANGELOG 条目与 Release 自动关联,实现"一次发布,处处同步"。

#
★★

8. npm publish、PyPI upload、Maven Central deploy 的凭据管理

请说明 npm publish、PyPI upload、Maven Central deploy 等发布操作的凭据管理?

  • 发布凭据的类型
  • 安全存储与 CI 集成
  • 最小权限与轮换

发布到各包管理器需要凭据:npm 使用 token(可配置为只读/发布权限),PyPI 使用 API token 或用户名密码,Maven Central 使用 GPG 签名密钥与 Sonatype 凭据。安全做法是:将凭据以密文形式存储在 CI 平台的密钥管理(如 GitHub Actions secrets、GitLab CI variables),在 CI 中通过环境变量注入发布命令,绝不硬编码进仓库。同时遵循最小权限原则(如 npm 用 --publish 权限的 token 而非读写全部)、定期轮换凭据、对发布步骤进行审计。

发布凭据是高价值资产,一旦泄露可导致恶意代码被发布到公共仓库。工程上应强调"凭据只在 CI 中、且只用于发布",并配合权限隔离(发布 job 与普通 job 分离)、密钥轮换、以及发布步骤的审查。GPG 密钥还需妥善保管私钥,防止丢失或泄露。

#
★★

9. 包签名(cosign、GPG)与 Sigstore 的发布签名

请说明包签名(cosign、GPG)与 Sigstore 的发布签名机制?

  • 包签名的目的
  • cosign 与 GPG 的对比
  • Sigstore 的免密钥签名

包签名用于验证发布产物的来源与完整性,防止伪造或篡改。GPG 签名用发布者的私钥对产物签名,使用者用公钥验证;cosign 是云原生签名工具,常用于容器镜像与 OCI 产物,支持密钥接入与 Sigstore 集成。Sigstore 提供免密钥的签名基础设施:通过 Fulcio(免密钥证书签发)与 Rekor(透明日志)实现"无密钥签名 + 可审计验证",配合 OpenID Connect 身份验证,让开发者无需管理长期密钥即可签名。验证端通过入口(如 cosign verify)校验签名与透明日志。

传统 GPG 的问题在于密钥管理负担与信任链建立;Sigstore 通过"一次性短期证书 + 透明日志"降低门槛并提升可审计性。包签名在供应链安全(SLSA 的完整性与来源)中关键,尤其对公共仓库的发布(如 npm、PyPI)日益重要。工程上应在发布流水线中集成签名与验证步骤。

#
★★

10. 发布的"原子性"(atomicity)与"幂等性"(idempotency)

请说明发布的"原子性"(atomicity)与"幂等性"(idempotency)?

  • 原子性的含义
  • 幂等性的含义
  • 两者在发布中的价值

原子性(atomicity)指发布操作要么全部成功、要么全部失败,不留中间状态。例如把"更新版本号 + 生成 CHANGELOG + 打 tag + 推送发布"作为一个整体,若中途失败则整体回滚,避免出现"版本号已改但 tag 未打"的不一致。幂等性(idempotency)指重复执行同一次发布不会产生不同结果,例如重跑发布流水线不会重复打 tag、重复发布相同版本、或覆盖已发布产物。实现上通过"先检查后执行"(如 tag 已存在则跳过)、基于版本号的唯一性判断、以及发布前校验来实现。

原子性与幂等性保障发布流水线的可靠性。无原子性会导致发布半途而废时仓库状态混乱;无幂等性会导致重试时产生重复 tag、重复发布或误覆盖。工程上应把发布设计为"可安全重试"的流程,并在发布前校验版本唯一性、tag 存在性等前置条件。

#
★★

11. 发布的"回滚"(rollback)策略中 yank、deprecated、unpublish

请说明发布的回滚(rollback)策略:yank、deprecated、unpublish 的差异与适用场景?

  • 三种回滚手段的含义
  • 各自的风险与适用场景
  • 推荐做法

三种方式:

  • yank(如 Cargo、npm 的标记):将版本标记为"已撤回",但产物仍存在,新用户默认不会安装,已安装用户不受影响。风险低,但无法阻止已安装用户。
  • deprecated(弃用):在版本上标注弃用警告,用户安装时看到提示,但版本仍可安装。适合"版本有隐患但不必彻底移除"的场景。
  • unpublish(取消发布):彻底删除版本,如 npm unpublish。风险最高,会破坏已依赖该版本的用户,且各平台有严格的时间/条件限制(如 npm 仅允许 72 小时内 unpublish 且无依赖)。

回滚策略的核心权衡是"纠正错误"与"避免破坏已使用者"。多数学者推荐优先用 yank 或 deprecated 而非 unpublish,因为 unpublish 会破坏依赖方。工程上应尽量减少需要回滚的发布,通过更充分的预发布验证(CI、staging、canary)降低坏版本概率。紧急回滚时,yank 通常是最安全的第一步。

#
★★

12. 版本号与 git tag 的"双向同步"(version ↔ tag)的 CI 强制

请说明版本号与 git tag 的"双向同步"(version ↔ tag)以及 CI 如何强制这一同步?

  • 双向同步的含义
  • CI 如何强制二者一致
  • 防止不一致带来的问题

"双向同步"指版本号与 git tag 必须一一对应:version 文件/Cargo.toml/package.json 中的版本号应与对应 tag(如 v1.2.3)一致,且一个版本号对应一个 tag。CI 可通过以下方式强制:发布 job 中校验 tag 名与 package 版本号相等、校验 tag 指向的 commit 恰好是版本号变更的 commit、以及发布前检查目标 tag 是否已存在。这样可以防止"tag 说 1.2.3 但包内版本是 1.2.4"这种不一致。

版本号与 tag 不一致会导致发布工具的推算混乱、回滚困难、以及用户对版本来源的困惑。CI 强制同步把"必须一致"变成可验证的硬门禁。工程上常由单一工具(如 release-please、semantic-release)同时生成版本号变更与 tag,从源头保证同步。

#
★★

13. Git tag 的"pre-release"(rc、alpha、beta)的命名约定

请说明 Git tag 中 pre-release(rc、alpha、beta)的命名约定?

  • pre-release 的命名约定
  • 与 SemVer pre-release 的对应
  • 在 CI 中的应用

Git tag 的 pre-release 命名通常与 SemVer 的 pre-release 标识对应,如 v1.0.0-alpha.1v1.0.0-beta.2v1.0.0-rc.1。约定类型:alpha 为早期内测、beta 为公开测试、rc(release candidate)为发布候选。命名应保持统一格式(如 v<MAJOR>.<MINOR>.<PATCH>-<pres><.n>),以便工具解析。CI 可根据 tag 判断发布渠道:pre-release tag 触发预发布流水线(如仅发布到 staging 或 pre-release 渠道),正式 tag 触发正式发布。

pre-release tag 让人与工具都能识别"这还不是正式版本"。统一命名约定使 CI 能可靠地根据 tag 分流(pre-release vs release),也避免 pre-release 误触发正式发布。工程上应约定后缀与递增规则,并保证 tag 与版本号的 pre-release 标识一致。

#
★★

14. Git tag 的"锁定"(lock)中保护已发布 tag 防止误删

请说明对已发布 git tag 的"锁定"(lock)保护,防止误删或误改?

  • 已发布 tag 的重要性
  • 锁定 tag 的手段
  • 误删后果与恢复

已发布的 tag 是发布历史的锚点,一旦被误删或移动,会导致用户无法获取对应版本、发布记录丢失、甚至与已发布产物失联。保护手段包括:分支保护规则中标记 tag 为"protected"(GitLab 支持 protected tag,GitHub 可在仓库设置中限制 tag 的操作权限)、限制只有指定角色/CI 才能创建/删除 tag、以及禁止对已发布 tag 强制 force-push。误删 tag 可尝试用 reflog 找回(若 tag 未指向被 GC 的提交),但若 tag 指向的提交已被 GC 则无法恢复。

tag 的不可变性是版本发布可靠性的基础。锁定 tag 是防御性措施,防止人为或脚本错误破坏发布记录。工程上应把 tag 操作纳入权限控制,并将 tag 的创建与 CI 发布流程绑定,避免手工误操作。

#
★★

15. 版本号的"deprecation policy"中 API 的 EOL(end of life)周期

请说明版本号的"deprecation policy"与 API 的 EOL(end of life)周期?

  • EOL 周期的含义
  • 如何制定 deprecation policy
  • 对用户的影响

版本号的 deprecation policy 定义了 API 从发布到被标记弃用、再到正式停止维护(EOL)的完整周期,以及各阶段的支持策略。通常包括:明确支持哪些版本(如仅支持最近 N 个 MAJOR)、弃用公告的提前量、安全修复的时限。EOL 是指某个版本不再接收任何修复(包括安全修复)的时间点。良好实践是提前公布 EOL 计划、提供迁移路径、并在 EOL 时明确告知用户。

清晰的 deprecation/EOL policy 建立用户信任,让用户能规划升级。工程上应发布支持矩阵(supported versions、EOL dates),并随版本演进更新。过于激进或含糊的 EOL 政策会增加用户风险与维护混乱。

#

16. Keep a Changelog 与 GitHub Releases 的发布说明协同

请说明 Keep a Changelog 与 GitHub Releases 的发布说明如何协同?

  • 两者内容的对应关系
  • 如何避免重复维护
  • 自动化同步

Keep a Changelog 的 CHANGELOG 是"单一事实来源"(source of truth),而 GitHub Releases 的发布说明应尽量复用 CHANGELOG 内容,避免重复维护。协同方式有二:一是从 CHANGELOG 中提取对应版本的条目作为 Release 说明;二是完全由工具(release-please、semantic-release)基于 Conventional Commits 同时生成 CHANGELOG 与 Release 说明,保证二者一致。这样发布时仅需维护一份变更记录。

若 CHANGELOG 与 Release 说明各自独立维护,极易因版本迭代而不同步、内容重复或遗漏。将 CHANGELOG 作为源头、Release 自动引用,可省去重复劳动并保证一致性。工程上应选择单一来源与自动同步机制。

#

17. Keep a Changelog 与 release notes 自动生成工具(towncrier、release-please)

请说明 Keep a Changelog 与 release notes 自动生成工具(towncrier、release-please)的协同?

  • towncrier 与 release-please 的机制
  • 与 Keep a Changelog 的关系
  • 各自适用场景

towncrier 是一个"变更文件驱动"的工具:每个 PR/变更在 news/ 目录下写入一个变更片段文件(如 .added.bugfix),发布时由 towncrier 汇总这些片段并写入 CHANGELOG,同时从版本历史中移除已发布片段。release-please 则基于 Conventional Commits 从提交历史自动推算版本并生成 CHANGELOG。两者都与 Keep a Changelog 结构兼容,只是数据来源不同:towncrier 依赖显式变更片段,release-please 依赖提交消息语义。

选择取决于团队对"变更来源"的偏好。towncrier 的片段文件更精确(不受提交消息质量影响),适合需要精确控制的团队;release-please 更自动化、依赖 Conventional Commits 纪律。两者都使 CHANGELOG 保持 Keep a Changelog 结构,且与发布的版本号对齐。

#

18. 版本号的"停止维护"(EOL)公告与迁移路径

请说明版本号"停止维护"(EOL)的公告与迁移路径?

  • EOL 公告的时机与内容
  • 迁移路径的提供
  • 对用户与维护者的价值

当某个版本/系列进入 EOL(停止维护)时,维护者应提前发布公告,说明:EOL 的具体日期、受影响的版本、不再提供哪些修复(尤其是安全修复)、以及推荐的迁移路径(升级到哪些受支持版本)。迁移路径应尽量具体,例如提供升级指南、自动迁移工具、或兼容层。公告应通过多渠道(CHANGELOG、Release notes、文档、邮件列表)传达,以便用户及时规划。

EOL 公告与迁移路径是"负责任弃用"的体现。提前、清晰、可操作的公告降低用户风险,避免用户因突然停止维护而陷入无支持的暴露状态。工程上应建立 EOL 计划文档,并在临近时主动提醒仍在使用旧版本的用户。