Lerna version 命令完全指南:多包仓库的版本号管理与发布流程
2026/9/19 6:58:00 网站建设 项目流程

Lerna version 命令完全指南:多包仓库的版本号管理与发布流程

【免费下载链接】lernaLerna is a fast, modern build system for managing and publishing multiple JavaScript/TypeScript packages from the same repository.项目地址: https://gitcode.com/gh_mirrors/le/lerna

lerna version是 Lerna 中负责"为自上次发布以来发生变更的包提升版本号"的核心命令,它串联起变更检测、版本选择、manifest 修改、changelog 生成、git 提交打标签与推送的完整发布链路。本文以本仓库中 libs/commands/version/README.md 为主体,结合 命令入口实现 与 核心执行逻辑 的源码细节,系统讲解该命令的用法、全部配置项、prerelease 策略与生命周期脚本执行顺序,帮助你掌握一套可落地的多包版本管理方案。

一、命令概览:一次运行完成五件事

lerna version的运行流程可以概括为以下五个步骤(也是该命令的官方定义):

  1. 识别变更包:找出自上一次 tagged release 以来发生更新的包;
  2. 提示选择新版本:交互式询问每个变更包的新版本号;
  3. 修改包元数据:更新package.json中的版本字段,并在根目录和各个包内执行相应的生命周期脚本;
  4. 提交并打标签:将这些变更提交到 git 并打上版本标签;
  5. 推送到远端:将提交与标签推送到 git remote。

在 libs/commands/version/src/index.ts 的execute()方法中,可以清楚看到这四个执行阶段被编排为串行任务队列:updatePackageVersions()(更新各包版本、依赖与 lockfile)→commitAndTagUpdates()(提交并打标签)→gitPushToRemote()(推送)→createRelease()(可选,创建 GitHub/GitLab Release)。

二、基本用法与位置参数bump

命令的三种典型调用方式:

lerna version 1.0.1 # 显式指定版本号 lerna version patch # semver 关键字 lerna version # 从交互式提示中选择

semver bump 位置参数

lerna version [major | minor | patch | premajor | preminor | prepatch | prerelease] # 直接使用下一个语义化版本号,跳过 "Select a new version for..." 提示

当传入该位置参数时,lerna version会跳过版本选择提示,直接按该关键字递增版本号(底层使用semver.inc()进行递增,见 getVersionsForUpdates 实现)。但注意:你仍然需要--yes标志来跳过全部确认提示

从源码角度,位置参数的校验逻辑在 libs/commands/version/src/command.ts 的addBumpPositional()中实现:yargs 会将输入值coerce校验,只有满足semver.valid()或属于七个关键字之一才会通过,否则抛出bump must be an explicit version string _or_ one of: ...错误。随后在index.ts中,合法版本号走repoVersion分支(全局统一版本),合法关键字走increment分支(按major/minor/...递增)。

固定模式与独立模式的区别

版本号的选择策略取决于lerna.json中的version配置:

  • 固定模式(fixed,默认):所有包共享同一个版本号,运行时一次提示、全局统一;源码中通过makeGlobalVersionPredicatesetGlobalVersionFloor/Ceiling保证各包版本被收敛到全局版本(含 breaking change 时强制所有包同步 major,见 setUpdatesForVersions);
  • 独立模式(independent)"version": "independent",每个包分别提示并拥有独立版本号与独立 tag。

三、Prerelease 预发布版本管理

如果你的仓库中存在携带预发布版本号的包(如2.0.0-beta.3),并且执行非预发布 bump(majorminorpatch),Lerna 会将这些"先前已预发布"的包与自上次发布以来变更的包一并发布。

对于使用 conventional commits 的项目,预发布管理依赖以下三个标志:

  • --conventional-prerelease:将当前变更以预发布版本形式发布;
  • --conventional-graduate:将预发布版本的包"毕业"到稳定版本;
  • 只运行lerna version --conventional-commits(不带上述标志)时,仅当版本本身已经是预发布版本时,当前变更才会以预发布形式发布。

四、核心配置项:变更检测与版本选择

--allow-branch <glob>

