26

lerna

最近在看vue-cli的源码部分,注意到这一个仓库下维护了多个package,很好奇他是如何在一个repo中管理这些package的。

我们组现在也在使用组件库的方式维护项目间共用的业务代码。有两个组件库,存在依赖的关系,目前联调是通过npm link的方式,性能并不好,时常出现卡顿的问题。加上前一段时间组内分享vue3也提到了lerna,于是便决定仔细的调研一下这个工具,为接下里的组件库优化助力。

lerna的文档还是很详细的,因为全是英文的,考虑到阅读问题,这里我先是自己跑了几个demo,然后做了中文翻译。后续我会出一篇专门的lerna实战篇

demo

lerna 是干什么的?

Lerna 是一个工具,它优化了使用 git 和 npm 管理多包存储库的工作流。

背景

1.将一个大的 package 分割成一些小的 packcage 便于分享,调试

2.在多个 git 仓库中更改容易变得混乱且难以跟踪

3.在多个 git 仓库中维护测试繁琐

两种工作模式

Fixed/Locked mode (default)

vue,babel 都是用这种,在 publish 的时候,所有的包版本都会更新,并且包的版本都是一致的,版本号维护在 lerna.jon 的 version 中

Independent mode

lerna init --independent

独立模式,每个 package 都可以有自己的版本号。版本号维护在各自 package.json 的 version 中。每次发布前都会提示已经更改的包,以及建议的版本号或者自定义版本号。这种方式相对第一种来说,更灵活

初始化项目


npm install -g lerna // 这里是全局安装,也可以安装为项目开发依赖,使用全局方便后期使用命令行

mkdir lerna-repo

cd lerna-repo

lerna init // 初始化一个lerna项目结构,如果希望各个包使用单独版本号可以加 -i | --independent

lerna init

标准的 lerna 目录结构

  • 每个单独的包下都有一个 package.json 文件
  • 如果包名是带 scope 的,例如@test/lerna,package.json 中,必须配置"publishConfig": {"access": "public"}

my-lerna-repo/

    package.json

    lerna.json

    LICENSE

    packages/

        package-1/

            package.json

        package-2/

            package.json

启用 yarn Workspaces (强烈建议)

Workspaces can only be enabled in private projects.

默认是 npm, 每个子 package 下都有自己的 node_modules,通过这样设置后,会把所有的依赖提升到顶层的 node_modules 中,并且在 node_modules 中链接本地的 package,便于调试

注意:必须是 private 项目才可以开启 workspaces


// package.json

"private": true,

"workspaces": [

    "packages/*"

],
 

// lerna.json

 
"useWorkspaces": true,

"npmClient": "yarn",

hoist: 提取公共的依赖到根目录的node_moduels,可以自定义指定。其余依赖安装的package/node_modeles中,可执行文件必须安装在package/node_modeles

workspaces: 所有依赖全部在跟目录的node_moduels,除了可执行文件

hoist vs workspaces

常用命令

lerna init

初始化 lerna 项目

  • -i, --independent 独立版本模式

