2021年管理Monorepo代碼庫(kù)的11種出色工具
如今,許多工具可以在20個(gè)不同的文件夾中運(yùn)行“npm install”和“npm run build”。但是,并不是所有的工具都能促進(jìn)正確的monorepo。
促進(jìn)一個(gè)正確的單體開(kāi)發(fā)意味著要解決一些挑戰(zhàn),比如為分離的模塊運(yùn)行測(cè)試和構(gòu)建過(guò)程,能夠從項(xiàng)目中獨(dú)立發(fā)布模塊,以及管理變更對(duì)項(xiàng)目中每個(gè)受影響的依賴模塊的部分影響。
挑戰(zhàn)的清單還在繼續(xù),甚至包括“瑣碎”的事情,比如你如何管理issues和PRs,這可能會(huì)隨著你的開(kāi)發(fā)規(guī)模而變得困難。
請(qǐng)注意,一個(gè)monorepo不是一個(gè)整體的應(yīng)用程序(!) ——它不是一次性構(gòu)建或部署的,它是一組單獨(dú)開(kāi)發(fā)的應(yīng)用程序。
什么是 monorepo?
國(guó)慶期間10月5日尤大公開(kāi)了vue3.0已完成的源碼,也是采用了monorepo管理模式,看來(lái)monorepo確實(shí)有其獨(dú)到的優(yōu)勢(shì)。
monorepo是一種將多個(gè)package放在一個(gè)repo中的代碼管理模式,摒棄了傳統(tǒng)的多個(gè)package多個(gè)repo的模式。
目前 Babel, React, Angular, Ember, Meteor, Jest等許多開(kāi)源項(xiàng)目都使用該種模式來(lái)管理代碼。
解決的問(wèn)題
- 多個(gè)repo難以管理,編輯器需要打開(kāi)多個(gè)項(xiàng)目;
- 某個(gè)模塊升級(jí),依賴改模塊的其他模塊需要手動(dòng)升級(jí),容易疏漏;
- 公用的npm包重復(fù)安裝,占據(jù)大量硬盤(pán)容量,比如打包工具webpack會(huì)在每個(gè)項(xiàng)目中安裝一次;
- 對(duì)新人友好,一句命令即可完成所有模塊的依賴安裝,且整個(gè)項(xiàng)目模塊不用到各個(gè)倉(cāng)庫(kù)去找;
帶來(lái)的問(wèn)題
- 所有package代碼集中在一個(gè)項(xiàng)目,單個(gè)項(xiàng)目體積較大;
- 所有package代碼對(duì)所有人可見(jiàn),無(wú)法做權(quán)限管理;
在這篇綜述中,我收集了一些世界上最好的工具來(lái)構(gòu)建一個(gè)“monorepo”,你可以在一個(gè)項(xiàng)目里面構(gòu)建多個(gè)模塊,并且有不錯(cuò)的開(kāi)發(fā)者體驗(yàn),可以擴(kuò)展。
這個(gè)列表并沒(méi)有進(jìn)行排名,旨在根據(jù)每個(gè)工具的優(yōu)點(diǎn)來(lái)概述其優(yōu)勢(shì)。希望能幫助你節(jié)省時(shí)間,找到合適的工具。
歡迎在下方評(píng)論,分享自己的心得。
1. Yarn Workspaces
Yarn Workspaces 的目標(biāo)是簡(jiǎn)化與monorepos的工作,以更明確的方式解決 yarn link 的一個(gè)主要用例。你的依賴關(guān)系可以鏈接在一起,這意味著你的工作空間可以相互依賴,同時(shí)總是使用最新的代碼。這也是比 yarn link更好的機(jī)制,因?yàn)樗挥绊懩愕墓ぷ骺臻g樹(shù)而不是你的整個(gè)系統(tǒng)。
Workspaces有助于解決一些問(wèn)題,使其成為一個(gè)很好的單兵裝備。
- 它設(shè)置了一個(gè)單一的 node_modules,不需要在項(xiàng)目中的不同包中重復(fù)或克隆依賴關(guān)系。
- 你的所有項(xiàng)目依賴都將被安裝在一起,從而給Yarn更大的空間來(lái)更好地優(yōu)化它們。
- Yarn將使用一個(gè)單一的鎖文件,而不是為每個(gè)項(xiàng)目使用不同的鎖文件,這意味著更少的沖突和更容易的審查。
- 它允許你改變你的一個(gè)軟件包的代碼,并讓使用它的其他軟件包立即看到這些變化。對(duì)一個(gè)包的源代碼的任何修改都會(huì)立即應(yīng)用到其他包中。
因此,Yarn Workspaces是一個(gè)非常強(qiáng)大的組合,可以和列表中的幾乎所有工具,特別是Bit、Nx和Lerna等工具一起使用,作為你的monorepo管理抽象的下層。
不過(guò),你也可以直接用workspaces發(fā)布。當(dāng)一個(gè)工作空間被打包到一個(gè)存檔中時(shí),它會(huì)動(dòng)態(tài)地將任何 workspace: 依賴關(guān)系替換為一個(gè)包的版本,因此您可以將結(jié)果包發(fā)布到遠(yuǎn)程注冊(cè)表,而無(wú)需運(yùn)行中間步驟——消費(fèi)者將能夠像使用任何其他包一樣使用發(fā)布的工作空間。太酷了!
2. Bit
Bit是用于構(gòu)建模塊化項(xiàng)目的下一代工具。這是一種新的、令人興奮的單倉(cāng)庫(kù)方法,在這種方法中,由同一個(gè)項(xiàng)目(同一個(gè)Bit工作空間)管理的模塊實(shí)際上分布在不同的范圍內(nèi),而不考慮倉(cāng)庫(kù)。
Bit讓你以完全解耦的方式拆分模塊的開(kāi)發(fā),享受簡(jiǎn)單的、整體的開(kāi)發(fā)體驗(yàn)來(lái)協(xié)調(diào)一切。
使用bit,你可以在你的項(xiàng)目中解耦組件,這樣每個(gè)組件都是獨(dú)立開(kāi)發(fā)、構(gòu)建、測(cè)試和發(fā)布的。每個(gè)組件都是使用特殊的環(huán)境進(jìn)行開(kāi)發(fā)和構(gòu)建的,這些環(huán)境是可擴(kuò)展和可重用的,這樣你就可以快速定制和再次使用它們。
Bit的工作空間管理著項(xiàng)目中所有組件之間的關(guān)系。當(dāng)你對(duì)任何組件進(jìn)行更改時(shí),Bit會(huì)單獨(dú)構(gòu)建和測(cè)試它,并將更改傳播到依賴關(guān)系圖中。
組件可以作為獨(dú)立的包,批量發(fā)布到NPM和/或bit.dev平臺(tái),用于協(xié)作、消費(fèi)和文檔。
Bit的UI可以幫助你查看你的monorepo的開(kāi)發(fā)情況。當(dāng)你編寫(xiě)代碼時(shí),每個(gè)組件都會(huì)被記錄、測(cè)試、構(gòu)建等,你可以通過(guò)實(shí)時(shí)反饋和熱重載直觀地看到正在發(fā)生的事情。
Bit提供了解耦的開(kāi)發(fā)環(huán)境--可重用和可定制的模塊,這些模塊將獨(dú)立組件整個(gè)生命周期所需的不同服務(wù)配置和“捆綁”在一起,如編譯、捆綁、測(cè)試、磨合、文檔等。
Bit的工作空間以簡(jiǎn)單而全面的方式解耦組件開(kāi)發(fā)
掌握組件圖——Bit定義、管理并幫助你利用項(xiàng)目中所有組件之間的關(guān)系。
圖形驅(qū)動(dòng)的構(gòu)建——當(dāng)您對(duì)某個(gè)組件進(jìn)行更改時(shí),Bit會(huì)自動(dòng)檢測(cè)依賴于它的其他組件,并“知道”只構(gòu)建依賴組件的受影響的圖形。
“圖形驅(qū)動(dòng)的構(gòu)建”也意味著,萬(wàn)一一個(gè)組件被標(biāo)記了新的發(fā)布版本(在被導(dǎo)出到Bit的云端之前),Bit不僅會(huì)在每個(gè)受影響的組件上運(yùn)行構(gòu)建,而且會(huì)確保給它們標(biāo)記一個(gè)新的發(fā)布版本。
隔離的測(cè)試和構(gòu)建——每個(gè)組件都是在項(xiàng)目外部隔離地構(gòu)建和測(cè)試的,因此您可以確切地看到更改的影響。
組件構(gòu)建管道——您可以在可重用的管道中構(gòu)建作業(yè),該管道可應(yīng)用于項(xiàng)目或所有項(xiàng)目中的所有組件。
批量發(fā)布——在Bit monorepo中開(kāi)發(fā)的每個(gè)組件都可以作為一個(gè)獨(dú)立的包發(fā)布。Bit去掉了配置每個(gè)組件的“package.json”和其他設(shè)置文件的所有開(kāi)銷。你要做的就是運(yùn)行'bit tag',這樣Bit就會(huì)自動(dòng)給所有修改過(guò)的組件打上版本補(bǔ)丁(支持semver規(guī)則),然后批量發(fā)布修改。
可重復(fù)使用的文檔模板——每個(gè)組件都使用可重復(fù)使用和可定制的模板進(jìn)行文檔化,Bit為您自動(dòng)完成大部分工作。用MDX工作?也許還可以添加一些可視化的例子?沒(méi)問(wèn)題。
獨(dú)立渲染的組合——每個(gè)組件都是完全獨(dú)立渲染的,完全在項(xiàng)目之外渲染,渲染的視覺(jué)效果(在編寫(xiě)代碼時(shí)熱重新加載)成為每個(gè)組件文檔的一部分。
3. NX
NX是一套先進(jìn)的可擴(kuò)展的開(kāi)發(fā)工具,適用于monorepos,非常強(qiáng)調(diào)現(xiàn)代全棧Web技術(shù)。
空NX monorepo
NX的目標(biāo)是通過(guò)CLI(帶編輯器插件)提供整體的開(kāi)發(fā)體驗(yàn),并提供可控代碼共享和一致代碼生成的功能。它還提供了增量構(gòu)建,因此它不會(huì)在你的每一次提交中重建和重新測(cè)試所有內(nèi)容,從而加快構(gòu)建時(shí)間。
有了Nx,你可以使用你喜歡的框架,集成你可能已經(jīng)在使用的現(xiàn)代工具。例如,NX可以讓你使用與Cypress、Jest、Typescript、Prettier和其他工具的開(kāi)箱即用的集成。
NX團(tuán)隊(duì)還提供了NX云,通過(guò)云中的智能計(jì)算記憶和更快的構(gòu)建來(lái)幫助使用NX的團(tuán)隊(duì)更快地交付。
4. Rush
Rush是由微軟+開(kāi)源的一個(gè)強(qiáng)大的monorepo基礎(chǔ)設(shè)施,它的目的是幫助你在一個(gè)倉(cāng)庫(kù)中構(gòu)建和發(fā)布許多包。
登陸頁(yè)面和一些組件,兩個(gè)項(xiàng)目,一個(gè)倉(cāng)庫(kù)
rush的一些主要功能包括一個(gè)單一的NPM安裝(也可以和Yarn和pnpm一起使用),所以你可以將所有項(xiàng)目的所有依賴關(guān)系安裝到一個(gè)共同的文件夾中,使用隔離的符號(hào)鏈接為每個(gè)項(xiàng)目重新構(gòu)建一個(gè)準(zhǔn)確的“node_modules”文件夾。
這也有助于確保沒(méi)有幻影依賴,所以你不會(huì)意外地導(dǎo)入一個(gè)在package.json中缺失的庫(kù),也不會(huì)在node_modules中發(fā)現(xiàn)10份lib的依賴重復(fù)。
Rush交互式CLI不錯(cuò)
自動(dòng)本地鏈接意味著你所有的項(xiàng)目都會(huì)自動(dòng)地相互建立符號(hào)鏈接,當(dāng)你做了一個(gè)改變,你可以看到下游的效果,而不需要發(fā)布任何東西,也沒(méi)有任何 npm link 的麻煩。
Rush獨(dú)特的安裝策略為你的所有項(xiàng)目生成一個(gè)快速安裝的單一收縮/鎖定文件。Rush會(huì)檢測(cè)你的依賴關(guān)系圖,并以正確的順序構(gòu)建你的項(xiàng)目,所以如果兩個(gè)包之間沒(méi)有直接的依賴關(guān)系,Rush會(huì)將它們作為單獨(dú)的進(jìn)程并行構(gòu)建。
如果你只打算使用你的repo中的幾個(gè)項(xiàng)目,Rush提供了子集和增量構(gòu)建,所以 rush rebuild --to
當(dāng)你想發(fā)布的時(shí)候,Rush支持批量發(fā)布,所以它會(huì)檢測(cè)哪些包有變化,自動(dòng)跳轉(zhuǎn)所有相關(guān)的版本號(hào),并在每個(gè)文件夾中運(yùn)行 npm publish 。
Rush還有助于實(shí)施和執(zhí)行發(fā)展政策。例如,當(dāng)創(chuàng)建PR時(shí),你可以要求開(kāi)發(fā)人員提供受影響項(xiàng)目的主要/次要/補(bǔ)丁日志條目,這些條目隨后將在發(fā)布時(shí)匯總到一個(gè)變更日志文件中。它還可以幫助你執(zhí)行諸如發(fā)布前的審查、特定的依賴版本等東西。
5. Lerna
Lerna(以多頭野獸Hydra的家命名)是一個(gè)“用于管理帶有多個(gè)包的JavaScript項(xiàng)目的工具”。
Lerna的創(chuàng)建是為了解決Babel的多包問(wèn)題,以優(yōu)化使用git和npm管理多包倉(cāng)庫(kù)的工作流程,它本質(zhì)上是一種工具和腳本,可以有效地管理和發(fā)布許多獨(dú)立版本的包在一個(gè)Git倉(cāng)庫(kù)中。
- my-lerna-repo/
- package.json
- packages/
- package-1/
- package.json
- package-2/
- package.json
Lerna 的兩個(gè)主要命令是 lerna bootstrap 和 lerna publish。bootstrap 會(huì)將 repo 中的依賴關(guān)系連接在一起,publish 會(huì)幫助發(fā)布任何更新的包。
您可以使用以下兩種模式之一來(lái)管理項(xiàng)目:固定(Fixed)或獨(dú)立(Independent)。
固定模式的Lerna項(xiàng)目是以單一的版本行來(lái)操作的,版本是保存在你的項(xiàng)目根目錄下的 lerna.json 文件中的 version 鍵。當(dāng)您運(yùn)行 lerna publish 時(shí),如果一個(gè)模塊在上次發(fā)布后被更新,它將被更新到您發(fā)布的新版本。這是Babel目前使用的模式。
一個(gè)帶有Yarn Workspaces的Lerna例子
獨(dú)立模式Lerna項(xiàng)目允許維護(hù)者相互獨(dú)立地增加包的版本,每次發(fā)布時(shí),你都會(huì)收到一個(gè)提示,提示你每一個(gè)已經(jīng)改變的軟件包,以指定它是一個(gè)補(bǔ)丁,小的,大的或自定義的變化。獨(dú)立模式可以讓你更具體地更新每個(gè)包的版本,對(duì)于一組包來(lái)說(shuō)是有意義的。
“lerna.json”文件是一個(gè)匹配包含 package.json 的目錄的globs列表,這也是lerna識(shí)別“葉子”包的方式(相對(duì)于管理整個(gè)repo的開(kāi)發(fā)依賴和腳本)。例子:
- {
- "version": "1.1.3",
- "npmClient": "npm",
- "command": {
- "publish": {
- "ignoreChanges": ["ignored-file", "*.md"],
- "message": "chore(release): publish",
- "registry": "https://npm.pkg.github.com"
- },
- "bootstrap": {
- "ignore": "component-*",
- "npmClientArgs": ["--no-package-lock"]
- }
- },
- "packages": ["packages/*"]
- }
即使你不打算發(fā)布到NPM,Lerna仍然可以在monorepo中幫助管理版本管理和常見(jiàn)的開(kāi)發(fā)任務(wù)。
6. Bazel構(gòu)建系統(tǒng) (Google)
谷歌推出了Bazel build system,它是一個(gè)類似于Make、Maven和Gradle的開(kāi)源構(gòu)建和測(cè)試工具,使用的是人類可讀的高級(jí)構(gòu)建語(yǔ)言。Bazel支持多種語(yǔ)言的項(xiàng)目,并為多種平臺(tái)構(gòu)建輸出。它支持大型單一倉(cāng)庫(kù)中的大型代碼庫(kù)或跨多個(gè)倉(cāng)庫(kù)的大型代碼庫(kù)和大量用戶。
Uber開(kāi)發(fā)者使用Bazel來(lái)構(gòu)建他們的Go monorepo。Uber用Go編寫(xiě)了大部分的后端服務(wù)和庫(kù),在2018年,這些服務(wù)和庫(kù)都被歸納到一個(gè)大型的Go monorepo中,現(xiàn)在有超過(guò)10萬(wàn)個(gè)文件。Bazel讓這個(gè)項(xiàng)目得以擴(kuò)展,縮短了構(gòu)建時(shí)間,并支持其發(fā)展。
這是一個(gè)不錯(cuò)的小型開(kāi)源項(xiàng)目,以Bazel作為演示:thundergolfer/example-bazel-monorepo
Bazel被設(shè)計(jì)成大規(guī)模工作,并支持跨分布式基礎(chǔ)設(shè)施的增量密封構(gòu)建,這是大型代碼庫(kù)所必需的。有了Bazel的遠(yuǎn)程緩存,構(gòu)建服務(wù)器還可以共享它們的構(gòu)建工件。Bazel緩存所有以前完成的工作,并跟蹤對(duì)文件內(nèi)容和構(gòu)建命令的更改。只有在包或包的依賴關(guān)系發(fā)生更改時(shí),才構(gòu)建和測(cè)試包。
Bazel可以在Linux、macOS和Windows上運(yùn)行。Bazel可以從同一個(gè)項(xiàng)目為多個(gè)平臺(tái)構(gòu)建二進(jìn)制文件和可部署的包,包括桌面、服務(wù)器和移動(dòng)設(shè)備。支持許多語(yǔ)言,你可以擴(kuò)展Bazel來(lái)支持任何其他語(yǔ)言或框架。
7. Buck構(gòu)建系統(tǒng) (Facebook)
Buck是一個(gè)鼓勵(lì)創(chuàng)建由代碼和資源組成的小型可重用模塊的構(gòu)建系統(tǒng),支持不同平臺(tái)上的各種語(yǔ)言。
它是由Facebook開(kāi)發(fā)和使用的,作為FB單體的官方構(gòu)建系統(tǒng),由于被Uber開(kāi)發(fā)者等團(tuán)隊(duì)使用,大大縮短了構(gòu)建時(shí)間,因此名聲大噪。而AirbnbEng的團(tuán)隊(duì)則將構(gòu)建速度提高了50%,將應(yīng)用程序縮小了30%。
Uber憑借buck獲得了更好的構(gòu)建結(jié)果
Buck被設(shè)計(jì)用來(lái)構(gòu)建一個(gè)monorepo,而對(duì)monorepo設(shè)計(jì)的支持激發(fā)了Buck對(duì)cell和項(xiàng)目的支持。
Facebook的經(jīng)驗(yàn)是,將所有的依賴關(guān)系維護(hù)在同一個(gè)版本庫(kù)中,可以更容易地確保所有開(kāi)發(fā)者擁有正確的代碼版本,并簡(jiǎn)化了進(jìn)行原子提交的過(guò)程。
Buck常用于Android和iOS開(kāi)發(fā)。
8. Pants構(gòu)建系統(tǒng)(Twitter)
2014年,Twitter推出了名為Pants的monorepo構(gòu)建系統(tǒng)。今天,在v2版本上,Pants的目標(biāo)是成為一個(gè)快速、可擴(kuò)展的構(gòu)建系統(tǒng),以適應(yīng)不斷增長(zhǎng)的代碼庫(kù)。目前,它的重點(diǎn)是Python,很快就會(huì)支持其他語(yǔ)言。
Pants使用細(xì)粒度的工作流,并將每個(gè)工作單元與副作用隔離,因此可以利用所有可用的內(nèi)核。Pant的一些最佳特性包括明確的依賴建模、細(xì)粒度的無(wú)效化、共享結(jié)果緩存、并發(fā)執(zhí)行、遠(yuǎn)程執(zhí)行,以及通過(guò)插件API的可擴(kuò)展性和可定制性。
Pants引擎是用Rust寫(xiě)的,為的是性能。構(gòu)建規(guī)則是用類型化的Python 3寫(xiě)的,為了熟悉和簡(jiǎn)單。該引擎的設(shè)計(jì)使得細(xì)粒度的無(wú)效化、并發(fā)性、密封性、緩存和遠(yuǎn)程執(zhí)行自然發(fā)生,而無(wú)需規(guī)則作者的干預(yù)。
9. Please構(gòu)建系統(tǒng)
Please是一個(gè)跨語(yǔ)言的構(gòu)建系統(tǒng),強(qiáng)調(diào)高性能、可移植性、可擴(kuò)展性和正確性。
請(qǐng)確保構(gòu)建步驟是在自己的密封環(huán)境中執(zhí)行的,只能訪問(wèn)被賦予權(quán)限的文件和env變量。增量構(gòu)建意味著它只構(gòu)建它需要的東西,它還提供了任務(wù)并行性,以及分布式緩存,以實(shí)現(xiàn)大規(guī)模的可靠和高性能的構(gòu)建系統(tǒng)。
Please的目標(biāo)也是專注于開(kāi)發(fā)體驗(yàn),所以你可以享受一個(gè)常用的CLI,并為使用自動(dòng)完成的常見(jiàn)任務(wù)定義別名。
Please用Go編寫(xiě),Please提供所有這些用戶體驗(yàn),沒(méi)有運(yùn)行時(shí)依賴。并且,沒(méi)有需要處理太多配置的單個(gè)大工作區(qū)文件。
10. Oao
Oao并不是列表中最成熟、最豐富、最容易使用的工具,但它還是很有趣。它是一個(gè)基于Yarn的,有意見(jiàn)的monorepo管理工具,p提供monorepo功能,如安裝所有的依賴關(guān)系,添加/刪除/升級(jí)子包的依賴關(guān)系,驗(yàn)證版本號(hào),確定更新的子包,一次性發(fā)布所有的東西,更新變更日志等。
Oao可以讓你在所有子包上運(yùn)行命令或 package.json 腳本,串行或并行,可選擇遵循反向依賴樹(shù)。而且,它支持yarn workspaces,從整體上優(yōu)化了monorepo依賴樹(shù),簡(jiǎn)化了bootstrap以及依賴的添加/升級(jí)/刪除。
支持非單包發(fā)布:從oao’s的發(fā)布前檢查、標(biāo)簽、版本選擇、變更日志更新等方面受益,也可以在你的單包、非單包中使用。需要注意的是,Oao使用的是同步版本方案,所以在根級(jí)的 package.json 中配置了一個(gè)主版本,而子包也將與該版本同步。你可以在這里嘗試一下。
11. Bolt
Boltpkg旨在成為一個(gè)“超級(jí)功能JavaScript項(xiàng)目管理工具”。
Bolt在Yarn的基礎(chǔ)上實(shí)現(xiàn)了workspaces的概念。Bolt CLI在很大程度上是Yarn CLI的替代品,你可以在任何Yarn項(xiàng)目中使用它。
我們知道,workspaces是嵌套在一個(gè)更大的項(xiàng)目/repo中的,每個(gè)workspaces都可以有自己的依賴關(guān)系,有自己的代碼和腳本。workspaces也可以歸入子目錄進(jìn)行組織。
使用Bolt,你可以一次安裝所有這些包的依賴關(guān)系(而且你可以做得非常非常快)。而且,當(dāng)你從一個(gè)工作區(qū)指定一個(gè)依賴關(guān)系到另一個(gè)工作區(qū)時(shí),它將被鏈接到源代碼。這樣,當(dāng)你去測(cè)試你的代碼時(shí),你所有的變化都會(huì)被一起測(cè)試。
來(lái)源:https://blog.bitsrc.io/11-tools-to-build-a-monorepo-in-2021-7ce904821cc2
作者:Jonathan Saring
本文轉(zhuǎn)載自微信公眾號(hào)「前端全棧開(kāi)發(fā)者」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系前端全棧開(kāi)發(fā)者公眾號(hào)。