对允许执行lerna version的 git 分支做 glob 白名单过滤。推荐配置在lerna.json中:

{ "command": { "version": { "allowBranch": "main" } } }

上述配置下,在main之外的任何分支运行lerna version都会直接失败。将版本发布限制在主分支是公认的最佳实践。也支持分支数组:

{ "command": { "version": { "allowBranch": ["main", "feature/*"] } } }

上面的配置允许所有feature/前缀分支执行版本命令。但请务必注意:在 feature 分支上生成 git tag 隐患很大——当这些分支被合并进主分支后,若 tag 因 squash 合并或冲突解决而"脱离"了原始上下文,后续lerna version将难以正确计算"自上次发布以来的 diff"。

持久化配置总可以被命令行覆盖,请谨慎使用:

lerna version --allow-branch hotfix/oops-fix-the-thing

在源码中,该选项通过minimatch对当前分支名做匹配,不匹配时抛出ENOTALLOWED校验错误(见 index.ts initialize 阶段)。

--amend

lerna version --amend # 保留原有提交信息,并跳过 git push

使用该标志后,lerna version将在当前提交上直接修改,而不是新增一个提交。这对 CI 场景非常有用,可以减少项目历史中的提交数量。为防止意外覆盖,该命令会跳过git push(即隐式开启--no-push)。源码中pushToRemote = gitTagVersion && amend !== true && push直接体现了这一约束(index.ts),git 提交时也通过--amend --no-edit保留原提交信息(见 git-commit.ts)。

--build-metadata <buildMetadata>

lerna version --build-metadata 001

构建元数据必须符合 SemVer 规范。提供后它将应用于所有更新的包,无论项目使用独立还是固定版本模式。如果选择交互式提示,可以通过请求自定义版本为特定包修改或移除构建元数据。源码中通过applyBuildMetadata()在固定、独立与推荐版本各分支统一注入(index.ts)。

--ci-behind-behavior <behavior>

lerna version --ci-behind-behavior skip

控制 CI 环境下当前分支落后于其上游 remote 时lerna version的行为:

  • 默认值error:抛出EBEHIND错误并以非零退出码失败;
  • skip:记录EBEHIND警告并以成功状态退出,不执行版本化或发布。

非 CI 的交互式执行中,只要当前分支落后于上游就始终失败。该选项同样可写入lerna.json

{ "command": { "version": { "ciBehindBehavior": "skip" } } }

在 command.ts 中该参数被限定为choices: ["error", "skip"];行为逻辑见 index.ts,底层通过git remote updategit rev-list --left-right --count判断落后提交数(is-behind-upstream.ts)。

--ignore-changes

检测变更包时忽略匹配 glob(s) 的文件。该选项最适合配置在根目录lerna.json,既避免 shell 过早展开 glob,也便于与lerna difflerna changed共享配置:

{ "ignoreChanges": ["**/__fixtures__/**", "**/__tests__/**", "**/*.md"] }

命令行方式:

lerna version --ignore-changes '**/*.md' '**/__tests__/**'

--no-ignore-changes可禁用任何已有的持久化配置。文档还特别提醒:以下两种情况下包一定会被发布,不受本选项约束

  1. 该包最新 release 是 prerelease 版本(如1.0.0-alpha1.0.0–0.3.7等);
  2. 该包的一个或多个本地关联依赖发生了变化。

--include-merged-tags

lerna version --include-merged-tags

检测变更包时,包含来自已合并分支的 tag。

--exact

lerna version --exact

指定更新包中的关联依赖时使用精确版本号(不带标点),而不是 semver 兼容范围(带^)。对应源码中的savePrefix = this.options.exact ? "" : "^"(index.ts),该前缀随后用于updateDependencies()中改写本地依赖声明(index.ts)。

--force-publish

lerna version --force-publish=package-2,package-4 # 强制所有包都进行版本化 lerna version --force-publish

强制发布指定包(逗号分隔)或所有包(*)。这会跳过lerna changed的变更检查,强制更新一个没有git diff变更的包。注意:与--conventional-commits--conventional-graduate组合使用时该选项会被忽略。

--preid

