跳到主要內容
黯羽輕揚每天積累一點點

lerna 入門指南

免費2018-01-13#Tool#monorepo#lerna monorepo#babel lerna#lerna tutorial#lerna-changelog

簡單實用的 monorepo 管理工具,再也不要人肉整理 changelog

一。定位

Lerna is a tool that optimizes the workflow around managing multi-package repositories with git and npm.

多模組管理工具,用來幫助維護 monorepo

P.S.Lerna 是 Babel 自己日用並開源的工具,見 Why is Babel a monorepo?

二。monorepo

monorepo(monolithic repository),與 multirepo 相對,分別是單代碼倉庫與多代碼倉庫(one-repository-per-module)

multirepo 即傳統做法,按模組分為多個代碼庫,實踐中發現一些問題:

  • issue 管理混亂,經常有在 core repo 提 module 問題的,需要 Close this and track that

  • changelog 難以整合,需要人工梳理所有變動的倉庫,並做整合

  • core repo 版本更新麻煩,需要同步所有 module 更新其依賴的 core repo 版本

monorepo 把所有相關 module 都放到一個 repo 裡,每個 module 獨立發布,但使用與該 repo 統一的版本號(例如 BabelReact),issue 和 PR 都集中到該 repo,changelog 可以簡單地從一份 commit 列表梳理出來(甚至如果按照 commit 規範關聯 issue tag 的話,能夠自動生成規範的 changelog)

monorepo 也存在一些問題,但不如上面提到的痛點強烈:

  • repo 體積較大,可能帶來版本控制的問題(Git 不適合管理體積太大的 repo)

  • 統一構建工具,對構建工具提出了更高要求,要能構建各種相關 module

從源碼管理的角度來看,multirepo 與 monorepo 是兩種不同的理念,前者允許多元化發展,各個 module 可以有自己的玩法(構建,依賴管理,單元測試等),後者希望集中管理,減少玩法差異帶來的溝通��本

monorepo 標誌性的特徵是目錄結構,例如 React:

react-16.2.0/
  packages/
    react/
    react-art/
    react-.../

每個 module 都有自己的依賴項(package.json),能夠作為獨立的 npm package 發布,只是源碼放在一起維護

典型案例:

P.S. 之前使用 rollup 遇到問題都先去主 repo 查相關 issue,再根據線索找到對應的 plugin repo,再查相關 issue。一直感覺異常麻煩,又說不出來哪裡不對,原來是源碼組織方式帶來的困擾

三。lerna 試玩

// 安裝
npm install lerna -g
git init hoho-lerna && cd hoho-lerna
// 初始化目錄結構
lerna init

得到如下結構:

hoho-lerna/
  packages/
  lerna.json
  package.json

創建 module:

mkdir packages/hoho-lerna-core && cd packages/hoho-lerna-core
npm init

這樣最終會得到一堆 package:

packages/
  hoho-lerna-core/
    package.json
  hoho-lerna-module-a/
    package.json
  hoho-lerna-module-b/
    package.json
  module.../

我們實際做的事情是按模組拆分成 package,並(通過 module 級的 package.json)聲明了各 package 之間的依賴關係

依賴處理

如果 moduleA 依賴 core,通過 lerna bootstrap 命令處理依賴過後,會在 moduleAnode_modules 下創建軟鏈接指向 core 目錄,有一隻 活生生的例子

注意:npm不會自動安裝 peerDependencies,lerna 也不提供這個服務

lerna bootstrap 按照之前聲明的依賴關係,通過建立軟鏈接來把各 package 實際關聯起來

發布 package

既然都放在 packages 裡了,容易統一管理,所以支持一鍵發布所有 package 到 npm

P.S. 先要有 npm 賬號(自行 註冊),並 npm adduser 添加到本地配置裡

準備好之後,迫不及待的開始一箭 n 星

lerna publish

不出意外的話,會得到類似輸出:

lerna info version 2.7.0
lerna info current version 0.0.0
lerna info Checking for updated packages...
lerna info Comparing with initial commit.
lerna info Checking for prereleased packages...
? Select a new version (currently 0.0.0) Major (1.0.0)

Changes:
 - hoho-lerna-core: 1.0.0 => 1.0.0
 - hoho-lerna-module-a: 1.0.0 => 1.0.0
 - hoho-lerna-module-b: 1.0.0 => 1.0.0

? Are you sure you want to publish the above changes? Yes
lerna info publish Publishing packages to npm...
lerna info published hoho-lerna-module-b
lerna info published hoho-lerna-core
lerna info published hoho-lerna-module-a
lerna info git Pushing tags...
Successfully published:
 - hoho-lerna-core @1.0.0
 - hoho-lerna-module-a @1.0.0
 - hoho-lerna-module-b @1.0.0
