Nx Release 推荐流程:从 dry-run 到 CI 发布
Nx Release 推荐流程:从 dry-run 到 CI 发布
Nx 的 nx release 是一套面向 monorepo 的发版工具。它不是单纯的 npm publish 包装器,而是把一次 release 拆成三个阶段:
- Versioning:决定新版本号,更新项目版本和内部依赖引用。
- Changelog:根据 commit 或 version plan 生成 changelog。
- Publishing:把包、镜像或 crate 发布到对应 registry。
官方文档反复强调一个原则:先 --dry-run,因为发布很难撤销。
1. 最基础的 release 流程
第一次 release 时,因为没有历史 tag、changelog 或已发布包作为参照,需要加 --first-release:
nx release --first-release --dry-run nx release --first-release
之后的 release 不再需要 --first-release:
nx release --dry-run nx release
默认情况下,nx release 会提示选择版本 bump:
majorminorpatchpremajorpreminorprepatchprerelease- 自定义精确版本号
然后依次执行 versioning、changelog、publishing。
2. 推荐的生产流程:本地做版本,CI 做发布
更推荐的实际流程不是在本地直接 publish,而是:
- 本地预览 release 结果。
- 本地生成版本、changelog、release commit 和 tag。
- 跳过 publish。
- push commit 和 tag。
- CI 监听 tag 后执行
nx release publish。
本地执行:
nx release --skip-publish --dry-run nx release --skip-publish git push git push --tags
CI 中执行:
nx release publish
这种方式的好处是:
- 发布权限收敛在 CI,不需要每个开发者本机都有 npm token 或 Docker registry token。
- release commit 和 tag 可审计。
- 发布失败后可以单独重跑 publish 阶段。
- Nx 会检查包是否已经发布,降低重复发布冲突。
3. 基础配置:明确哪些项目参与 release
如果 workspace 里只有要发布的库,可以不配置 projects。如果是混合仓库,比如同时有 app、e2e、demo、工具项目,就应该明确声明哪些项目参与 release:
{ "release": { "projects": ["packages/*"] } }
projects 支持项目名、tag、目录、glob,也支持用 ! 排除:
{ "release": { "projects": ["packages/*", "!packages/internal-*"] } }
4. 三种版本策略
4.1 手动选择版本 bump
这是默认模式。执行 nx release 后人工选择 major、minor、patch 或自定义版本。
适合:
- 小团队。
- release 频率不高。
- 有专人判断版本语义。
缺点是人工判断成本高,自动化程度低。
4.2 Conventional Commits 自动决定版本
配置:
{ "release": { "version": { "conventionalCommits": true } } }
默认规则:
feat→ minorfix→ patch- breaking change → major
适合:
- 团队严格执行 Conventional Commits。
- 希望 release 流程高度自动化。
- 可以接受 commit message 成为版本语义来源。
风险是:只要 commit 规范执行不稳,就容易误判版本。
4.3 Version Plans:更适合真实团队协作
Nx 支持类似 Changesets 的文件式版本计划。
配置:
{ "release": { "versionPlans": true } }
开发时创建 version plan:
nx release plan
生成的文件位于:
.nx/version-plans/
示例:
--- my-app: minor my-lib: patch release-group-a: major --- 这里写这次变更的说明,后续会进入 changelog。
release 时仍然执行:
nx release
Nx 会读取 .nx/version-plans/ 作为版本 bump 的事实来源。应用后,对应 plan 文件会被删除,并被纳入 release commit。
CI 中还可以检查 PR 是否缺少 version plan:
nx release plan:check
我更推荐中大型团队使用 Version Plans,因为它把“这次 PR 对版本有什么影响”显式放进代码评审,而不是隐含在 commit message 里。
5. Fixed releases 与 Independent releases
Fixed releases:默认模式
默认情况下,一个 release group 内的项目共享同一个版本号。
特点:
- 一个项目需要 bump,组内所有项目一起 bump。
- 内部依赖自动更新。
- tag 默认类似
v1.2.3。
适合强绑定的一组包,比如组件库和配套工具一起发版。
Independent releases:项目独立发版
配置:
{ "release": { "projectsRelationship": "independent" } }
特点:
- 每个项目独立决定版本号。
- 每个项目生成自己的 tag。
- 默认 tag 类似
{projectName}@{version}。
例如:
[email protected] [email protected]
如果自定义 tag pattern,必须包含 {projectName},否则多个项目会产生 tag 冲突:
{ "release": { "releaseTag": { "pattern": "release/{projectName}/{version}" } } }
适合多个包生命周期不同、消费者不同、发布节奏不同的 monorepo。
6. Release Groups:复杂 monorepo 的关键能力
如果一个仓库里同时有 npm packages、Docker apps、Rust crates 或内部工具,推荐用 release.groups 分组。
示例:
{ "release": { "groups": { "packages": { "projects": ["packages/*"], "projectsRelationship": "fixed" }, "apps": { "projects": ["apps/*"], "projectsRelationship": "independent" } } } }
这样不同组可以有不同的版本策略、changelog 策略和发布方式,避免所有项目被一套规则绑死。
7. 发布前构建
如果包是从 dist 目录发布,而不是直接发布源目录,通常需要在 versioning 前先 build:
{ "release": { "version": { "preVersionCommand": "npx nx run-many -t build" } } }
release group 级别也可以配置:
{ "release": { "groups": { "my-group": { "projects": ["my-lib-one", "my-lib-two"], "version": { "groupPreVersionCommand": "npx nx run-many -t build -p my-lib-one,my-lib-two" } } } } }
Docker 场景中可以配置:
{ "release": { "docker": { "preVersionCommand": "npx nx run-many -t docker:build" } } }
8. 内部依赖更新策略
独立发版时,要特别注意项目之间的依赖关系。例如:
project-a -> project-b
如果 project-b 从 2.0.0 升到 2.1.0,那么 project-a 中对 project-b 的依赖引用也应该更新。
Nx 用 version.updateDependents 控制这个行为:
{ "release": { "version": { "updateDependents": "always" } } }
可选值:
always:依赖被 bump 时,总是更新 dependent。Nx 22 起这是默认方向。auto:旧默认值,只更新同 release group 且未被 filter 排除的 dependent。never:不自动更新 dependent。
推荐使用 always,尤其是 independent releases 场景,否则很容易发出引用旧内部依赖的包。
9. Changelog 与 GitHub/GitLab Release
Nx 可以生成 workspace 级别和 project 级别 changelog,也可以自动创建 GitHub 或 GitLab Release。
启用 GitHub Release:
{ "release": { "changelog": { "workspaceChangelog": { "createRelease": "github" } } } }
如果只想用 GitHub Release,不维护本地 CHANGELOG.md:
{ "release": { "changelog": { "workspaceChangelog": { "file": false, "createRelease": "github" } } } }
GitHub 认证优先使用环境变量:
GITHUB_TOKENGH_TOKEN
GitLab 类似:
{ "release": { "changelog": { "workspaceChangelog": { "createRelease": "gitlab" } } } }
认证变量:
GITLAB_TOKENGL_TOKEN- GitLab CI 中的
CI_JOB_TOKEN
10. GitHub Actions 发布示例
推荐让 CI 监听 tag,然后只执行 publish:
name: Publish on: push: tags: - v*.*.* jobs: publish: runs-on: ubuntu-latest permissions: contents: read id-token: write steps: - uses: actions/checkout@v4 with: fetch-depth: 0 filter: tree:0 - uses: actions/setup-node@v4 with: node-version: 20 registry-url: https://registry.npmjs.org/ - run: npm install - run: npx nx report - run: npx nx release publish env: NODE_AUTH_TOKEN: ${{ secrets.NPM_ACCESS_TOKEN }} NPM_CONFIG_PROVENANCE: true
这里有两个关键点:
fetch-depth: 0很重要,Nx 需要完整 git history 和 tags。- npm 发布建议启用 provenance:
NPM_CONFIG_PROVENANCE: true。
11. 面向前端 monorepo 的推荐模板
如果是一个前端 packages monorepo,我会从这个配置开始:
{ "release": { "projects": ["packages/*"], "versionPlans": true, "version": { "updateDependents": "always", "preVersionCommand": "npx nx run-many -t build" }, "changelog": { "workspaceChangelog": true, "projectChangelogs": true } } }
对应流程:
# PR 中声明版本影响 nx release plan # CI 检查是否缺少 version plan nx release plan:check # 发版前预览 nx release --skip-publish --dry-run # 生成 release commit 和 tag nx release --skip-publish # 推送 git push git push --tags # CI 发布 nx release publish
我的判断
Nx Release 的最佳实践不是“一个命令从本地直接发完”,而是把 release 拆成两个责任边界:
- 本地或 release 分支负责确定版本、生成 changelog、创建 tag。
- CI 负责使用受控 token 发布产物。
版本策略上,如果团队很小,手动选择 bump 就够;如果团队 commit 规范非常稳定,可以用 Conventional Commits;但对真实的前端团队,我更推荐 Version Plans。它能让版本影响在 PR 阶段显性化,review 更直接,也不容易被不规范 commit message 带偏。
最终推荐顺序是:
- 任何 release 都先
--dry-run。 - 真实发布用
--skip-publish,把 publish 放 CI。 - 中大型 monorepo 使用 Version Plans。
- 多产物仓库使用 Release Groups。
- independent releases 场景下保持
updateDependents: "always"。
这套流程的目标不是炫技,而是减少误发、减少权限暴露、让版本变化可审计。对于前端基础库、组件库和工具链 monorepo,这比“谁有 token 谁本地发”要稳得多。