lerna version prerelease # 使用下一个语义化预发布版本,例如 # 1.0.0 => 1.0.1-alpha.0 lerna version prepatch --preid next # 使用带特定预发布标识符的下一个版本,例如 # 1.0.0 => 1.0.1-next.0

该标志指定premajorpreminorprepatchprereleasebump 使用的预发布标识符。默认标识符为alpha(见 command.ts 的defaultDescription: "alpha",以及 index.ts 中resolvePrereleaseIdpreid || existingPreid || "alpha"优先级链)。

--premajor-version-bump

控制当检测到非破坏性变更时,Lerna 如何处理premajor版本(尚未进行过 major 发布的包,如"version": "0.2.4")的 bump。注意:premajor 包中的破坏性变更始终触发minorbump

lerna version --conventional-commits # 或者显式使用默认值 lerna version --conventional-commits --premajor-version-bump default # 所有非破坏性变更按配置的 conventional commits preset 触发 bump # 对于默认 preset,非破坏性 feat 是 minor bump # 0.1.0 --> 0.2.0 lerna version --conventional-commits --premajor-version-bump force-patch # 确保所有非破坏性 premajor bump 都按 patch 处理 # 此时非破坏性 feat 永远是 patch bump # 0.1.0 --> 0.1.1

该参数在 command.ts 中被限定为choices: ["default", "force-patch"],并在recommendVersions()中传给recommendVersion()参与计算(index.ts)。

--json

lerna version --json

以 JSON 格式输出更新的包。示例输出:

[ { "name": "footer", "version": "0.0.4", "private": false, "location": "/home/ammo/git/lerna-getting-started-example/packages/footer", "newVersion": "0.0.5" }, { "name": "header", "version": "0.0.4", "private": false, "location": "/home/ammo/git/lerna-getting-started-example/packages/header", "newVersion": "0.0.5" }, { "name": "remixapp", "version": "0.0.4", "private": true, "location": "/home/ammo/git/lerna-getting-started-example/packages/remixapp", "newVersion": "0.0.5" } ]

五、Conventional Commits 相关选项

--conventional-commits

lerna version --conventional-commits

使用conventional-changelog包来确定版本 bump 并生成CHANGELOG.md文件。若不额外指定--changelog-preset,将使用conventional-changelog的默认配置,即当前的 angular 规范。如果想要按其他规范(如支持在 header 中使用!表示破坏性变更的 Conventional Commits 规范)进行版本递增,需要传入规范名,例如--changelog-preset conventionalcommits。配合--no-changelog可禁用CHANGELOG.md的生成(或更新)。

--changelog-preset

lerna version --conventional-commits --changelog-preset angular-bitbucket

默认 changelog preset 为angular。Preset 可以是内置或可安装的 conventional changelog 配置包的名称,既可以用完整包名,也可以用自动展开的后缀(如angular会展开为conventional-changelog-angular)。该选项也可写在lerna.json

{ "changelogPreset": "angular" }

如果 preset 导出的是 builder 函数(如conventional-changelog-conventionalcommits),还可以同时指定 preset 配置:

{ "changelogPreset": { "name": "conventionalcommits", "issueUrlFormat": "{{host}}/{{owner}}/{{repository}}/issues/{{id}}" } }

若使用默认 preset 之外的任何 preset,还应在 devDependencies 中安装对应的conventional-changelog包。该参数同时参与每个包版本 bump 的计算(传入recommendVersion)。

--changelog-entry-additional-markdown

lerna version --conventional-commits --changelog-entry-additional-markdown "### Some title\n\nSome *paragraph*"

允许为当前 release 的新增 changelog 条目追加额外的 markdown 内容,例如补充文档或链接到参考站点、视频。如果文本是静态的、每次发布不变,可以固化在lerna.json中:

{ "command": { "version": { "changelogEntryAdditionalMarkdown": "### Some title\n\nSome *paragraph*" } } }

--conventional-graduate

lerna version --conventional-commits --conventional-graduate=package-2,package-4 # 强制所有 prerelease 包毕业 lerna version --conventional-commits --conventional-graduate

