1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        抖音前端團(tuán)隊(duì)的自動(dòng)發(fā)包方案是如何做到的?

        共 5339字,需瀏覽 11分鐘

         ·

        2021-11-17 02:54

        作者簡(jiǎn)介: zoomdong,目前在抖音前端技術(shù)團(tuán)隊(duì)參與 Monorepo 相關(guān)的工程化基建工作,熱愛(ài)編程,熱愛(ài)開(kāi)源,是?pnpm、Ant Design等多個(gè)知名開(kāi)源項(xiàng)目的貢獻(xiàn)者。

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

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

        在這篇文章中,將會(huì)介紹 changesets 工具是如何來(lái)完成 Monorepo 倉(cāng)庫(kù)中項(xiàng)目包版本的管理、一些基本的命令使用以及原理、同時(shí)還會(huì)介紹一些缺陷以及目前可以優(yōu)化的一些點(diǎn)。

        Lerna 發(fā)包方案缺陷

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

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

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

        Changesets 工作流介紹

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

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

        Changesets 工作流會(huì)將開(kāi)發(fā)者分為兩類角色,一類是項(xiàng)目的維護(hù)者,還有一類為項(xiàng)目的開(kāi)發(fā)者,兩者的職責(zé)可以通過(guò)如下流程圖很簡(jiǎn)潔的表示出來(lái):

        根據(jù)上圖, changesets 的工作流程是這樣:開(kāi)發(fā)者在 Monorepo 項(xiàng)目下進(jìn)行開(kāi)發(fā),開(kāi)發(fā)完成后,給對(duì)應(yīng)的子項(xiàng)目添加一個(gè) changesets 文件。項(xiàng)目的維護(hù)者后面會(huì)通過(guò) changesets 來(lái)消耗掉這些文件并自動(dòng)修改掉對(duì)應(yīng)包的版本以及生成 CHANGELOG 文件,最后將對(duì)應(yīng)的包發(fā)布出去。

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

        子命令及工作原理

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

        init

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

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

        add

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

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

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

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

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

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

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

        如果有信息相同,只是 CHANGELOG 描述不同的 changeset 文件,在消耗這些文件的時(shí)候是會(huì)被合并處理的,即對(duì)應(yīng)包的 version 并不會(huì)被升級(jí)多次。

        version

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

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

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

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

        其它命令

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

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

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

        changesets 目前缺陷

        筆者在前面其實(shí)有提到過(guò)目前團(tuán)隊(duì)開(kāi)發(fā) Monorepo 工具時(shí),并沒(méi)有直接接入 changesets 這套方案,而是通過(guò)直接 fork 該倉(cāng)庫(kù)進(jìn)行修改,主要在于這套方案目前在一些使用場(chǎng)景下確實(shí)存在許多問(wèn)題。

        changeset 文件名隨機(jī)

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

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

        命令均不支持項(xiàng)目篩選

        例如 add 命令無(wú)法指定特定的包,而只能通過(guò)前面 getPackages() 方法得到所有的子項(xiàng)目名來(lái)進(jìn)行選擇,如果一個(gè)項(xiàng)目下存在好幾十個(gè)子項(xiàng)目的話,找具體的項(xiàng)目就是一件很費(fèi)成本的事情。

        不過(guò) add 命令至少會(huì)通過(guò) git diff 來(lái)篩出修改的子包名稱,這樣在一定程度上減少了用戶去找項(xiàng)目的成本,但是 version 命令因?yàn)闆](méi)有提供對(duì)應(yīng)的篩選功能,導(dǎo)致在一些場(chǎng)景下,用戶只想消耗特定的 changeset 文件去更新特定包是無(wú)法完成的。

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

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

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

        項(xiàng)目缺少維護(hù)

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

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

        不過(guò)好消息是最近作者已經(jīng)開(kāi)始活躍起來(lái),并回復(fù)了大量的 issue ,期待能在不久之后重新將整個(gè)項(xiàng)目運(yùn)作起來(lái)。

        總結(jié)

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

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

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

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


        瀏覽 133
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            国产成人aa精品 | 欧美人妖乱伦 | 三个男人添到我高潮 | 精品久久免费看 | 中国久久久久 | 北条麻妃黄色视频 | 操操网继续操用力豆花 | 操逼免费大片 | 永久国产精品免费A片分环 | 日本一级婬一A一A |