Backstage Changesets 机制与 @backstage/ui 变更集规范:从提交到发布的版本管理实战指南

原创2026-10-09 22:12:21741 阅读
文章标签:开发者门户后端前端

Backstage Changesets 机制与 @backstage/ui 变更集规范:从提交到发布的版本管理实战指南

导读

本文围绕 Backstage 仓库根目录下的 .changeset/ 变更集(Changeset)机制展开,系统讲解这一多包 Monorepo 项目如何借助 changesets 工具链管理数百个 npm 包的版本号与 CHANGELOG 生成。你将掌握:changeset 文件的标准格式与命名规则、@backstage/ui 专属的变更集书写规范(含 **Affected components:** 与 **Migration:** 必需项)、何时需要提交变更集、如何通过 yarn changeset 创建变更集,以及从 pre-release 模式到 "Version Packages" 合并发布的完整链路。文中所有结论均以当前仓库中的文档、配置与真实变更集文件为证据。

一、什么是 Changesets:.changeset/ 目录的定位

在 Backstage 这种包含 packages/ 与 plugins/ 两大 workspace 的巨型 Monorepo 中,一次 PR 往往同时改动多个会被发布到 npm 的包。如果不加以约束,每次发版前都需要人工核对"哪些包升了什么版本、CHANGELOG 写了什么",极易遗漏与出错。

Changesets 的解法是把"版本决策"前置到开发者提交代码的那一刻:每个需要发版的改动,都伴随一个 Markdown 变更集文件,其中声明受影响的包名与语义化版本类型(patch/minor/major),并用一段面向用户的语言描述变更内容。这些文件在每次发版时被统一消费、转化为各包真实的版本号与 CHANGELOG 条目。

仓库对 .changeset/ 目录的官方定位在 docs/contribute/project-structure.md 中有明确说明:

该目录包含自上次发布以来项目发生了哪些变更的文件。这些文件由人工添加,但由 changesets 管理,并会在每次新版本发布时被移除。它们本质上是 CHANGELOG 的构建模块。

目录内当前实际包含三类文件,共同构成完整的版本管理闭环:

文件类型 作用 仓库实例
README.md 变更集格式指南(含 @backstage/ui 专属规范) .changeset/README.md
*.md 变更集 每一条待发布变更的记录 如 .changeset/afraid-showers-sip.md、.changeset/notifications-persist-metadata.md
config.json / pre.json 工具配置与预发布状态 .changeset/config.json、.changeset/pre.json

二、变更集的标准格式:front matter 声明包与版本类型

每一条变更集都是一个 Markdown 文件,由三部分构成:YAML front matter、摘要正文、可选的结构化区块(迁移说明、受影响组件等)。

2.1 front matter:包名 + 语义化版本类型

front matter 中的每一项以包名作为键,以 patch、minor、major 作为值,声明该包应如何升级。从 CONTRIBUTING.md 与仓库实际变更集看,规则如下:

  • 包处于 0.x 阶段时:破坏性变更用 minor,其余用 patch;
  • 包已到 1.0.0 及以上时:破坏性变更用 major,向后兼容的 API 新增用 minor,其余用 patch;
  • 一条变更集可以同时声明多个包,例如 .changeset/afraid-showers-sip.md 同时声明了 4 个包的升级:
---
'@backstage/cli-module-catalog': minor
'@backstage/cli-module-scaffolder': minor
'@backstage/cli-module-search': minor
'@backstage/cli-node': patch
---

Add intent-based CLI modules for catalog, scaffolder, and search.

多个包可以声明不同的版本类型,系统会为每个包独立计算新版本号。

2.2 摘要正文:面向用户、而非面向贡献者

变更集正文是生成 CHANGELOG 的唯一素材来源,因此 CONTRIBUTING.md 强调:变更集消息是写给 Backstage 使用者(adopter)看的,不是写给贡献者看的。它应描述"从用户视角发生了什么变化",而不是内部如何实现:

  • 不应引用函数名、类名、变量名等内部实现细节(公开 API 名除外);
  • 描述应具体清晰。文档给出了正反例:
<!-- 反例:描述模糊,无法写入 CHANGELOG -->
Fixed table layout

<!-- 正例:描述具体,用户能判断是否影响自己 -->
Fixed bug in EntityTable component where table layout did not readjust properly below 1080x768 pixels.

2.3 破坏性变更:必须用 BREAKING 显式标记

凡是类型检查器发现不了的破坏性变更(例如函数从同步变为返回 Promise、构造函数的参数签名变化),必须在正文中以加粗的 BREAKING 开头显式标记,且通常应附上需要用户执行的迁移 diff。仓库实例可参考 .changeset/README.md 中 Table API 的示例,以及 CONTRIBUTING 中关于 FluxCapacitor 的示例——它要求附上对 packages/backend/src/plugins/catalog.ts 的 diff。

