Changesets 自动化发布实战指南:从 CI 强制校验到 version/publish 全流程自动化

发布时间:2026/9/23 5:04:08
Changesets 自动化发布实战指南:从 CI 强制校验到 version/publish 全流程自动化 Changesets 自动化发布实战指南从 CI 强制校验到 version/publish 全流程自动化【免费下载链接】changesets A tool to manage versioning and changelogs with a focus on monorepos项目地址: https://gitcode.com/gh_mirrors/ch/changesetsChangesets 是一款面向 monorepo 的版本管理与 changelog 生成工具它允许贡献者以“changeset 文件”的形式声明变更应如何发布再由工具统一更新包版本、生成 changelog 并完成发布。本文围绕当前仓库中的 docs/automating-changesets.md 指南展开系统讲解如何将这套原本手动的工作流自动化包括如何确保每个 Pull Request 都带有 changeset非阻塞提示与阻塞校验两种方案以及如何自动运行 version 与 publish 命令基于 GitHub Actions 的多种发布模式与纯手动流程兜底。读完本文你将掌握changeset status --since、changeset --empty等命令的正确用法并能够为你的仓库配置出一套完整、安全、可落地的自动化发布流水线。一、自动化的两个核心决策虽然 changeset 的设计初衷是配合完全手动的工作流使用但它也提供了帮助自动化的工具。整个自动化体系可以拆解为两个独立的决策问题如何确保 Pull Request 带有 changeset——解决贡献者忘记写 changeset这一最常见的人为疏漏如何运行 version 和 publish 命令——解决版本更新与发布这一重复且容易出错的手动环节。两个问题彼此独立可以单独实施即使不强制校验 changeset也可以让 GitHub Action 自动创建版本 PR反之亦然。二、确保 Pull Request 带有 changesetChangesets 以文件形式提交到仓库理论上细心的 reviewer 总能发现 changeset 缺失并要求补上。但作为人类肉眼检查某个文件不存在是很容易遗漏的。因此官方建议引入某种机制来自动检测 PR 上 changeset 的存在与否把这件事从人肉检查变成机器检查同时直接向 PR 作者高亮提示。实现方式分两种非阻塞与阻塞。2.1 非阻塞缺失 changeset 不阻止合并非阻塞方案下PR 即使没有 changeset 也可以合并缺失 changeset 不会让 CI 变红。官方推荐使用Changesets GitHub Bot在仓库中安装该 GitHub App 即可它会在 PR 上自动评论 changeset 是否存在但不会阻止合并。一个额外便利是Bot 还会给维护者提供直接补充 changeset的链接方便维护者在合并 PR 时自行补上而不必等待贡献者返回修改。如果你更希望用 GitHub Action 自己写一套自定义的非阻塞检查可以参照仓库中 site/guide/_snippets/automating-non-blocking.yaml 提供的模板它使用pull_request_target事件用于支持 fork 仓库的 PR第一步仅读取代码并生成 PR 状态文本第二步由pr-comment动作把评论写到 PR 上。安全警告不要运行不可信代码pull_request_target事件默认开启写权限如果执行了来自 fork 的不可信代码且权限未收窄将带来安全风险。上述模板中的做法是只checkout 与 readfork 的代码绝不executefork 中的任何代码。若你更倾向锁死权限、不处理 fork PR可以改用pull_request事件并加入如下 if 判断避免 Action 在 fork PR 中失败jobs: pr-status: if: github.event.pull_request.head.repo.full_name github.repository !startsWith(github.head_ref, changeset-release/) # ...2.2 阻塞让 CI 在缺失 changeset 时失败如果希望流程绝对一致——每个 PR 都必须带 changeset否则 CI 失败——就在 CI 中加入一个步骤运行# 以 pnpm 为例npm/yarn 对应下方代码组 changeset status --sincemain各包管理器下的等价写法pnpm changeset status --since main # pnpm npx changesets/cli status --since main # npm yarn changeset status --since main # yarn该命令的语义是如果自main分支以来有包被改动、但没有新增 changeset则退出码为 1CI 失败如果没有包被改动则不会失败。这一点可以直接从源码得到印证。在 packages/cli/src/commands/status/index.ts 中status命令先通过getPackages、readConfig、readPreState、readChangesets收集仓库信息再用assembleReleasePlan组装发布计划并用getVersionableChangedPackages计算自since引用以来发生变更的可发布包当changedPackages.length 0 releasePlan.changesets.length 0时它会打印错误提示并throw new ExitError(1)——这正是 CI 失败退出码 1 的来源。错误提示中会直接建议开发者运行changeset add或changeset add --empty。对应的测试用例也验证了这些行为见 packages/cli/src/commands/status/tests/status.test.ts有变更包但无 changeset 时抛出错误第 175-204 行无变更包时不触发退出第 206-235 行有变更包且同时存在 changeset 时不触发退出第 237-276 行仅改动被ignore或privatePackages.version false排除的包时不触发退出第 493-586 行支持changedFilePatterns配置如[src/**]只有改动命中了这些模式且缺 changeset 才失败第 351-491 行。2.3 例外情况只改测试、构建工具等无需发版的内容有时你确实想要合并一个不需要发版的变更例如只改测试或构建工具。此时可以运行changeset --empty这会添加一个特殊的空 changeset——它不产生任何版本更新却能让阻塞式校验有 changeset这一关通过。需要特别指出的是官方文档明确不推荐普遍采用阻塞方案因为并非每个变更都需要发版阻塞只是偏好绝对一致流程时的选项。三、如何运行 version 与 publish 命令3.1 推荐方案Changesets GitHub Action官方提供Changesets GitHub Action来承担版本与发布环节其能力包括创建一个versionPR并在后续提交时持续更新它该 PR 始终包含最新一次changeset version的运行结果当变更合并到基础分支后可选地执行真正的发布动作。在 site/guide/automating.md 中给出了官方推荐的整体流程判断逻辑可以用下面的流程图概括Has changesets? ──YES──▶ Version packages and create/update PR │ NO ▼ Are there any publishable packages? ──YES──▶ Publish packages │ NO ▼ Do nothing即推送到基础分支后先判断是否有 changeset有则更新版本 PR没有则继续判断是否存在可发布包存在才发布。3.2 前提条件允许 GitHub Actions 创建并批准 PR由于 Action 需要为版本更新创建 PR请务必在仓库设置的Actions General中开启Allow GitHub Actions to create and approve pull requests。若未开启可能遇到如下报错remote: Permission to xxx.git denied to github-actions[bot]GitHub Actions is not permitted to create or approve pull requests3.3 发布模式一Trusted Publishing推荐npm 官方推荐使用Trusted Publishing或 Staged Publishing从 CI 安全地发布包。当前阶段Staged Publishing 与 Changesets 不兼容所以应选择 Trusted Publishing。与 npm 官方工作流建议相反务必让id-token: write只出现在真正需要发布的那个 job上因此建议把构建、测试、发布拆分成独立 job。仓库中的 site/guide/_snippets/automating-trusted-publishing.yaml 提供了一个完整示例其 job 结构为select-mode读取仓库、安装依赖调用changesets/action/select-modev2判断本次推送是进入version模式还是publish模式versionmode version时需要contents: write与pull-requests: write权限调用changesets/action/versionv2生成并更新版本 PRpackmode publish时构建并用changesets/action/packv2打包产物publish仅此 job声明id-token: writeTrusted Publishing 需要调用changesets/action/publishv2发布。此外还可以考虑为publishjob 配置一个带必需审阅者的 GitHub environment让发布 job 在继续前必须经过维护者审批——这是确保只有受信任维护者才能发布的一种方式。3.4 发布模式二Token-based Publishingnpm token官方已不再推荐token 方式尤其不推荐 Granular Access Tokens原因是其限制较多token 最长 90 天过期、需要周期性人工轮换2FA-bypass token 也正在被弃用启用 2FA 后将无法直接用于发布。但如果你的目标是不支持 Trusted Publishing 的其他 npm 兼容 registry仍可选择 token 方式。你需要一个勾选了Bypass two-factor authentication的 npm token避免 CI 中被 npm 要求二次验证并以NPM_TOKEN为名加入仓库 Secrets。之后参照 site/guide/_snippets/automating-token-based-publishing.yaml 配置在publishjob 中通过actions/setup-node的registry-url: https://registry.npmjs.org/配置认证并在调用changesets/action/publishv2时通过env.NODE_AUTH_TOKEN传入${{ secrets.NPM_TOKEN }}。对于贡献者可信的私有仓库仓库还提供了一个简化版工作流 site/guide/_snippets/automating-token-based-publishing-simplified.yaml单个 job 直接调用changesets/actionv2通过publish-script: npx changesets/cli publish指定发布命令同样以NODE_AUTH_TOKEN传 token。若要在 GitHub Package Registry 发布而非 npm把registry-url改为https://npm.pkg.github.com/并将NODE_AUTH_TOKEN传${{ secrets.GITHUB_TOKEN }}。3.5 发布模式三只创建 Git Tags由其他工作流发布如果发布动作由独立工作流承担例如基于 git tag 创建事件触发可以让 Changesets 只负责在发布时创建 git tag。这要求把包的package.json设为private: true并在配置中开启privatePackages参见 site/guide/config.md 与 site/guide/beyond-npm.md 的相关说明。工作流模板见 site/guide/_snippets/automating-publish-git-tags-only.yaml结构与 Token 方式相似但不设置registry-url与NODE_AUTH_TOKEN。3.6 发布模式四只做版本Version Only如果完全不打算发布包或只用 Changesets 管理 changelog可以配置只执行版本的流水线。模板见 site/guide/_snippets/automating-version-only.yaml推送到main后安装依赖、构建再调用changesets/actionv2的 version 部分该 job 需要contents: write与pull-requests: write权限。此模式下同样需要把NODE_AUTH_TOKEN传给 Action官方模板如此即使不发布也保持一致配置以避免失败。3.7 兜底方案手动执行 version 与 publish如果你不想引入 GitHub Action官方文档 docs/automating-changesets.md 给出了手动运行version与publish的推荐流程由一名发布协调人RCRelease Coordinator执行RC 通知暂停向基础分支合并RC 拉取基础分支运行changeset version将版本变更提交为一个新 PR将版本 PR 合并回基础分支RC 再次拉取基础分支运行changeset publishRC 运行git push --follow-tags推送发布 tagRC 解除基础分支的合并限制。这套流程步骤较多且比较繁琐需要从基础分支拉取两次官方也承认这一点建议你根据自身情况灵活调整。这也正说明用 GitHub Action 接管这两个环节能显著降低人为出错率。四、从源码理解 version 与 publish 的底层行为changeset version命令的核心实现在 packages/cli/src/commands/version/index.ts阅读它可以更准确地理解自动化工作流中 Action 做了什么先读取配置与 changeset通过assembleReleasePlan组装发布计划会综合 pre 模式状态、ignore、snapshot等配置若处于 pre预发布模式但请求了 snapshot 发布或没有未发布的 changeset且不在pre exit状态命令会报错退出——这正是 CI 里无 changeset 就不应触发 version的底层保证随后applyReleasePlan会实际改写 package.json 版本号与 CHANGELOG.md若配置了commit它会把改动文件逐个git add并提交否则只改文件、留待人工提交日志提示 All files have been updated. Review them and commit at your leisure。配置了commit时工作流中还可以通过 Changesets 的commit配置项含skipCI选项自定义提交信息与消息格式。相应地changeset publish会基于 version 产生的版本信息更新包并推送 git tag——自动化场景下Action 正是分别调用这两个命令完成版本 PR与实际发布的。五、自动化流水线的额外注意事项5.1 理解 npm 认证原理当使用actions/setup-node并设置registry-url时它内部会生成一个类似下面的.npmrc//registry.npmjs.org/:_authToken${NODE_AUTH_TOKEN}这种写法只在NODE_AUTH_TOKEN环境变量存在时才向 npm registry 认证比把 token 直接写死在.npmrc中更安全。进阶场景下也可以手写~/.npmrc注意此时要移除actions/setup-node的registry-url以避免冲突。例如需要把不同 scope 发布到不同 registry 时- run: | cat EOF ~/.npmrc # 无 scope 的包发布到默认 npm registry //registry.npmjs.org/:_authToken${NODE_AUTH_TOKEN} # foo/* 发布到自定义 registry foo:registryhttps://my-registry.com/ //my-registry.com/:_authToken${NODE_FOO_AUTH_TOKEN} # bar/* 发布到 GitHub Package Registry bar:registryhttps://npm.pkg.github.com/ //npm.pkg.github.com/:_authToken${GITHUB_TOKEN} EOF5.2 让工作流在版本 PR 上自动运行GitHub Actions 创建的 PR 默认不会触发针对 PR 的工作流。要让版本 PR 上的 CI 自动运行需要把个人 token 或 GitHub App token 传给 Changesets GitHub Action。以 GitHub App 为例需要配置APP_CLIENT_ID变量与APP_PRIVATE_KEY密钥jobs: version: runs-on: ubuntu-latest permissions: contents: read # 用于 actions/checkout steps: # ... - name: Create GitHub App Token uses: actions/create-github-app-tokenv3 id: app-token with: client-id: ${{ vars.APP_CLIENT_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} permission-contents: write # 提交版本变更 (changesets/action/version) permission-pull-requests: write # 创建 PR (changesets/action/version) - name: Version packages uses: changesets/action/versionv2 with: github-token: ${{ steps.app-token.outputs.token }}同时注意版本提交与合并提交中不要包含[skip ci]或其任何变体否则会跳过工作流运行导致测试或发布步骤不执行。这在使用commit-message或 Changesetscommit配置的skipCI选项时需要格外小心。5.3 包管理器各自的认证配置差异pnpm出于安全原因项目目录下的.npmrc不支持环境变量应改在用户主目录配置其他包管理器同样建议如此避免与项目内既有配置混杂。yarnyarn 不支持.npmrc需改用~/.yarnrc.yml- run: | cat EOF ~/.yarnrc.yml npmAuthToken: ${NODE_AUTH_TOKEN} EOF多 registry 场景可扩展为npmScopes配置例如将fooscope 指向https://my-registry.com/、barscope 指向 GitHub Package Registry并分别使用不同的认证 token。六、自动化方案选型速查需求场景推荐做法关键命令 / 动作提示 PR 作者补 changeset不阻塞Changesets GitHub Bot或自定义非阻塞 Action见 site/guide/_snippets/automating-non-blocking.yamlBot 自动评论强制每个 PR 都必须有 changesetCI 中运行 status 校验changeset status --sincemain退出码 1 即失败无需发版的变更绕过校验添加空 changesetchangeset --empty自动版本 发布npm 推荐Trusted Publishing 工作流见 site/guide/_snippets/automating-trusted-publishing.yamlchangesets/action/versionv2changesets/action/publishv2id-token: write仅限 publish job自动版本 发布其他 registryToken 工作流见 site/guide/_snippets/automating-token-based-publishing.yamlNPM_TOKENSecret NODE_AUTH_TOKEN只创建 git tag发布交给其他工作流Tags-only 工作流见 site/guide/_snippets/automating-publish-git-tags-only.yaml包设为 private 并开启privatePackages只管理版本/changelog不发布Version-only 工作流见 site/guide/_snippets/automating-version-only.yamlchangesets/actionv2的 version 环节不使用 Action手动 RC 流程版本 PR → 合并 → publish → push tagschangeset version/changeset publish/git push --follow-tags七、延伸阅读changeset 的创建、格式与语义见 docs/adding-a-changeset.mdstatus命令的更多选项--verbose、--output、--since与配置项见 docs/command-line-options.md 与 docs/config-file-options.md发布在 monorepo 中可能遇到的问题与解法见 docs/problems-publishing-in-monorepos.md本文引用的工作流模板全部位于 site/guide/_snippets/可直接复制到仓库.github/workflows/下按需修改使用。【免费下载链接】changesets A tool to manage versioning and changelogs with a focus on monorepos项目地址: https://gitcode.com/gh_mirrors/ch/changesets创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考