将指定包(逗号分隔)或所有包(*)"毕业"(graduate)为稳定版本。与--force-publish类似,它不要求当前 HEAD 已发布;区别在于非 prerelease 的包会被忽略。若存在未指定包(指定包列表时)或非 prerelease 包的变更,这些包会按--conventional-commits的正常逻辑版本化。

"毕业"意味着将 prerelease 版本提升为对应的非 prerelease 变体,例如:package-1@1.0.0-alpha.0 => package-1@1.0.0

注意:指定包时,被指定包的依赖方会被发布,但不会被毕业

--force-conventional-graduate

lerna version --conventional-commits --conventional-graduate=package-2,package-4 --force-conventional-graduate # 强制所有 prerelease 包毕业,且非 prerelease 或无变更的包也一并更新 lerna version --conventional-commits --conventional-graduate --force-conventional-graduate

强制毕业所有由--conventional-graduate指定的包,此时非 prerelease 包不会像不加该标志时那样被忽略。在单版本(fixed)模式下,可以用来强制所有指定包统一更新到同一版本,即使它们没有变更或不是 prerelease。它类似于--force-publish,但在启用了--conventional-commits--conventional-graduate时不会被忽略。该标志仅在设置了--conventional-graduate时生效,否则被忽略。

--conventional-prerelease

lerna version --conventional-commits --conventional-prerelease=package-2,package-4 # 强制所有变更包以 prerelease 形式发布 lerna version --conventional-commits --conventional-prerelease

将指定包(逗号分隔)或所有包(*)以 prerelease 版本发布。实现方式是为conventional-commits推荐的版本加pre前缀:例如当前变更包含一个 feature commit 时,推荐 bump 为minor,该标志将产生preminor发布。若存在未指定包(指定包列表时)或已是 prerelease 的包的变更,这些包会按--conventional-commits的正常逻辑版本化。

--conventional-bump-prerelease

lerna version --conventional-commits --conventional-prerelease --conventional-bump-prerelease

即使已发布的包已经是 prerelease,也强制以递增后的 prerelease 版本发布。与--conventional-prerelease一样,通过对conventional-commits推荐版本加pre前缀实现;不同之处在于:不加该标志时,对已是 prerelease 的包只应用 prerelease bump。开启后效果示例:

Changes: - major: 1.0.0-alpha.0 => 2.0.0-alpha.0 - minor: 1.0.0-alpha.0 => 1.1.0-alpha.0 - patch: 1.0.0-alpha.0 => 1.0.1-alpha.0

另外需要留意:--conventional-prerelease--conventional-graduate不能同时使用,源码中对此组合抛出ENOTALLOWED错误(index.ts)。

六、git 交互与提交推送选项

