<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Lerna 停止維護后,Monorepo發(fā)包新方案Changesets快了解一下!

          共 5499字,需瀏覽 11分鐘

           ·

          2021-10-30 11:28

          作者簡介: zoomdong,目前在抖音前端技術(shù)團隊參與 Monorepo 相關(guān)的工程化基建工作,熱愛編程,熱愛開源,是?pnpmAnt Design等多個知名開源項目的貢獻者。

          Changesets 是一個用于 Monorepo 項目下版本以及 Changelog 文件管理的工具。目前一些比較火的 Monorepo 倉庫都在使用該工具進行項目的發(fā)包例如 pnpm、mobx 等。github 倉庫為: https://github.com/atlassian/changesets,在 github 上有大約 2k 的 star。

          目前筆者組自研的 Monorepo 發(fā)包方案基于該方案進行二次開發(fā),替代 lerna 成為工程化團隊內(nèi)部統(tǒng)一的發(fā)包方案,并在其它團隊也取得了不錯的落地效果,其中包括之前關(guān)于 pnpm 的個人文章中提到的 Tiktok FE 團隊,另外也包括目前字節(jié)開源出來的 modern.js 倉庫:

          在這篇文章中,將會介紹 changesets 工具是如何來完成 Monorepo 倉庫中項目包版本的管理、一些基本的命令使用以及原理、同時還會介紹一些缺陷以及目前可以優(yōu)化的一些點。

          Lerna 發(fā)包方案缺陷

          在之前的文章中,有介紹過基于 lerna 發(fā)包方案的源碼解析。同時筆者組自研的 Monorepo 工具中,早期版本中也是采用了 lerna 這一套的發(fā)包方案,但隨著在用戶中的推廣以及使用,這套方案隨之帶來了不少問題:

          • ignoreChanges 不能做到文件的完全忽略,存在優(yōu)先級問題
          • lerna version 根據(jù) commit 以及 tag 更新出來的包版本不符合預期
          • 生成的 CHANGELOG 文件信息不完整
          • lifecycle scripts 經(jīng)常命中一些用戶自定義的 script(例如 publish 等)
          • CI 中自動化發(fā)包場景需要很高的定制成本
          • lerna 本身不支持 workspace 協(xié)議,導致基于 pnpm 開發(fā)的一些倉庫無法使用

          基于以上這些缺點,包括 lerna 本身的使用成本以及冗余的代碼設(shè)計,加上目前 lerna 本身停止維護,因此在調(diào)研之后,我們將自研 Monorepo 工具中發(fā)包方案逐步替換為了 changesets。

          Changesets 工作流介紹

          在前面我們講過了 changesets 的作用,changesets 主要關(guān)心 monorepo 項目下子項目版本的更新、changelog 文件生成、包的發(fā)布。一個 changeset 是個包含了在某個分支或者 commit 上改動信息的 md 文件,它會包含這樣一些信息:

          • 需要發(fā)布的包
          • 包版本的更新層級(遵循 semver 規(guī)范)
          • CHANGELOG 信息

          Changesets 工作流會將開發(fā)者分為兩類角色,一類是項目的維護者,還有一類為項目的開發(fā)者,兩者的職責可以通過如下流程圖很簡潔的表示出來:

          根據(jù)上圖, changesets 的工作流程是這樣:開發(fā)者在 Monorepo 項目下進行開發(fā),開發(fā)完成后,給對應的子項目添加一個 changesets 文件。項目的維護者后面會通過 changesets 來消耗掉這些文件并自動修改掉對應包的版本以及生成 CHANGELOG 文件,最后將對應的包發(fā)布出去。

          以上就是一個簡單的 changesets 工作流,當然這些工作流會對應到具體的 cli 命令以及 config 配置中去,下面我會基于此工作流介紹一些關(guān)于 changesets 最常用的幾個子命令以及使用原理。

          子命令及工作原理

          如果要使用 changesets,需要先安裝其 CLI 工具,通過 pnpm install @changeset/cli 安裝就行。安裝之后,就可以按照下面的一些命令開始使用了。

          init

          該命令為初始化命令,通過執(zhí)行 changeset init,可以在項目根目錄下生成一個 .changeset 目錄,里面會生成一個 changeset 的 config 文件,可以參考 pnpm 目前項目的根目錄:

          該命令原理相對簡單,執(zhí)行的時候通過 fs 將對應配置文件寫到目錄下就行,關(guān)于 config 中的具體配置描述可以參考官方文檔。init 初始化出來的為默認配置,一般不需要用戶去做過多的修改。

          add

          add 在 changesets 中算得上比較關(guān)鍵的命令之一了,它會根據(jù) monorepo 下的項目來生成一個 changeset 文件,里面會包含前面提到的 changeset 文件信息(更新包名稱、版本層級、CHANGELOG 信息)。

          還是以 pnpm 該項目作為例子,例如在 pnpm 倉庫下執(zhí)行 changeset add 會出現(xiàn)一系列 Prompt 問題:

          會讓我們選擇本次 changeset 需要發(fā)布的包,這些包名都是 Monorepo 項目下的子包,changesets 內(nèi)部通過 getPackages() 這一方法得到 Monorepo 項目下子項目信息,該方法的具體實驗可以參考 changesets 下面一個叫做 @manypkg/get-packages 的包。方法本質(zhì)上是通過讀 Monorepo 下所有子項目的 package.json 然后構(gòu)建出一個依賴圖出來,changesets 可以根據(jù)該結(jié)果得到需要進行發(fā)包流程的項目,可以說整個 changesets 項目本身都會基于底層這個方法來進行構(gòu)建,有點類似于一般 Monorepo 工具中的 graph 構(gòu)建。

          這里同時會通過封裝的 git diff 命令檢查出本次 commit 修改了的包名稱,不過即使是沒有修改的包,用戶其實也是可以進行選擇的,這里不同于其他 Monorepo 發(fā)包工具的區(qū)別在于更多的修改權(quán)限在用戶的手里。

          之后選擇了想要發(fā)布的包之后,后面會選擇到想要更新包的版本層級,例如這里我選擇了 patch 級別,按照 semver 的規(guī)范,這里選擇的包為 @pnpm-private,在填完 summay 之后,后面會生成一個文件名稱隨機的 changeset 文件,如圖所示:

          這里文件的名稱是通過一個叫做 human-id 的庫生成的,具體可以在 npm 上查看,但實際上這里用戶也是可以自行修改文件名稱的,這里并沒有太大的關(guān)系,也可以修改文件里面的 CHANGELOG 的信息。

          這個文件本質(zhì)上是做個信息的預存儲,在該文件被消耗之前,用于是可以自定義修改的。隨著不同開發(fā)者的迭代積累,changeset 文件是可以在一個周期之內(nèi)進行累積的。例如 pnpm 現(xiàn)在下面就積累了一些 changeset 文件:

          如果有信息相同,只是 CHANGELOG 描述不同的 changeset 文件,在消耗這些文件的時候是會被合并處理的,即對應包的 version 并不會被升級多次。

          version

          version 這個命令這里可以當作 bump version 來理解,這里本質(zhì)上做的工作是消耗 changeset 文件并且修改對應包版本以及依賴該包的包版本,同時會根據(jù)之前 changeset 文件里面的信息來生成對應的 CHANGELOG 信息。version 的源碼流程具體為:

          這一步的核心步驟主要在依賴于 changesets 本身項目下的兩個庫,分別為 @changesets/assemble-release-plan@changesets/apply-release-plan ,其中 assembleReleasePlan 主要是通過讀生成的 changesets 文件然后分析出需要更新的包版本以及其依賴關(guān)系,然后將讀出來的待更新結(jié)果給到 applyReleasePlan 中去,在 applyReleasePlan 中則會根據(jù)相應的信息修改掉包版本、消耗掉 changeset 文件、同時更新掉 CHANGELOG 文件(如果沒有就新生成一個)。

          例如現(xiàn)在在 pnpm 倉庫的根目錄下執(zhí)行一次 changeset version,那么就會根據(jù)上面的流程得到這樣的結(jié)果:

          對應的 changeset 文件被消耗,然后對應子項目的 CHANGELOG 以及版本發(fā)生變更,當然改完后不滿意用戶還可以手動對 changelog 進行修改。自動修改的 changelog 信息如下:

          其它命令

          changesets 還提供了一些其他的命令,這里我就不再一一對其介紹,這些命令其實相對比較好理解并且實現(xiàn)上沒有特別讓人難以理解的地方。例如用戶如果要發(fā)一個 prelease 的包版本(例如 beta、alpha 版本),那么就可以使用 changeset pre 命令,然后再結(jié)合 version 命令去進行版本的 bump。

          如果用戶想查看當前的 changesets 文件消耗狀態(tài),那么可以使用 changeset status 命令。

          發(fā)包的 changeset publish 本質(zhì)上就是對 npm publish 做了一次封裝,同時會檢查對應的 registry 上有沒有對應包的版本,如果已經(jīng)存在了,就不會再發(fā)包了,如果不存在會對對應的包版本執(zhí)行一次 npm publish

          changesets 目前缺陷

          筆者在前面其實有提到過目前團隊開發(fā) Monorepo 工具時,并沒有直接接入 changesets 這套方案,而是通過直接 fork 該倉庫進行修改,主要在于這套方案目前在一些使用場景下確實存在許多問題。

          changeset 文件名隨機

          在前面有提到 add 這一命令生成出來的 changeset 文件名稱是隨機的(通過 human-id 這個庫生成),那么在一個快速迭代的 Monorepo 開發(fā)場景下。例如筆者組 Monorepo 項目,每周會大概產(chǎn)生 20+ 的 changeset 文件,而這些文件名稱又是隨機的,非常不便于用戶去進行管理和辨別。

          因此筆者在 fork 該項目之后,通過修改了 @changesets/write 這一部分代碼,使得生成的 changeset 文件能夠按照分支名+用戶名+id 的形式顯示出來,便于不同的開發(fā)者對自己的 changeset 文件進行篩選。

          命令均不支持項目篩選

          例如 add 命令無法指定特定的包,而只能通過前面 getPackages() 方法得到所有的子項目名來進行選擇,如果一個項目下存在好幾十個子項目的話,找具體的項目就是一件很費成本的事情。

          不過 add 命令至少會通過 git diff 來篩出修改的子包名稱,這樣在一定程度上減少了用戶去找項目的成本,但是 version 命令因為沒有提供對應的篩選功能,導致在一些場景下,用戶只想消耗特定的 changeset 文件去更新特定包是無法完成的。

          因此筆者在 fork 該項目之后,通過其與 pnpm 的 filter 機制(參考文檔: https://pnpm.io/filtering)結(jié)合,使得整個工作流能夠被用戶進行自定義篩選。

          Prelease 包發(fā)布過程繁瑣

          使用 changesets 如果想發(fā)一些測試版本的包,需要反復執(zhí)行 changeset pre enterchangeset pre exit 以及 changeset version 等命令,整個流程上是很繁瑣的。實際上在自行維護的過程中,這些瑣碎的流程可以集合到一個命令中來完成的,并不用消費如此大的成本。

          項目缺少維護

          這一點其實也算是支撐筆者自己 fork 源碼重新搞一套的一個重要理由,目前該項目處于長期沒有 PR 合并的一個狀態(tài),近半年來合并的 pr 都是一些簡單的文檔修改而沒有實質(zhì)性的功能進展:

          同時 changesets 本身的文檔還是比較欠缺的,例如一些常見的 FAQ 文檔目前還是處于 TODO 的狀態(tài)。

          不過好消息是最近作者已經(jīng)開始活躍起來,并回復了大量的 issue ,期待能在不久之后重新將整個項目運作起來。

          總結(jié)

          目前的 changesets 方案整體而言在 Monorepo 項目下還是挺適用的,而且整體架構(gòu)上而言并沒有特別大的技術(shù)難點,主要難點在于 version bump 這一部分。

          筆者認為該方案最大的優(yōu)點在于提供了很大的自主權(quán)在用戶手中,在復雜的業(yè)務(wù)場景下能夠做出一些合適的調(diào)整,例如用戶可以自行修改 changeset 文件、changelog 文件、甚至是 bump version 后不滿意的版本。

          相比較于 lerna 提供的比較理想化的方案而言,changeset 本身是一套泛用性很強的方案,而且比較適合當下 Monorepo 工作流場景下的一些運作方式,雖然本身還存在著不少的缺點。

          期待作為目前不少 Monorepo 項目正在使用的發(fā)包方案,未來 changesets 能越來越流行~


          最后



          如果你覺得這篇內(nèi)容對你挺有啟發(fā),我想邀請你幫我三個小忙:

          1. 點個「在看」,讓更多的人也能看到這篇內(nèi)容(喜歡不點在看,都是耍流氓 -_-)

          2. 歡迎加我微信「?sherlocked_93?」拉你進技術(shù)群,長期交流學習...

          3. 關(guān)注公眾號「前端下午茶」,持續(xù)為你推送精選好文,也可以加我為好友,隨時聊騷。


          點個在看支持我吧,轉(zhuǎn)發(fā)就更好了


          瀏覽 297
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  中文天堂网视频在线 | 欧美黑人XXXX高潮交 | 五月色丁香 | 影音先锋无码一区二区 | 啊啊啊额在线视频 |