三、@backstage/ui 变更集专属规范(核心内容)

.changeset/README.md 的独特价值在于:它为 @backstage/ui 这个包定制了比通用 changesets 更严格的提交规范。@backstage/ui 是仓库自 0.18.0 起维护的统一 UI 组件包(见 packages/ui/package.json,组件目录见 packages/ui/src/components,含 Button、Card、Table、Combobox、Dialog、Select 等 40 余个组件)。因此它的 CHANGELOG 需要足够结构化,让使用者一眼看出改动了哪个组件、是否需要迁移。

3.1 完整格式模板

---
'@backstage/ui': patch
---

Brief summary

Optional description with code examples.

**Migration:**

Migration instructions (breaking changes only).

**Affected components:** Button, Card

3.2 三条必需规则

按 .changeset/README.md 的规定,@backstage/ui 的变更集必须满足:

  1. 结尾必须是 **Affected components:** 加逗号分隔的组件名列表——每个变更集都必须声明受影响的组件,缺一不可;
  2. 破坏性变更必须包含 **Migration:** 小节——给出迁移指引(仅破坏性变更需要);
  3. 正文内部禁止使用标题(##、###)——一律使用加粗标记(bold markers)组织结构,以保证 CHANGELOG 的版面一致性。

3.3 两个官方示例

普通修复(只需摘要 + 受影响组件):

Fixed button hover state

**Affected components:** Button

破坏性变更(摘要 + Migration + diff + 受影响组件):

**BREAKING**: New Table API

**Migration:**

Update imports:

```diff
- import { Table } from '@backstage/ui';
+ import { Table, type ColumnConfig } from '@backstage/ui';
```

**Affected components:** Table

仓库中 @backstage/ui 的真实变更集正是按此规范书写的。例如 .changeset/ui-fix-combobox-keyboard-search.md 用大段用户视角描述修复了 Combobox 的键盘筛选行为(含 screen reader 播报行为),并以 **Affected components:** Combobox 收尾;另一个对 @backstage/ui 的变更 .changeset/large-times-join.md 则声明模板改用 @backstage/ui 替代废弃的 @material-ui/core。

四、仓库级工具配置与验证机制

4.1 .changeset/config.json:工具行为配置

.changeset/config.json 是 changesets CLI 的配置入口,各字段含义如下:

字段 当前值 含义
changelog ./backstage-changelog.js 指定自定义 CHANGELOG 生成函数,指向同目录的 .changeset/backstage-changelog.js
commit false 不自动创建版本提交
linked [] 不启用跨包联动版本(各包独立升级)
access public 发布访问级别为公开
baseBranch master 版本分支基于 master
updateInternalDependencies patch 内部依赖同步升级时采用 patch
ignore [] 不忽略任何包

自定义的 .changeset/backstage-changelog.js 复用了 @changesets/cli/changelog 默认的 getReleaseLine,仅覆写了 getDependencyReleaseLine:将"每个依赖更新各列一条带 commit SHA 的条目"压缩为一条统一的 - Updated dependencies 列表,以牺牲 commit SHA 为代价换取 CHANGELOG 的简洁性。

4.2 变更集合法性校验:scripts/verify-changesets.js

仓库用 scripts/verify-changesets.js 在 CI 侧校验所有变更集。该脚本会:

  • 读取 .changeset/ 下所有 .md 文件(排除 README.md),用 @changesets/parse 解析 front matter;
  • 检查是否存在对私有包(example-app、example-backend、e2e-test、storybook、techdocs-cli-embedded-app)的发布声明;
  • 一旦命中,即输出 Changeset verification failed! 并以非零码退出,阻止合并。

这意味着:变更集只能针对会被发布到 npm 的公开包,示例类、测试类私有包即使被改动也不需要(也不允许)声明变更集。

4.3 预发布模式:.changeset/pre.json

.changeset/pre.json 表明仓库当前处于预发布模式:"mode": "pre"、"tag": "next",并记录了进入该模式时全部包的 initialVersions 与已消费/待消费的变更集清单。mode 的语义(见 docs/publishing.md):

  • "mode": "pre":处于 next 预发布线,此时合并产生的版本会打上 next 标签;
  • "mode": "exit" 或文件不存在:表示主线路(mainline)发布。

维护者通过 yarn changeset pre enter next 进入预发布模式、yarn changeset pre exit 退出预发布模式。

五、何时需要变更集

并非所有改动都需要变更集。综合 CONTRIBUTING.md 与 .changeset/README.md,判定规则如下:

需要变更集的情形:

  • 对 packages/ 或 plugins/ 下任一非 private 包做出了符合 SemVer 语义(patch/minor/major)的改动;
  • 新增包——变更集是让新包进入下一次发布清单的触发机制;
  • 对已发布包的 README.md 的修改(需同步到 npm 页面)。

不需要变更集的情形:

  • 仅改动测试代码或源码内联注释(不影响发布产物);
  • 改动本身不影响该包的对外版本,例如内部重构且对外行为完全一致(但按 CONTRIBUTING 精神,凡是影响发布版本的都应提交);
  • 对私有包(example-app 等)的改动——这类改动会被 scripts/verify-changesets.js 判定为非法。

六、创建变更集的完整流程

按 CONTRIBUTING.md 的指引,贡献者流程如下:

  1. 在仓库根目录运行 yarn changeset;
  2. 交互式选择本次要包含的包(可多选);
  3. 为每个选择的包选择影响级别:0.x 包破坏性用 minor、其余用 patch;1.0.0+ 包破坏性用 major、兼容新增用 minor、修复用 patch;
  4. 按本节与第三节规范撰写变更集正文;
  5. 将生成的 .md 文件加入 Git 暂存;
  6. 随 PR 一起推送到对应分支。

若改动对象是 @backstage/ui,还需额外遵循第三节的规范:以 **Affected components:** 结尾、破坏性变更补 **Migration:** 小节、正文内不使用标题。

七、从变更集到发布:版本晋升与 CHANGELOG 生成

7.1 发布命令链路

package.json 的 release 脚本展示了变更集被消费的完整命令链:

node scripts/prepare-release.js
changeset version
yarn prettier --write '{packages,plugins}/*/{package.json,CHANGELOG.md}' '.changeset/*.json'
node scripts/create-release-changelog.js
yarn install --no-immutable

其中 changeset version 会读取 .changeset/ 下所有待处理变更集,据此:

  • 计算每个受影响包的新版本号并写入各自 package.json;
  • 将变更集摘要追加到对应包的 CHANGELOG.md;
  • 消费完毕后删除已处理的 .md 变更集文件(与 docs/contribute/project-structure.md 中"每次发布时被移除"的描述一致)。

后续的 scripts/prepare-release.js 负责确定当前主发布版本与 patch 分支(patch/v 前缀)等发布准备逻辑,scripts/create-release-changelog.js 负责汇总生成 docs/releases/ 下的版本变更文档。

7.2 发布触发方式

据 docs/publishing.md:合并 master 的每次提交都会由 CI 检查各公开包是否有新版本,有则自动发布到 npm;而版本晋升本身由合并 "Version Packages" PR 触发(每周例行发布)。发布当天维护者需核对 .changeset/pre.json 的 mode 以确认当前是 next 线还是主线路。

八、真实变更集拆解:三种典型形态

为帮助读者直观掌握写法,以下拆解仓库中三个真实变更集。

形态一:多包、带能力说明(minor + patch 混合)

.changeset/afraid-showers-sip.md 为 catalog/scaffolder/search 三个 CLI 模块声明 minor,为 cli-node 声明 patch,正文列出新增子命令(catalog list、template execute、search 等)及其支持的能力(human-readable/JSON 输出、可重复的 key=value 过滤器、逗号分隔字段等),并说明 cli-node 提供共享解析器。这类"新增功能"变更适合 minor。

形态二:单包行为修复(patch)

.changeset/notifications-persist-metadata.md 声明 @backstage/plugin-notifications-backend 为 patch,用两句用户视角语言说明通知元数据被持久化、读取时返回,以及重复发送时元数据的覆盖/清除语义。

形态三:前端系统兼容性修复(patch)

.changeset/techdocs-addons-new-frontend-system.md 声明 @backstage/plugin-techdocs 为 patch,说明修复了新前端系统下 TechDocs addons 在独立阅读页与实体文档 Tab 中静默不渲染的问题——修复类变更统一使用 patch。

九、写在最后:变更集的黄金法则

综合 .changeset/README.md、CONTRIBUTING.md 与仓库实际案例,可以总结出四条黄金法则,这也是向 Backstage 提交 PR 时的最低要求:

  1. 凡改公开包、必带变更集:新增包、修改包 README、任何影响发布产物的改动,都要伴随变更集;
  2. 写给使用者,不写给自己:正文描述用户可见的行为变化,不暴露内部函数与实现细节;
  3. 破坏性变更三件套:**BREAKING** 标记 + 清晰迁移说明 + 必要时附 diff;
  4. @backstage/ui 专属纪律:以 **Affected components:** 收尾、破坏性变更含 **Migration:**、正文不出现标题。

遵循这套规范,你的每一次提交都会自动沉淀为准确、可检索、面向使用者的版本记录,让 Backstage 数百个包的发布流程始终可预期、可追溯。更多背景可参阅 docs/contribute/project-structure.md、docs/publishing.md 与 CONTRIBUTING.md。

登录后查看全文
backstage