nodejs.org 仓库的 Changesets 发布流程:从编写 changeset 文件到自动发布 npm 包
【免费下载链接】nodejs.orgThe Node.js® Website项目地址: https://gitcode.com/GitHub_Trending/no/nodejs.org
nodejs.org 是一个 pnpm monorepo,其中packages/下的包(如@node-core/ui-components、@node-core/rehype-shiki)会被独立发布到 npm。仓库用 Changesets 工具链来描述"这次改动对已发布包意味着什么":你在本地运行pnpm changeset生成一个 Markdown 文件并随 PR 提交,后续的 CI 流水线会自动消费这些文件来更新版本号、生成 CHANGELOG,最终通过 trusted publishing 发布到 npm。读完本文,你能掌握该仓库 changeset 文件的格式与写法、.changeset/config.json 中每个配置项的作用,以及从 PR 合并到 npm 发布的完整自动化链路和安全校验设计。
一、.changeset/目录的定位与基本工作流
整个流程的入口说明在 .changeset/README.md,它确立了三条核心规则:
- 该目录下的每个 changeset 文件,描述的都是已发布包的用户可见变更(user-facing changes);
- 新增 changeset 的唯一方式是运行交互式 CLI:
pnpm changeset- 生成的 Markdown 文件必须与对应的包改动一起提交;发布工作流会消费这些文件,在发布到 npm 之前更新包版本和 changelog。
对应的 npm script 定义在根目录 package.json 中:
{ "scripts": { "changeset": "changeset", "changeset:version": "changeset version" } }也就是说pnpm changeset实际调用的是@changesets/cli(根目录 devDependencies 中锁在^2.31.1)。运行后 CLI 会交互地要求你:勾选本次受影响的包、为每个包选择 semver 升级级别(major / minor / patch)、写一段用于 CHANGELOG 的摘要,然后在.changeset/目录下生成一个随机词组命名的 Markdown 文件(例如import-aliases-no-preconditions.md)。
完整的发布过程(monorepo 结构、发布流程六步、时序图)在仓库的 包发布指南 中有系统描述,该指南明确指出:对已发布包的修改应该在同一个 PR 中包含 changeset;如果某次包改动只影响内部工具、不需要发版,则应运行pnpm changeset add --empty来标记"无需版本变更"。
二、changeset 文件的真实格式:以仓库现有两个实例为例
仓库当前恰好保留了两个未发布的 changeset 文件,是理解格式的绝佳样本。
2.1 patch 级别示例
.changeset/import-aliases-no-preconditions.md:
--- '@node-core/ui-components': patch --- Resolve the `#ui/*` import alias to the compiled output by default, so consumers of the published package no longer need to opt into a bundler-specific `rolldown` resolution condition. The uncompiled sources stay reachable through the new `source` condition, which this repository's own tooling opts into.2.2 minor 级别示例
.changeset/shiki-more-more.md:
--- '@node-core/rehype-shiki': minor --- Add support for all Shiki languages when the `minimal` preset is not selected.两个文件共同展示了 changeset 的标准结构:
- YAML frontmatter:以
---包裹,键是包名(如@node-core/ui-components),值是升级级别(major/minor/patch)。一个文件可以同时声明多个包; - 正文:一条面向最终消费者的 CHANGELOG 条目。注意两份示例的写法都是"描述对使用者的影响"(如"消费方不再需要 opt into bundler-specific 的 resolution condition"),而不是罗列内部实现细节——这正是 changeset 与 commit message 的区别;
- 文件名:随机词组(如
shiki-more-more),由 CLI 生成,无实际语义,避免多人协作时命名冲突。
这两个包都位于 packages/ 工作区目录下(workspace 成员在 pnpm-workspace.yaml 中声明为packages/*、apps/*、platforms/*),它们的 CHANGELOG.md 会在版本发布时由@changesets/changelog-github生成器自动写入这些条目。
三、.changeset/config.json配置项逐项解析
仓库的 Changesets 配置见 .changeset/config.json,全部内容如下:
{ "$schema": "https://unpkg.com/@changesets/config@3.1.4/schema.json", "changelog": [ "@changesets/changelog-github", { "repo": "nodejs/nodejs.org" } ], "commit": false, "fixed": [], "linked": [], "access": "public", "baseBranch": "main", "updateInternalDependencies": "patch", "ignore": [] }各字段在本仓库语境下的含义:
| 字段 | 本仓库取值 | 作用 |
|---|---|---|
changelog | @changesets/changelog-github+repo: nodejs/nodejs.org | 使用 GitHub 风格的 changelog 生成器,生成的版本条目会关联到本仓库的 PR,方便从 changelog 追溯改动来源 |
commit | false | 版本发布时不自动git commit 版本变更,交由 CI 工作流以独立 PR 的方式管理(见下文chore: version packagesPR) |
fixed | [] | 没有强制锁步升版本的包集合;packages/下的各包独立演进版本 |
linked | [] | 没有需要联动升版本的包组 |
access | public | 发布到 npm 时使用 public access,与根目录releasescript 中pnpm publish -r --access=public的显式参数保持一致 |
baseBranch | main | 以main分支作为"哪些 changeset 尚未发布"的基线来比较 |
updateInternalDependencies | patch | 当一个已发布包依赖了另一个本仓库内的包且后者升版时,会自动以 patch 级别更新内部依赖版本 |
ignore | [] | 没有需要排除在 Changesets 管理之外的包 |
其中commit: false是一个值得注意的选择:它把"版本变更落盘"的动作从本地工具转移到了 CI,配合后文的 merge queue 与版本 PR 机制,使得版本号永远只由流水线统一产生,杜绝了人工改版本。
四、发布流水线:changeset 如何变成 npm 上的新版本
发布完全由 Publish Packages 工作流 自动化,main分支上的每次 push 都会触发它。其关键步骤可以从源码逐层确认:
4.1 只信任 merge queue 产出的已签名提交
工作流先执行 "Verify commit authenticity" 步骤(publish-packages.yml):通过 GitHub API 拉取本次 commit,校验两点——
commit.verification.verified必须为true(GPG 签名已验证);- committer 邮箱必须是
noreply@github.com(即提交确实经由 GitHub merge queue 合入,而非直接向 main 推送)。
任一检查失败立即exit 1。这保证了只有经过合并队列的受控改动才能触发包发布。
4.2 创建版本 PR 或直接发布
核心步骤调用 Changesets 官方 Action(v1.9.0,publish-packages.yml):
- name: Create release pull request or publish packages id: changesets uses: changesets/action@a45c4d594aa4e2c509dc14a9f2b3b67ba3780d0d # v1.9.0 with: commit: 'chore: version packages' title: 'chore: version packages' version: node --run changeset:version publish: node --run release commitMode: github-api参数含义:
version: node --run changeset:version—— 当 main 上存在未发布的 changeset 时,Action 会运行changeset version,把所有 changeset 文件消费掉:更新各package.json版本号、写入 CHANGELOG.md,并开/更新一个标题为chore: version packages的 PR;publish: node --run release—— 当版本 PR 合入后再触发时,Action 运行releasescript(即 package.json 中的pnpm --filter './packages/*' --if-present run build && pnpm publish -r --access=public),构建并发布所有新升版的包;commitMode: github-api—— 版本变更通过 GitHub API 提交,保证提交者身份可被上面的签名校验识别;id-token: write权限(workflow 顶层声明)支撑 npm trusted publishing(OIDC 换取发布凭据),全程不需要在仓库中存放 npm token。
这与 包发布指南 描述的六步完全对应:合并队列 → changeset 汇总为版本 PR → 版本 PR 更新版本号与 changelog → 走常规评审与合并队列 → 构建并发布新升版包 → 发布结果通知到#nodejs-web-infra-alerts。工作流末尾的 "Notify" 步骤(publish-packages.yml)用jq把publishedPackages输出格式化成带 npm 版本链接的 Slack 消息,并附上触发者与 commit 链接。
指南中给出的整体流程图:
指南同时强调:包版本不应被手动编辑或手动发布;发布失败时,排障后直接从 GitHub Actions 重试即可。
五、PR 侧的强制门禁:Pull Request Policy 工作流
changeset 不是"建议提交",而是被 CI 强制的。Pull Request Policy 工作流 的触发条件(pull-request-policy.yml):
on: pull_request: branches: [main] paths: - packages/** types: [opened, edited, synchronize, reopened, ready_for_review]即只有改动触及packages/**的 PR 才会运行 Changesets 检查,其检查逻辑只有一行(pull-request-policy.yml):
pnpm changeset status --since origin/mainchangeset status会检测"自基线以来是否有对已发布包的改动但缺少对应 changeset",若有则退出码非零、CI 红灯,PR 因此被拒收。同时该 job 通过if条件排除了 Changesets Action 自动开出的changeset-release/main分支 PR,避免版本 PR 自我卡死。另一个 job 则校验 PR 标题符合 conventional commits 格式(<type>[scope][!]: <description>且不以句号结尾)。
这也解释了 包发布指南 中"--empty"用法的存在:如果你的packages/改动只是内部工具性修改、确实不想触发发版,运行pnpm changeset add --empty生成一个空 changeset 作为"豁免凭证",让changeset status能够通过。
六、实用要点汇总
结合仓库现状(pnpm 11.18.0 workspace + turbo + Changesets 2.x,Node >= 24.11.0),对贡献者的操作清单:
- 修改
packages/*下任何会影响发布产物的代码后,在仓库根目录运行pnpm changeset; - 如实勾选受影响的包、选择正确的 semver 级别、撰写面向消费者的变更描述,并把生成的
.changeset/*.md文件与代码改动一起提交; - 纯内部工具性改动用
pnpm changeset add --empty豁免; pnpm changeset:version只用于本地验证升版步骤的产出,日常流程中该命令由 CI 的changeset version步骤执行并管理产物;- 永远不要手动改
package.json里的版本号或手动执行发布——版本号、changelog、npm 发布全部由chore: version packagesPR 与 Publish Packages 工作流统一产生。
从源码结构看,这套机制把"变更声明"(changeset 文件)、"变更门禁"(changeset status)与"版本执行"(changeset version+release)分别放在开发者、PR CI、main 分支 CI 三个环节,任何一环都无法被单人绕过,是 monorepo 多包独立版本管理的一个完整可参照实现。
【免费下载链接】nodejs.orgThe Node.js® Website项目地址: https://gitcode.com/GitHub_Trending/no/nodejs.org
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考