lerna success publish finished

然後,npm registry 裡就多了 3 個垃圾 package……

publish 的大致過程是:

  1. 本地打個 tag(例如 git tag v1.0.0

  2. 自動更新依賴項版本號 示例

  3. 然後把各個 package 發布到 npm

  4. 最後把 tag 和相應的 commit 給 push 上去

注意:如果發布到 npm 這一步失敗了的話(比如沒配置 npm 賬號),下一次直接 lerna publish 無法直接發布,貌似因為本地 tag 已經是 v1.0.0 認為上次發布成功了。把這個 tag 手動滾掉也不行,.git 裡可能記了一些發布狀態,滾掉之後出現 commit hash 匹配錯誤,這裡不太友好

P.S. 更多命令請查看 Lerna

自動生成 changelog

先安裝 changelog 工具:

npm install lerna-changelog -g

然後在 lerna.json 添加對應配置項:

"changelog": {
  "repo": "ayqy/hoho-lerna",
  "labels": {
    "enhancement": ":rocket: Enhancement",
    "bug": ":bug: Bug Fix",
    "doc": "Refine Doc",
    "feat": "New Feature"
  },
  "cacheDir": ".changelog"
}

特別注意repo 是必填的,說是能自動推斷,實際上不太靠譜,見 The 'repo' field automatically inferred failed, but no error occurred

P.S.labels 裡,key 是要在 Github 配置的標籤,用來給 Issue/PR 分類,value 裡的:bug:只是調皮的 emoji,會作為 changelog 裡該類 change 的標題

到這裡還不算完,還需要 Github repo 權限(為了能查 Issue、PR),把 token 以環境變量的形式暴露出來(常用的話,可以添到 ~/.bash_profile 裡):

export GITHUB_AUTH="..."

配置完畢。要達到「自動」,前提是日常開發維護遵守約定的規範,否則最後工具肯定猜不出來 changelog。規範是指:

  1. (建議)commit message 關聯上對應的 issue

  2. (必須)創建 PR 時要選擇我們預定義的 label

因為工具只整理 github 帶有指定 label 的 PR,並把 commit message 作為 changelog 項,建議 commit message 裡關聯上 issue,生成的 changelog 就能關聯到對應 issue:

Uses github PR/Issue names categorized by labels with configurable headings.

例如:

git cm -m "feat: changelog, Close #1"

然後提交 PR 並給貼上 label:feat,merge 之後,本地 pull 過來試試 lerna-changelog

## Unreleased (2018-01-13)

#### New Feature
* [#2](https://github.com/ayqy/hoho-lerna/pull/2) feat: changelog, Closes [#1](https://github.com/ayqy/hoho-lerna/issues/1). ([ @ayqy](https://github.com/ayqy))

#### Committers: 1
- 黯羽輕揚 ([ayqy](https://github.com/ayqy))

相當漂亮https://github.com/ayqy/hoho-lerna/releases/tag/v1.1.0

P.S. 應該在 .gitignore 忽略掉本地生成的 changelog 臨時文件,僅在發布新版本時本地 lerna-changelog,並把生成的 changelog 貼到 release note。不自動發布 release note 可能是 API 限制或出於慎重考慮,畢竟 release note 還是比較重要的

另外,以這種方式自動整理出 changelog,實際上靠的是開發中約束(PR 的 label 規範,commit message 作為 changelog 項的規範),與 lerna 沒有太大關係,只要是 monorepo(Issue/PR)都放在一起,就可以按照這個思路獲取 Issue/PR 信息,整理出 changelog

相當於把最後梳理 changelog 的巨大工作量分佈到日常開發維護了,change 都要走 PR,而且要有 issue 記錄,不習慣的話還是很麻煩的(有要求 commit message 自帶 label 而不走 PR 的 呼聲,以後應該會支持)

四。適用場景

哪些場景可以採用 monorepo(並用 lerna 管理?)?

  • 不過分龐大的項目,整合到一起有 100G 源碼的話,還是再考慮一下吧

  • 多模組/插件化項目,把官方維護的插件都作為 package 非常合適

另外,還需要:

  • 基礎建設

  • 團隊信任

基礎建設是指強大的構建工具,能滿足所有模組的 build 需求(純前端項目的話,build 壓力不大)

monorepo 環境下,可以並且鼓勵改別人的代碼,一方面需要持續集成機制(例如 React - CircleCI)確認修改帶來的影響,另一方面還需要不同團隊之間互相信任,否則會經常出現一個團隊的變更影響了另一個團隊的情況,需要回滾掉別人的修改,反而影響效率

P.S.Lerna 出來很久了(和 Babel 差不多年紀),很多項目都在用了

參考資料

評論

暫無評論,快來發表你的看法吧

提交評論