--message <msg>(别名-m

该选项为-m起别名以对齐git commit的习惯:

lerna version -m "chore(release): publish %s" # commit message = "chore(release): publish v1.0.0" lerna version -m "chore(release): publish %v" # commit message = "chore(release): publish 1.0.0" # 独立版本模式下不替换任何占位符 lerna version -m "chore(release): publish" # commit message = "chore(release): publish # # - package-1@3.0.1 # - package-2@1.5.4"
  • 若消息包含%s,会被替换为带v前缀的全局版本号;
  • 若消息包含%v,会被替换为不带v前缀的全局版本号;
  • 占位符替换仅适用于默认的"固定"版本模式——独立版本模式没有"全局"版本可供替换,此时每个更新包的标签会以@分隔符追加到消息主体之后(源码见 gitCommitAndTagVersionForUpdates)。

该选项同样可配置在lerna.json

{ "command": { "version": { "message": "chore(release): publish %s" } } }

这对于集成 commitizen、semantic-release 等期望特定提交消息规范的项目很有用。固定模式下的占位符替换实现位于 gitCommitAndTagVersion:message.replace(/%s/g, tag).replace(/%v/g, version)

--git-tag-command <cmd>

允许指定自定义命令来应用 git tag,例如为 CI/CD 管道中没有直接写权限的包装命令提供支持:

lerna version --git-tag-command "git gh-tag %s -m %s"

也可配置在lerna.json

{ "command": { "version": { "gitTagCommand": "git gh-tag %s -m %s" } } }

默认命令是git tag %s -m %s。源码中 git-tag.ts 会将命令按空格拆分,并把%s替换为 tag 名;同时--force-git-tag会追加--force参数、--sign-git-tag会追加--sign参数。

--git-remote <name>

lerna version --git-remote upstream

将 git 变更推送到指定 remote 而不是默认的origin。默认值为origin(command.ts)。对应的推送实现见 git-push.ts,其中使用git push --follow-tags --no-verify --atomic推送,并在--atomic不被旧版 git 支持时自动降级为重试非原子推送。

--no-commit-hooks

默认情况下lerna version在提交版本变更时会允许 git commit hooks 运行。传--no-commit-hooks禁用此行为(对应 npm version 的--commit-hooks选项,语义反转)。源码中 git-commit.ts 会在commitHooks === false时追加--no-verify

--no-git-tag-version

默认lerna version会提交 package.json 变更并打标签。传--no-git-tag-version禁用此行为(对应 npm version 的--git-tag-version选项,语义反转)。禁用后源码会输出Skipping git tag/commit日志。

--no-granular-pathspec

默认情况下lerna versiongit add版本化过程中变更过的叶子包 manifest(以及可能的 changelog),效果等价于git add -- packages/*/package.json,但精确到实际变更的文件。若你确定需要不同行为,才应传--no-granular-pathspec,它会让 git 命令字面意义上变成git add -- .。选择这种 pathspec 后,必须确保所有密钥和构建产物已被正确忽略,否则它们将被提交并推送

该选项最适合配置在lerna.json中,因为真的不建议搞错:

{ "version": "independent", "granularPathspec": false }

根级配置是刻意的,这样还能同时覆盖lerna publish中同名的选项。源码实现见 git-add.ts:粒度模式收集相对路径的文件列表,非粒度模式则使用"."全局添加。

--no-private

默认情况下lerna version在选择版本、提交和打标签时会包含 private 包。传--no-private禁用此行为。注意该选项不排除私有 scoped 包(如@scope/pkg),只排除package.json中带"private": true字段的包。在 index.ts 中,--no-private会在收集更新包时完全移除 private 包,同时未设置版本的包只有在其为 private 时才会被跳过(否则抛出ENOVERSION)。

--no-push

默认lerna version会将提交和标签推送到配置的 git remote。传--no-push禁用推送。

--sign-git-commit/--signoff-git-commit/--sign-git-tag/--force-git-tag

  • --sign-git-commit:提交时传递--gpg-sign标志(对应 npm version 同名选项);
  • --signoff-git-commit:提交时追加--signoff标志。注意它与--sign-git-commit不同,后者是 GPG 签名;
  • --sign-git-tag:打标签时传递--sign标志(对应 npm version 同名选项);
  • --force-git-tag:替换任何已有标签而不是报错(对应 git tag 的--force)。

上述标志在 git-commit.ts 与 git-tag.ts 中均有直接对应实现。

--tag-version-prefix

提供自定义的 tag 前缀,默认是v注意:目前需要为version命令和publish命令各指定一次

# 本地 lerna version --tag-version-prefix='' # CI 上 lerna publish from-git --tag-version-prefix=''

--sync-dist-version

更新 contents 目录(dist 等发布产物目录)中package.json的版本号。对应pkg.syncDistVersion(syncDistVersion)调用(index.ts)。

--amend的补充说明

--no-git-tag-version--no-push组合使用时,--amend会跳过工作区校验(因为 amend 提交通常意味着工作区是脏的),此时日志会警告Skipping working tree validation, proceed at your own risk(index.ts)。

七、锁文件与 npm 客户端相关选项

--npm-client-args

该选项允许向lerna version为更新 lockfile 而执行的npm install传递参数。例如:

lerna version 3.3.3 --npm-client-args=--legacy-peer-deps lerna version 3.3.3 --npm-client-args="--legacy-peer-deps,--force" lerna version 3.3.3 --npm-client-args="--legacy-peer-deps --force"

也可设置在lerna.json中:

{ "npmClientArgs": ["--legacy-peer-deps", "--production"] }

或仅针对 version 命令:

{ "command": { "version": { "npmClientArgs": ["--legacy-peer-deps", "--production"] } } }

源码中参数数组会按空白或逗号展开后拼接进 install 命令(index.ts)。

--run-scripts-on-lockfile-update

默认情况下,lerna version在版本 bump 后同步 package-lock 文件时会跳过所有生命周期脚本。开启该选项后会运行preparepostinstall等脚本。对应实现中,未开启时会给 install 追加--ignore-scripts(index.ts)。

各包管理器 lockfile 的同步逻辑

源码 updatePackageVersions 按npmClient分支处理:

  • pnpm:执行pnpm install --lockfile-only,更新根目录pnpm-lock.yaml
  • bun:通过updateBunLockfile()更新 bun.lock(见 update-bun-lockfile.ts);
  • yarn:检测 yarn 版本,>=2.0.0时执行yarn install --mode update-lockfile并设置YARN_ENABLE_SCRIPTS=false,更新根目录yarn.lock
  • npm(或未指定):仅当根目录存在package-lock.json时执行npm install --package-lock-only,更新根目录锁文件。

八、Release 发布与其余选项

--create-release <type>

lerna version --conventional-commits --create-release github lerna version --conventional-commits --create-release gitlab

基于变更的包创建正式的 GitHub 或 GitLab Release。要求必须同时传--conventional-commits(否则源码抛出ERELEASETo create a release, you must enable --conventional-commits)。

GitHub 认证环境变量:

  • GH_TOKEN(必需):GitHub 认证 token(Settings > Developer settings > Personal access tokens),创建时请授予repo:public_reposcope;
  • GHE_API_URL:使用 GitHub Enterprise 时,API 的绝对 URL;
  • GHE_VERSION:使用 GitHub Enterprise 时,当前安装的 GHE 版本。

GitLab 认证环境变量:

  • GL_TOKEN(必需):GitLab 认证 token(User Settings > Access Tokens);
  • GL_API_URL:包含版本号的 API 绝对 URL(默认https://gitlab.com/api/v4)。

注意:使用该选项时不能同时传--no-changelog(源码中同样以ERELEASE校验,见 index.ts)。

Release 的创建实现位于 create-release.ts,会基于本次生成的 tag 与各包 changelog 条目(releaseNotes)构建 release 内容。

--no-changelog

lerna version --conventional-commits --no-changelog

使用conventional-commits时不生成任何CHANGELOG.md文件。

注意:使用该选项时不能同时传--create-release

--ignore-scripts

传入后,lerna version期间将禁用生命周期脚本的运行。

--yes

lerna version --yes # 跳过确认提示

跳过所有确认提示。在 CI 中自动应答发布确认提示非常有用。对应源码confirmVersions()this.options.yes分支(index.ts),交互式提示消息在独立执行时为Are you sure you want to create these versions?,被lerna publish组合调用时为Are you sure you want to publish these packages?

交互式版本选择器的工作方式

不带bump位置参数且不使用--conventional-commits时,会触发交互式提示。从 prompt-version.ts 的实现看,提示器会根据当前版本预计算 6 个候选(Patch / Minor / Major / Prepatch / Preminor / Premajor),并提供Custom Prerelease(自定义 prerelease 标识符)与Custom Version(自定义完整版本,需通过semver.valid校验)两个入口,所有候选都会应用--build-metadata

九、弃用选项(Deprecated Options)

以下旧选项已被移除,运行时会被拦截并提示改用新写法:

  • --cd-version:请改用bump位置参数传 semver 关键字;
  • --repo-version:请改用bump位置参数传显式版本号;
  • --skip-git:请改用--no-git-tag-version--no-push

注意:--skip-git不会限制所有 git 命令的执行——lerna version仍然依赖git

另外源码中还拦截了更早的--ignore(改名为--ignore-changes)与--github-release(改为--create-release=github)两个旧选项,并统一建议运行lerna repair来同步lerna.json(command.ts)。

十、实战技巧:生成历史 Changelog

如果在 monorepo 活跃一段时间之后才启用--conventional-commits,仍可通过conventional-changelog-cli配合lerna exec为之前的 release 生成 changelog:

# Lerna 实际并不使用 conventional-changelog-cli,需要临时安装 npm i -D conventional-changelog-cli # 文档:npx conventional-changelog --help # 固定版本模式(默认) # 先在根目录运行,再在叶子包中运行 npx conventional-changelog --preset angular --release-count 0 --outfile ./CHANGELOG.md --verbose npx lerna exec --concurrency 1 --stream -- 'conventional-changelog --preset angular --release-count 0 --commit-path $PWD --pkg $PWD/package.json --outfile $PWD/CHANGELOG.md --verbose' # 独立版本模式 # (不生成根目录 changelog) npx lerna exec --concurrency 1 --stream -- 'conventional-changelog --preset angular --release-count 0 --commit-path $PWD --pkg $PWD/package.json --outfile $PWD/CHANGELOG.md --verbose --lerna-package $LERNA_PACKAGE_NAME'

如果使用了自定义--changelog-preset,请将示例中的--preset值对应修改。

十一、生命周期脚本执行顺序

Lerna 在lerna version期间会按以下语义运行 npm 生命周期脚本:

// preversion: Run BEFORE bumping the package version. // version: Run AFTER bumping the package version, but BEFORE commit. // postversion: Run AFTER bumping the package version, and AFTER commit.

完整执行顺序:

  1. 检测变更包,选择版本 bump;
  2. 在根目录运行preversion生命周期;
  3. 对每个变更包,按拓扑顺序(所有依赖先于依赖方):
    1. 运行该包的preversion生命周期;
    2. 更新 package.json 中的版本号;
    3. 运行该包的version生命周期;
  4. 在根目录运行version生命周期;
  5. 将变更文件加入 git index(若未禁用--no-git-tag-version);
  6. 创建提交和 tag(若未禁用--no-git-tag-version);
  7. 对每个变更包,按字典顺序(按目录结构的字母序):
    1. 运行该包的postversion生命周期;
  8. 在根目录运行postversion生命周期;
  9. 将提交和 tag 推送到远端(若未禁用--no-push);
  10. 创建 Release(若启用--create-release)。

源码层面的佐证:updatePackageVersions()中根目录preversion最先执行(且存在一个防递归保护——如果npm_lifecycle_event本身就是(pre|post)?version,会跳过根生命周期避免递归,见 index.ts);包级动作链依次为preversion→ 刷新 manifest → 写入新版本与依赖 → 序列化/更新锁文件 →version→(可选)更新 changelog,并通过runProjectsTopologically保证拓扑顺序;commitAndTagUpdates()在提交打标签后通过pMap执行postversion。另外,包级更新动作之间会重新refresh()manifest,因为"任何前一个生命周期都可能修改 manifest"。git add之前还会检测工作区是否安装了 prettier,若有则对变更文件自动格式化(见 git-add.ts)。

十二、执行前的 git 前置校验

除了上述配置,lerna version在正式运行前还会做一系列 git 校验(index.ts initialize),理解这些校验有助于排查 CI 中常见的失败:

  • 仓库必须有提交:无任何提交时抛出ENOCOMMIT
  • 必须处于分支而非 detached HEADgetCurrentBranch返回HEAD时抛出ENOGIT
  • 分支必须存在于远端:需要推送时远端不存在当前分支会抛出ENOREMOTEBRANCH
  • 分支受allowBranch约束:不匹配时抛出ENOTALLOWED
  • 本地分支不得落后于上游:否则抛出EBEHIND(交互模式始终失败,CI 下由--ci-behind-behavior决定成败);
  • 工作区必须干净:默认执行checkWorkingTree,而--force-publishconventional-commits + conventional-graduate组合时降级为仅检查未提交内容(throwIfUncommitted)。

满足这些前置条件后,lerna version会依次完成版本计算、确认、manifest 更新、changelog 生成、提交打标签与推送,最终输出更新包清单与各自的新版本号,完成一次可靠的多包发布流程。

【免费下载链接】lernaLerna is a fast, modern build system for managing and publishing multiple JavaScript/TypeScript packages from the same repository.项目地址: https://gitcode.com/gh_mirrors/le/lerna

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询