轉發:基於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配合語義化版本時(majorminorpatch),它將發布之前的預發布版本和自上次發布以來改變過的 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-cli 和 lerna 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會列出dependencies 和 devDependencies

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 將增量改變 premajorpreminorprepatch, 或者 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"], // 命令行參數 },


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM