iT邦幫忙

0

JSDC 2020 回顧 - Lerna

png
(https://www.pinterest.com/pin/492722015457646968/)

兩種管理程式碼的方式

在 git、npm 成為主流後,為了能獨立各自的開發環境,我們會為每個專案建立一個 repository 進行版控和套件管理,這種方式就稱為 Multi-Repo

不過產品線的範圍變大時,專案 B、C 都是基於專案 A 的底層程式去做新服務的開發,或甚至專案之間都有一些相依的程式碼時,除非能將這些共用的程式另外做成套件,並且保證每次的更動在多個專案下都能運作,不然通常會很難維護在同一支產品線下的各個專案。

Mono-Repo

跟 Multi-Repo 相反,Mono-Repo是將相關的產品專案都放在同一個 repository。

Mono-Repo 的好處主要有:

  • Better developer testing - 可以立即知道修改程式碼後,所有相關的專案是否還能正常運作
  • Sharing of common components - 只要開發一次元件,就能給所有專案使用
  • Effective code reviews - 要在專案之間檢視程式碼時,可以省下切換的時間

不過有幾個問題是:

  • 程式碼日益龐大,目錄結構就會愈複雜。因此在建立初期,最後可以做好規劃,並保有彈性
  • 建置測試的時間可能會愈花愈長。所以在開發工具與環境設定時,盡量能區分專案各自的範圍,只針對影響的範圍進行測試

目前對於Mono-Repo的管理功能最完整的工具是Lerna,在一些著名的大型專案,像是Babel、React、Storybook等都能看到它被大量使用,可見這也成為程式碼管理的主流方式之一。

Lerna 基本觀念

  • packages: 一個獨立專案或套件的單位,可互相依賴
  • workspaces: 集中與分類 package 的單位
  • versioning: 在版本控管上,主要分為兩種。
    • fixed mode,一旦執行發布,各package版本都會維持一致的更新。
    • independent mode,每個package各自維護自己的版本,並不干涉到其他的package
  • dependency: 在Mono-Repo的架構下,除了從npm安裝下來的依賴以外,內部的package也能透過指令的鏈結,成為依賴之一

Lerna 基本指令

  • npm i -g lerna - 全域安裝lerna
  • lerna init - 專案初始化,建立package.json、lerna.json和packages目錄
  • lerna create <pkg-name> - 建立package目錄,並產生package.json、README.md和預設目錄
  • lerna ls: 列出所有packages。
    • -a - 可列出所有設為private的packages
  • 安裝依賴
    • --dev - 將依賴安裝到devDendency
    • --peer - 將依賴安裝到peerDendency
    • lerna add <external-pkg-name> - 為所有packages安裝外部依賴
    • lerna add <internal-pkg-name> - 為所有packages(除依賴本身以外)安裝內部依賴
    • lerna add <internal-pkg-name> --scope=<target-pkg-name> - 在目標package安裝內部依賴
    • lerna add <internal-pkg-name> <worksapce-name>/<prefix->* - 將內部依賴安裝到所有適配的packages
  • lerna bootstrap - 安裝package所需要的依賴,如果內部package互相依賴則自動安裝。
    • --hoist - 將同版本的同套件,安裝到根目錄下的node_modules下
  • lerna clean - 移除所有package底下的node_modules
  • lerna run <command> - 在所有packages下執行npm指令
  • lerna exec 'command' - 在所有packages下執行shell指令
  • lerna version [major | minor | patch | premajor | preminor | prepatch | prerelease] - 跳版本
  • lerna publish - 發布packages
  • lerna changed - 找出跟上次發布有差異的packages

lerna.json設定

  • packages: workspaces的列表
  • version: 預設fixed mode。如果要設成independent mode,則改為 "independent"
  • npmClient: 套件管理的執行指令,預設為npm。如果專案是以yarn執行,則改為"yarn"
  • command
    • publish
      • ignoreChanges: 忽略變更的檔案列表,例如.gitignore、README.md等。避免不必要的更新
      • message: 執行lerna version提交commit時的內容文字
      • registry: npm server的位置。預計為 npmjs.org

Lerna 與 CICD workflow

以執行測試job為例,在CI上希望只針對有修改的packages執行測試。相關的lerna指令可以這樣寫 -

# 列出所有從release_tag之後有修改過的packages
lerna ls --all --since <release_tag>

# 在這些packages安裝依賴,並把相同套件安裝在根目錄的node_modules
lerna bootstrap --hoist --since <release_tag> 

# 在這些packages執行測試的npm指令
lerna run test --since <release_tag> --parallel

Lerna and Yarn Workspaces

在一開始嘗試以lerna管理mono repo時,發現到有些麻煩的地方,像是安裝全域依賴時,會希望依賴可以直接安裝在根目錄的node_modules下,讓所有的packages都能以link方式引用依賴。在lerna中你可能要這樣做以下三件事情 -

lerna add <pkg-name>
lerna bootstrap --hoist
lerna clean

過不久後我改用了yarn的workspaces,以上面的安裝依賴為例,我只要輸入 yarn add -W <pkg-name>,就能達到那3個lerna指令才能做到的事,相當的簡潔快速。

不過聽完這個議程之後,我發現這兩個工具是可以相輔相成的。因為在某些狀況下,使用Lerna還是比較理想的。

簡單、單一範圍的事情交給yarn workspaces;而複雜、大範圍的事情就給Lerna。個人覺得這樣的搭配是最適合管理mono repo,在google相關文章時會發現有些大大也是這樣建議使用。


尚未有邦友留言

立即登入留言