NxMonorepoReleaseFrontend Engineering

Nx Release 推荐流程:从 dry-run 到 CI 发布

Zero··原文链接
收录于 2026/6/9 16:25:48

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:

  • major
  • minor
  • patch
  • premajor
  • preminor
  • prepatch
  • prerelease
  • 自定义精确版本号

然后依次执行 versioning、changelog、publishing。


2. 推荐的生产流程:本地做版本,CI 做发布

更推荐的实际流程不是在本地直接 publish,而是:

  1. 本地预览 release 结果。
  2. 本地生成版本、changelog、release commit 和 tag。
  3. 跳过 publish。
  4. push commit 和 tag。
  5. 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 → minor
  • fix → 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-b2.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_TOKEN
  • GH_TOKEN

GitLab 类似:

{
  "release": {
    "changelog": {
      "workspaceChangelog": {
        "createRelease": "gitlab"
      }
    }
  }
}

认证变量:

  • GITLAB_TOKEN
  • GL_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 带偏。

最终推荐顺序是:

  1. 任何 release 都先 --dry-run
  2. 真实发布用 --skip-publish,把 publish 放 CI。
  3. 中大型 monorepo 使用 Version Plans。
  4. 多产物仓库使用 Release Groups。
  5. independent releases 场景下保持 updateDependents: "always"

这套流程的目标不是炫技,而是减少误发、减少权限暴露、让版本变化可审计。对于前端基础库、组件库和工具链 monorepo,这比“谁有 token 谁本地发”要稳得多。