[lerna create <name> [loc]](https://github.com/lerna/lern...

创建一个 packcage

  • --access 当使用scope package时(@qinzhiwei/lerna),需要设置此选项 可选值: "public", "restricted"
  • --bin 创建可执行文件 --bin <executableName>
  • --description 描述 [字符串]
  • --dependencies 依赖,用逗号分隔 [数组]
  • --es-module 初始化一个转化的Es Module [布尔]
  • --homepage 源码地址 [字符串]
  • --keywords 关键字数 [数组]
  • --license 协议 字符串
  • --private 是否私有仓库 [布尔]
  • --registry 源 [字符串]
  • --tag 发布的标签 [字符串]
  • -y, --yes 跳过所有的提示,使用默认配置 [布尔]

lerna add

为匹配的 package 添加本地或者远程依赖,一次只能添加一个依赖


$ lerna add <package>[@version] [--dev] [--exact] [--peer]

运行该命令时做的事情:

  1. 为匹配到的 package 添加依赖
  2. 更改每个 package 下的 package.json 中的依赖项属性
Command Options

以下几个选项的含义和npm install时一致

  • --dev
  • --exact
  • --peer 同级依赖,使用该package需要在项目中同时安装的依赖
  • --registry <url>
  • --no-bootstrap 跳过 lerna bootstrap,只在更改对应的 package 的 package.json 中的属性

所有的过滤选项都支持

Examples


# Adds the module-1 package to the packages in the 'prefix-' prefixed folders

lerna add module-1 packages/prefix-*

  

# Install module-1 to module-2

lerna add module-1 --scope=module-2

  

# Install module-1 to module-2 in devDependencies

lerna add module-1 --scope=module-2 --dev

  

# Install module-1 to module-2 in peerDependencies

lerna add module-1 --scope=module-2 --peer

  

# Install module-1 in all modules except module-1

lerna add module-1

  

# Install babel-core in all modules

lerna add babel-core

lerna bootstrap

将本地 package 链接在一起并安装依赖

执行该命令式做了一下四件事:

1.为每个 package 安装依赖
2.链接相互依赖的库到具体的目录,例如:如果 lerna1 依赖 lerna2,且版本刚好为本地版本,那么会在 node_modules 中链接本地项目,如果版本不满足,需按正常依赖安装
3.在 bootstraped packages 中 执行 npm run prepublish
4.在 bootstraped packages 中 执行 npm run prepare

lerna bootstrap

lerna bootstrap

Command Options
  • --hoist 匹配 [glob] 依赖 提升到根目录 [默认值: '**'], 包含可执行二进制文件的依赖项还是必须安装在当前 package 的 node_modules 下,以确保 npm 脚本的运行
  • --nohoist 和上面刚好相反 [字符串]
  • --ignore-prepublish 在 bootstraped packages 中不再运行 prepublish 生命周期中的脚本 [布尔]
  • --ignore-scripts 在 bootstraped packages 中不再运行任何生命周期中的脚本 [布尔]
  • --npm-client 使用的 npm 客户端(npm, yarn, pnpm, ...) [字符串]
  • --registry 源 [字符串]
  • --strict 在 bootstrap 的过程中不允许发出警告,避免花销更长的时间或者导致其他问题 [布尔]
  • --use-workspaces 启用 yarn 的 workspaces 模式 [布尔]
  • --force-local 无论版本范围是否匹配,强制本地同级链接 [布尔]
  • --contents 子目录用作任何链接的源。必须适用于所有包 字符串

lerna link

将本地相互依赖的 package 相互连接。例如 lerna1 依赖 lerna2,且版本号刚好为本地的 lerna2,那么会在 lerna1 下 node_modules 中建立软连指向 lerna2

Command Options
  • --force-local 无论本地 package 是否满足版本需求,都链接本地的

// 指定软链到package的特定目录

"publishConfig": {

    "directory": "dist" // bootstrap的时候软链package下的dist目录 package-1/dist => node_modules/package-1

}

lerna list

list 子命令
  • lerna ls: 等同于 lerna list本身,输出项目下所有的 package
  • lerna ll: 输出项目下所有 package 名称、当前版本、所在位置
  • lerna la: 输出项目下所有 package 名称、当前版本、所在位置,包括 private package
Command Options

所有的过滤选项都支持

--json

以 json 形式展示


$ lerna ls --json

[

    {

        "name": "package-1",

        "version": "1.0.0",

        "private": false,

        "location": "/path/to/packages/pkg-1"

    },

    {

        "name": "package-2",

        "version": "1.0.0",

        "private": false,

        "location": "/path/to/packages/pkg-2"

    }

]
--ndjson

newline-delimited JSON展示信息


$ lerna ls --ndjson

{"name":"package-1","version":"1.0.0","private":false,"location":"/path/to/packages/pkg-1"}

{"name":"package-2","version":"1.0.0","private":false,"location":"/path/to/packages/pkg-2"}
--all

Alias: -a

显示默认隐藏的 private package


$ lerna ls --all

package-1

package-2

package-3 (private)
--long

Alias: -l

显示包的版本、位置、名称


$ lerna ls --long

package-1 v1.0.1 packages/pkg-1

package-2 v1.0.2 packages/pkg-2

  

$ lerna ls -la

package-1 v1.0.1 packages/pkg-1

package-2 v1.0.2 packages/pkg-2

package-3 v1.0.3 packages/pkg-3 (private)
--parseable

Alias: -p

显示包的绝对路径

In --long output, each line is a :-separated list: <fullpath>:<name>:<version>[:flags..]


$ lerna ls --parseable

/path/to/packages/pkg-1

/path/to/packages/pkg-2

  

$ lerna ls -pl

/path/to/packages/pkg-1:package-1:1.0.1

/path/to/packages/pkg-2:package-2:1.0.2

  

$ lerna ls -pla

/path/to/packages/pkg-1:package-1:1.0.1

/path/to/packages/pkg-2:package-2:1.0.2

/path/to/packages/pkg-3:package-3:1.0.3:PRIVATE
--toposort

按照拓扑顺序(dependencies before dependents)对包进行排序,而不是按目录对包进行词法排序。


$ json dependencies <packages/pkg-1/package.json

{

    "pkg-2": "file:../pkg-2"

}

  

$ lerna ls --toposort

package-2

package-1
--graph

将依赖关系图显示为 JSON 格式的邻接表 adjacency list.


$ lerna ls --graph

{

    "pkg-1": [

        "pkg-2"

    ],

    "pkg-2": []

}

  

$ lerna ls --graph --all

{

    "pkg-1": [

       "pkg-2"

    ],

    "pkg-2": [

        "pkg-3"

    ],

    "pkg-3": [

        "pkg-2"

    ]

}

lerna changed

列出自上次发布(打 tag)以来本地发生变化的 package

注意: lerna publishlerna versionlerna.json配置同样影响lerna changed。 例如 command.publish.ignoreChanges.

Command Options

lerna changed 支持 lerna ls的所有标记:

lerna 不支持过滤选项, 因为lerna version or lerna publish不支持过滤选项.

lerna changed 支持 lerna version (the others are irrelevant)的过滤选项:

lerna import

lerna import <path-to-external-repository>

将现有的 package 导入到 lerna 项目中。可以保留之前的原始提交作者,日期和消息将保留。

注意:如果要在一个新的 lerna 中引入,必须至少有个 commit

Command Options
  • --flatten 处理合并冲突
  • --dest 指定引入包的目录
  • --preserve-commit 保持引入项目原有的提交者信息

lerna clean

lerna clean

移除所有 packages 下的 node_modules,并不会移除根目录下的

所有的过滤选项都支持

lerna diff

查看自上次发布(打 tag)以来某个 package 或者所有 package 的变化


$ lerna diff [package]

  

$ lerna diff

# diff a specific package

$ lerna diff package-name
Similar to lerna changed. This command runs git diff.

lerna exec

在每个 package 中执行任意命令,用波折号(--)分割命令语句

使用方式

$ lerna exec -- <command> [..args] # runs the command in all packages

$ lerna exec -- rm -rf ./node_modules

$ lerna exec -- protractor conf.js

可以通过LERNA_PACKAGE_NAME变量获取当前 package 名称:


$ lerna exec -- npm view $LERNA_PACKAGE_NAME

也可以通过LERNA_ROOT_PATH获取根目录绝对路径:


$ lerna exec -- node $LERNA_ROOT_PATH/scripts/some-script.js
Command Options

所有的过滤选项都支持


$ lerna exec --scope my-component -- ls -la
  • --concurrenty
使用给定的数量进行并发执行(除非指定了 --parallel)。

输出是经过管道过滤,存在不确定性。

如果你希望命令一个接着一个执行,可以使用如下方式:


$ lerna exec --concurrency 1 -- ls -la
  • --stream

从子进程立即输出,前缀是包的名称。该方式允许交叉输出:


$ lerna exec --stream -- babel src -d lib

lerna exec --stream -- babel src -d lib

  • --parallel

--stream很像。但是完全忽略了并发性和排序,立即在所有匹配的包中运行给定的命令或脚本。适合长时间运行的进程。例如处于监听状态的babel src -d lib -w


$ lerna exec --parallel -- babel src -d lib -w
注意: 建议使用命令式控制包的范围。

因为过多的进程可能会损害shell的稳定。例如最大文件描述符限制

  • --no-bail

# Run a command, ignoring non-zero (error) exit codes

$ lerna exec --no-bail <command>

默认情况下,如果一但出现命令报错就会退费进程。使用该命令会禁止此行为,跳过改报错行为,继续执行其他命令

  • --no-prefix

在输出中不显示 package 的名称

  • --profile

生成一个 json 文件,可以在 chrome 浏览器(devtools://devtools/bundled/devtools_app.html)查看性能分析。通过配置--concurrenty可以开启固定数量的子进程数量

lerna exec --stream -- babel src -d lib


$ lerna exec --profile -- <command>
注意: 仅在启用拓扑排序时分析。不能和 --parallel and --no-sort一同使用。
  • --profile-location <location>

设置分析文件存放位置


$ lerna exec --profile --profile-location=logs/profile/ -- <command>

lerna run

在每个 package 中运行 npm 脚本

使用方法

$ lerna run <script> -- [..args] # runs npm run my-script in all packages that have it

$ lerna run test

$ lerna run build

  

# watch all packages and transpile on change, streaming prefixed output

$ lerna run --parallel watch
Command Options
  • --npm-client <client>

设置npm客户端,默认是npm


$ lerna run build --npm-client=yarn

也可以在lerna.json配置:


{

    "command": {

        "run": {

            "npmClient": "yarn"

        }

    }

}
  • 其余同lerna exec

lerna version

生成新的唯一版本号

bumm version:在使用类似 github 程序时,升级版本号到一个新的唯一值

使用方法

lerna version 1.0.1 # 显示指定

lerna version patch # 语义关键字

lerna version # 从提示中选择

当执行时,该命令做了一下事情:

1.识别从上次打标记发布以来发生变更的 package 2.版本提示 3.修改 package 的元数据反映新的版本,在根目录和每个 package 中适当运行lifecycle scripts 4.在 git 上提交改变并对该次提交打标记(git commit & git tag) 5.提交到远程仓库(git push)

lerna version

Positionals
semver bump

lerna version [major | minor | patch | premajor | preminor | prepatch | prerelease]

# uses the next semantic version(s) value and this skips `Select a new version for...` prompt

When this positional parameter is passed, lerna version will skip the version selection prompt and increment the version by that keyword.

You must still use the --yes flag to avoid all prompts.

Prerelease

如果某些 package 是预发布版本(e.g. 2.0.0-beta.3),当你运行lerna version配合语义化版本时(major, minor, patch),它将发布之前的预发布版本和自上次发布以来改变过的 packcage。

对于使用常规提交的项目,可以使用如下标记管理预发布版本:

  • --conventional-prerelease: 发布当前变更为预发布版本(即便采用的是固定模式,也会单独升级该 package)
  • --conventional-graduate: 升级预发布版本为稳定版(即便采用的是固定模式,也会单独升级该 package)

当一个 package 为预发版本时,不使用上述标记,使用lerna version --conventional-commits,也会按照预发版本升级继续升级当前 package。

lerna la

lerna version --conventional-commits

Command Options
--allow-branch <glob>

A whitelist of globs that match git branches where lerna version is enabled.

It is easiest (and recommended) to configure in lerna.json, but it is possible to pass as a CLI option as well.

设置可以调用lerna version命令的分支白名单,也可以在lerna.json中设置


{

    "command": {

        "version": {

            "allowBranch": ["master", "beta/*", "feature/*"]

        }

    }

}
--amend

lerna version --amend

# commit message is retained, and `git push` is skipped.

默认情况下如果暂存区有未提交的内容,lerna version会失败,需要提前保存本地内容。使用该标记可以较少 commit 的次数,将当前变更内容随着本次版本变化一次 commit。并且不会git push

--changelog-preset

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

默认情况下,changelog 预设设置为angular。在某些情况下,您可能需要使用另一个预置或自定义。

--conventional-commits

lerna version --conventional-commits

当使用这个标志运行时,lerna 版本将使用传统的提交规范/Conventional Commits Specification确定版本并生成CHANGELOG.md

传入 --no-changelog 将阻止生成或者更新CHANGELOG.md.

--conventional-graduate

lerna version --conventional-commits --conventional-graduate=package-2,package-4

  

# force all prerelease packages to be graduated

lerna version --conventional-commits --conventional-graduate

但使用该标记时,lerna vesion将升级指定的 package(用逗号分隔)或者使用*指定全部 package。和--force-publish很像,无论当前的 HEAD 是否发布,该命令都会起作用,任何没有预发布的 package 将会被忽略。如果未指定的包(如果指定了包)或未预先发布的包发生了更改,那么这些包将按照它们通常使用的--conventional-commits进行版本控制。

"升级"一个包意味着将一个预发布的包升级为发布版本,例如package-1@1.0.0-alpha.0 => package-1@1.0.0

注意: 当指定包时,指定包的依赖项将被释放,但不会被“升级”。必须和--conventional-commits一起使用
--conventional-prerelease

lerna version --conventional-commits --conventional-prerelease=package-2,package-4

  

# force all changed packages to be prereleased

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

当使用该标记时,lerna version将会以预发布的版本发布指定的 package(用逗号分隔)或者使用*指定全部 package。

--create-release <type>

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

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

当使用此标志时,lerna version会基于改变的 package 创建一个官方正式的 GitHub 或 GitLab 版本记录。需要传递--conventional-commits去创建 changlog。

GithuB 认证,以下环境变量需要被定义。

  • GH_TOKEN (required) - Your GitHub authentication token (under Settings > Developer settings > Personal access tokens).
  • GHE_API_URL - When using GitHub Enterprise, an absolute URL to the API.
  • GHE_VERSION - When using GitHub Enterprise, the currently installed GHE version. Supports the following versions.

GitLab 认证,以下环境变量需要被定义。

  • GL_TOKEN (required) - Your GitLab authentication token (under User Settings > Access Tokens).
  • GL_API_URL - An absolute URL to the API, including the version. (Default: https://gitlab.com/api/v4)
注意: 不允许和--no-changelog一起使用

这个选项也可以在lerna.json中配置:


{

    "changelogPreset": "angular"

}

If the preset exports a builder function (e.g. conventional-changelog-conventionalcommits), you can specify the preset configuration too:


{

    "changelogPreset": {

        "name": "conventionalcommits",

        "issueUrlFormat": "{{host}}/{{owner}}/{{repository}}/issues/{{id}}"

    }

}

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

--exact

lerna version --exact
--force-publish

lerna version --force-publish=package-2,package-4

  

# force all packages to be versioned

lerna version --force-publish

强制更新版本

这个操作将跳过lerna changed检查,即便 package 没有做任何变更也会更新版本
--git-remote <name>

lerna version --git-remote upstream

将本地commitpush 到指定的远程残酷,默认是origin

--ignore-changes

变更检测时忽略的文件


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

建议在根目录lerna.json中配置:


{

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

}

--no-ignore-changes 禁止任何现有的忽略配置:

--ignore-scripts

禁止lifecycle scripts

--include-merged-tags

lerna version --include-merged-tags
--message <msg>

-m别名,等价于git commit -m


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"

  

# When versioning packages independently, no placeholders are replaced

lerna version -m "chore(release): publish"

# commit message = "chore(release): publish

#

# - package-1@3.0.1

# - package-2@1.5.4"

也可以在lerna.json配置:


{

    "command": {

        "version": {

            "message": "chore(release): publish %s"

        }

    }

}
--no-changelog

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

不生成CHANGELOG.md

注意:不可以和--create-release一起使用
--no-commit-hooks

默认情况下,lerna version会运行git commit hooks。使用该标记,阻止git commit hooks运行。

--no-git-tag-version

默认情况下,lerna version 会提交变更到package.json文件,并打标签。使用该标记会阻止该默认行为。

--no-granular-pathspec

默认情况下,在创建版本的过程中,会执行git add -- packages/*/package.json操作。

也可以更改默认行为,提交除了package.json以外的信息,前提是必须做好敏感数据的保护。


// leran.json

{

    "version": "independent",

    "granularPathspec": false

}
--no-private

排除private:true的 package

--no-push

By default, lerna version will push the committed and tagged changes to the configured git remote.

Pass --no-push to disable this behavior.

--preid

lerna version prerelease

# uses the next semantic prerelease version, e.g.

# 1.0.0 => 1.0.1-alpha.0

  

lerna version prepatch --preid next

# uses the next semantic prerelease version with a specific prerelease identifier, e.g.

# 1.0.0 => 1.0.1-next.0

版本语义化

--sign-git-commit

npm version option

--sign-git-tag

npm version option

--force-git-tag

取代已存在的tag

--tag-version-prefix

自定义版本前缀。默认为v


# locally

lerna version --tag-version-prefix=''

# on ci

lerna publish from-git --tag-version-prefix=''
--yes

lerna version --yes

# skips `Are you sure you want to publish these packages?`

跳过所有提示

生成更新日志CHANGELOG.md

如果你在使用多包存储一段时间后,开始使用--conventional-commits标签,你也可以使用conventional-changelog-clilerna exec为之前的版本创建 changelog:


# Lerna does not actually use conventional-changelog-cli, so you need to install it temporarily

npm i -D conventional-changelog-cli

# Documentation: `npx conventional-changelog --help`

  

# fixed versioning (default)

# run in root, then leaves

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'

  

# independent versioning

# (no root 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'

If you use a custom --changelog-preset, you should change --preset value accordingly in the example above.

Lifecycle Scripts

// 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.

Lerna will run npm lifecycle scripts during lerna version in the following order:

  1. Detect changed packages, choose version bump(s)
  2. Run preversion lifecycle in root
  3. For each changed package, in topological order (all dependencies before dependents):
  4. Run preversion lifecycle
  5. Update version in package.json
  6. Run version lifecycle
  7. Run version lifecycle in root
  8. Add changed files to index, if enabled
  9. Create commit and tag(s), if enabled
  10. For each changed package, in lexical order (alphabetical according to directory structure):
  11. Run postversion lifecycle
  12. Run postversion lifecycle in root
  13. Push commit and tag(s) to remote, if enabled
  14. Create release, if enabled

lerna publish


lerna publish # 发布自上次发版依赖更新的packages

lerna publish from-git # 显示的发布在当前提交中打了tag的packages

lerna publish from-package # 显示的发布当前版本在注册表中(registry)不存在的packages(之前没有发布到npm上)

运行时,该命令执行以下操作之一:

  • 发布自上次发版依赖更新的 packages(背后调用lerna version判断)
  • 这是 2.x 版本遗留的表现
  • 显示的发布在当前提交中打了 tag 的 packages
  • 显示的发布在最新的提交中当前版本在注册表中(registry)不存在的 packages(之前没有发布到 npm 上)
  • 发布在之前提交中未版本化的进行过金丝雀部署的 packages(canary release)
Lerna 无法发布私有的 packcage("private":true)

在所有发布操作期间,适当的生命周期脚本(lifecycle scripts)在根目录和每个包中被调用(除非被--ignore-scripts禁用)。

Positionals
  • bump from-git

除了lerna version支持的 semver 关键字之外,lerna publish还支持from-git关键字。这将识别lerna version标记的包,并将它们发布到 npm。这在 CI 场景中非常有用,在这种场景中,您希望手动增加版本,但要通过自动化过程一致地发布包内容本身

  • bump from-package

from-git关键字相似,除了要发布的软件包列表是通过检查每个package.json并确定注册表中是否没有任何软件包版本来确定的。 注册表中不存在的任何版本都将被发布。 当先前的lerna publish未能将所有程序包发布到注册表时,此功能很有用。

Command Options

lerna publish除了支持一下选项外,还支持lerna version的所有选项:

--canary

lerna publish --canary

# 1.0.0 => 1.0.1-alpha.0+${SHA} of packages changed since the previous commit

# a subsequent canary publish will yield 1.0.1-alpha.1+${SHA}, etc

  

lerna publish --canary --preid beta

# 1.0.0 => 1.0.1-beta.0+${SHA}

  

# The following are equivalent:

lerna publish --canary minor

lerna publish --canary preminor

# 1.0.0 => 1.1.0-alpha.0+${SHA}

针对最近一次提交发生改变的 package,做更精细的版本控制。类似于金丝雀部署,构建生产环境的容错测试。如果是统一的版本控制,其他 package 版本号不做升级,只针对变更的 package 做精准调试。

lerna publish --canary

--contents <dir>

子目录发布。子目录中必须包含 package.json。


lerna publish --contents dist

# publish the "dist" subfolder of every Lerna-managed leaf package
--dist-tag <tag>

lerna publish --dist-tag custom-tag

自定义 npm发布标签。默认是latest

该选项可以用来定义prerelease 或者 beta 版本

注意: npm install my-package 默认安装的是latest版本.

安装其他版本 npm install my-package@prerelease.

lerna publish --canary

--git-head <sha>

只可以和from-package配合使用,根据指定的git <sha>发布

也可以使用环境变量指定


lerna publish from-package --git-head ${CODEBUILD_RESOLVED_SOURCE_VERSION}
--graph-type <all|dependencies>

npm上构建package dependencies所采用的方式,默认是dependencies,只列出dependenciesall会列出dependenciesdevDependencies


lerna publish --graph-type all

也可以通过lerna.json配置:


{

    "command": {

        "publish": {

            "graphType": "all"

        }

    }

}

lerna publish --graph-type all

--ignore-scripts

关闭npm脚本生命周期事件的触发

--ignore-prepublish

近关闭npm脚本生命周期 prepublish事件的触发

--legacy-auth

发布前的身份验证


lerna publish --legacy-auth aGk6bW9t
--no-git-reset

默认情况下,lerna publish会把暂存区内容全部提交。即lerna publish发布时更改了本地 package 中的 version,也一并提交到 git。

lerna publish

未避免上述情况发生,可以使用--no-git-reset。这对作为管道配置--canary使用时非常有用。例如,已经改变的package.json的版本号可能会在下一步操作所用到(例如 Docker builds)。


lerna publish --no-git-reset
--no-granular-pathspec

By default, lerna publish will attempt (if enabled) to git checkout only the leaf package manifests that are temporarily modified during the publishing process. This yields the equivalent of git checkout -- packages/*/package.json, but tailored to exactly what changed.

If you know you need different behavior, you'll understand: Pass --no-granular-pathspec to make the git command literally git checkout -- .. By opting into this pathspec, you must have all intentionally unversioned content properly ignored.

This option makes the most sense configured in lerna.json, as you really don't want to mess it up:


{

    "version": "independent",

    "granularPathspec": false

}

The root-level configuration is intentional, as this also covers the identically-named option in lerna version.

--no-verify-access

默认情况下lerna会验证已登录用户对即将发布的 package 的权限。使用此标记将会阻止该默认行为。

如果你正在使用第三方的不支持npm access ls-packages的 npm 库,需要使用该标记。或者在lerna.json中设置command.publish.verifyAccessfalse

谨慎使用
--otp

当发布需要双重认证的 package 时,需要指定一次性密码


lerna publish --otp 123456

当开启npm 双重认证后,可以通过配置对 account 和 npm 操作的进行二次验证。需要 npm 版本大于5.5.0

验证工具

Two-factor authentication

密码的有效时长为 30s,过期后需要重新输入验证
--preid

Unlike the lerna version option of the same name, this option only applies to --canary version calculation.

--canary 配合使用,指定语义化版本


lerna publish --canary

# uses the next semantic prerelease version, e.g.

# 1.0.0 => 1.0.1-alpha.0

  

lerna publish --canary --preid next

# uses the next semantic prerelease version with a specific prerelease identifier, e.g.

# 1.0.0 => 1.0.1-next.0

当使用该标记时,lerna publish --canary 将增量改变 premajor, preminor, prepatch, 或者 prerelease 的语义化版本。

语义化版本(prerelease identifier)

--pre-dist-tag <tag>

lerna publish --pre-dist-tag next

效果和--dist-tag一样。只适用于发布的预发布版本。

--registry <url>
--tag-version-prefix

更改标签前缀

如果分割lerna versionlerna publish,需要都设置一遍:


# locally

lerna version --tag-version-prefix=''

  

# on ci

lerna publish from-git --tag-version-prefix=''

也可以在lerna.json中配置该属性,效果等同于上面两条命令:


{

"tagVersionPrefix": "",

"packages": ["packages/*"],

"version": "independent"

}
--temp-tag

当传递时,这个标志将改变默认的发布过程,首先将所有更改过的包发布到一个临时的 dis tag (' lerna-temp ')中,然后将新版本移动到'--dist-tag '(默认为' latest ')配置的 dist-tag 中。

这通常是没有必要的,因为 Lerna 在默认情况下会按照拓扑顺序(所有依赖先于依赖)发布包

--yes

lerna publish --canary --yes

# skips `Are you sure you want to publish the above changes?`

跳过所有的确认提示

Continuous integration (CI)很有用,自动回答发布时的确认提示

每个 package 的配置

每个 package 可以通过更改publishConfig,来改变发布时的一些行为。

publishConfig.access

当发布一个scope的 package(e.g., @mycompany/rocks)时,必须设置access


"publishConfig": {

    "access": "public"

}
  • 如果在没有使用 scope 的 package 中使用该属性,将失败
  • 如果你希望保持一个 scope 的 package 为私有(i.e., "restricted"),那么就不需要设置

注意,这与在包中设置"private":true不一样;如果设置了private字段,那么在任何情况下都不会发布该包。

publishConfig.registry

"publishConfig": {

    "registry": "http://my-awesome-registry.com/"

}
  • 也可以通过--registry或者在 lerna.json 中设置command.publish.registry进行全局控制
publishConfig.tag

自定义该包发布时的标签tag:


"publishConfig": {

    "tag": "flippin-sweet"

}
  • --dist-tag将覆盖每个 package 中的值
  • 在使用[--canary]时该值将被忽略
publishConfig.directory

非标准字段,自定义发布的文件


"publishConfig": {

    "directory": "dist"

}
npm 脚本生命周期

// prepublish: Run BEFORE the package is packed and published.

// prepare: Run BEFORE the package is packed and published, AFTER prepublish, BEFORE prepublishOnly.

// prepublishOnly: Run BEFORE the package is packed and published, ONLY on npm publish.

// prepack: Run BEFORE a tarball is packed.

// postpack: Run AFTER the tarball has been generated and moved to its final destination.

// publish: Run AFTER the package is published.

// postpublish: Run AFTER the package is published.

lerna publish执行时,按如下顺序调用npm 脚本生命周期

  1. 如果采用隐式版本管理,则运行所有 version lifecycle scripts
  2. Run prepublish lifecycle in root, if enabled
  3. Run prepare lifecycle in root
  4. Run prepublishOnly lifecycle in root
  5. Run prepack lifecycle in root
  6. For each changed package, in topological order (all dependencies before dependents):
  7. Run prepublish lifecycle, if enabled
  8. Run prepare lifecycle
  9. Run prepublishOnly lifecycle
  10. Run prepack lifecycle
  11. Create package tarball in temp directory via JS API
  12. Run postpack lifecycle
  13. Run postpack lifecycle in root
  14. For each changed package, in topological order (all dependencies before dependents):
  15. Publish package to configured registry via JS API
  16. Run publish lifecycle
  17. Run postpublish lifecycle
  18. Run publish lifecycle in root
  • To avoid recursive calls, don't use this root lifecycle to run lerna publish
  1. Run postpublish lifecycle in root
  2. Update temporary dist-tag to latest, if enabled

过滤选项

  • --scope 为匹配到的 package 安装依赖 [字符串]
  • --ignore 和上面正相反 [字符串]
  • --no-private 排除 private 的 packcage
  • --since 包含从指定的[ref]依赖改变的 packages,如果没有[ref],默认是最近的 tag
  • --exclude-dependents 当使用—since 运行命令时,排除所有传递依赖项,覆盖默认的“changed”算法 [布尔]
  • --include-dependents 启动命令式包含所有传递的依赖项,无视 --scope, --ignore, or --since [布尔]
  • --include-dependencies 启动命令式包含所有传递的依赖项,无视 --scope, --ignore, or --since [布尔]
  • --include-merged-tags 在使用—since 运行命令时,包含来自合并分支的标记 [布尔]

全局选项

  • --loglevel 打印日志的级别 [字符串] 默认值: info
  • --concurrency 并行任务时启动的进程数目 [数字] [默认值: 4]
  • --reject-cycles 如果 package 之间相互依赖,则失败 [布尔]
  • --no-progress 关闭进程进度条 [布尔]
  • --no-sort 不遵循拓扑排序 [布尔]
  • --max-buffer 设置子命令执行的 buffer(以字节为单位) [数字]
  • -h, --help 显示帮助信息
  • -v, --version 显示版本信息

Concept

lerna.json


{

    "version": "1.1.3", // 版本

    "npmClient": "npm", // npm客户端

    "command": {

        "publish": {

            "ignoreChanges": ["ignored-file", "*.md"], // 发布检测时忽略的文件

            "message": "chore(release): publish", // 发布时 tag标记的版本信息

            "registry": "https://npm.pkg.github.com" // 源

    },

    "bootstrap": {

        "ignore": "component-*", // bootstrap时忽略的文件

        "npmClientArgs": ["--no-package-lock"], // 命令行参数

    },

    "version": {

        "allowBranch": [ // 允许运行lerna version的分支

                "master",

                "feature/*"

        ],

    "message": "chore(release): publish %s" // 创建版本时 tag标记的版本信息

    }

},

    "ignoreChanges": [

        "**/__fixtures__/**",

        "**/__tests__/**",

        "**/*.md"

    ],

    "packages": ["packages/*"],// package位置

}

Wizard

这里推荐一个新手引导工具lerna-wizard

lerna-wizard


蚂蚁呀嘿
103 声望5 粉丝