Articles

Stěhování z více repozitářů na lerna-js mono-repo

V mitter.io, máme pár veřejný-čelí npm balíčky, které musíme publikovat, a nedávno jsme se přestěhovali do mono-repo struktura řízena z Lerny mají samostatné repozitáře pro každou z nich. Dnes bych se rád podělil o naše zkušenosti s touto migrací a naším nastavením s novou strukturou monorepo. Všechny naše balíčky jsou buď SDK cíle nebo jsou závislostí pro naše SDK cíle:

  • @mitter-io/core – základní funkce mitter.io SDK
  • @mitter-io/models – typescript modely (třídy, typové aliasy, rozhraní atd.) pro Sdk
  • @mitter-io/web – web SDK
  • @mitter-io/react-native – Reagovat Native SDK
  • @mitter-io/node – uzel.JS SDK (používá se pro uzel.js api)
  • @mitter-io/react-scl – standardní knihovna komponent pro ReactJS aplikací

Všechny naše balíčky jsou psány na psacím Stroji a typings jsou spojeny s balíčky sám, a nebudeme distribuovat samostatné psaní balíčky (ty, které obvykle vidět začínající @types/). Používáme kumulativní svazky těchto balíčků ve formátech modulů UMD i ES5. Kromě toho používáme TypeDoc ke generování naší dokumentace, která je poté zveřejněna na veřejném kbelíku v AWS S3.

před použitím Lerny jsme měli samostatný repozitář pro každý z našich balíčků a fungovalo to dobře, zatímco jsme měli pouze webovou sadu SDK. Jak jsme postupoval a měl více vývojářů pracuje na SDK, jsme začali čelit několika problémům, s naší instalace:

  1. Vzhledem k tomu, že většina z SDK logika spočívá v @mitter-io/core, téměř každá změna, která nastala v core balíček a všechny ostatní balíčky musely být aktualizovány, aby odkazovaly na novou verzi. Takže, i kdyby tam byla chyba, která byla stanovena pro, řekněme Reagovat Native, změna by jít v core, ale aktualizace potřebné k nyní odrážet ve všech ostatních cílů, tj. webnodereact-native. Bylo zcela běžné, že vývojář minul cíl.
  2. téměř každá změna SDK by vedla ke změnám alespoň ve 3 z 5 balíčků.
  3. viděli jsme obrovskou výhodu v zachování stejné verze napříč balíčky (usnadňuje vývojářům odhadnout, jaká bude nejnovější verze target), ale ruční sledování bylo stále těžkopádné.
  4. npm link (nebo yarn link pokud dáváte přednost) měl svou vlastní sadu otázek s tím, že všechny závislosti byly spojeny, pak odpojit, použít ten správný z npm a zpět k místní odkaz pro rozvoj.
  5. bylo zcela běžné spouštět skripty napříč balíčky (např., publikovat dokumenty typescript), a my jsme používali křehké lešení symlinků a bash skriptů, abychom to zvládli.

v té době jsme narazili na Lernu a zdálo se, že se perfektně hodí pro naše požadavky.

rozhodli jsme se jít nejjednodušší cestou, která existuje, a pokusit se co nejvíce použít výchozí hodnoty. Z toho, co jsme zažili, migrace do Lerny byla hračka. Začněte vytvořením nového repo Lerna:

mkdir my-new-monorepo && cd my-new-monorepo
git init .
lerna init

odpovězte na několik jednoduchých otázek (kde jsme se vždy uchýlili k výchozímu nastavení) a máte vše nastaveno. Pohybující se naše staré balíčky z jejich repo operace do nového (což doufali jsme, tak jsme myslel, že to bude masivní bolest), bylo mnohem snazší, než se očekávalo:

lerna import ~/projects/my-single-repo-package-1 --flatten

POZNÁMKA: --flatten může nebo nemusí být nutné, ale čelili jsme bez problémů.

Co je úžasné, o Lerna je, že to přináší ve všech git zavazuje spolu s ním (můžete ztratit něco o historii s tím --flatten), takové, že pro nové repo, historie vypadá to, že vývoj byl vždy děje v tomto monorepo. To je naprosto nezbytné, protože budete muset git blame někoho pro chybu, kterou jste objevili po přesunu na monorepo.

Aktuální nastavení

S Lerna, jsme nyní spravovat jediné úložiště pro všechny naše balíčky, s adresářovou strukturou, která vypadá jako tohle:

packages/
core/
models/
node/
react-native/
web/
lerna.json
package.json

publikovat změněné balíky, jsme teď prostě musí:

lerna boostrap
lerna publish

nemusíte dělat lerna bootstrap každý-čas; pouze pokud je to poprvé, kdy jste mimo kontrolu repo. To, co dělá, je jednoduše nainstalovat všechny závislosti každého z balíčků v rámci tohoto repo.

současně jsme se také rozhodli trochu zefektivnit náš proces a přidali jsme všechny úlohy balení v rámci samotného životního cyklu npm. Všimněte si, že to nemá nic společného s Lernou; to je něco, co by mělo být v ideálním případě v každém balíčku npm bez ohledu na strukturu repo. Pro každý z balíčků, následující skripty jsou přítomny v jednotlivých pacakge.json:

"scripts": {
...
"prepare": "yarn run build",
"prepublishOnly": "./../../ci-scripts/publish-tsdocs.sh"
...
}

Toto sestavení balíčku s typescript compiler, balíčky s kumulativní a vytváří dokumenty s typedoc:

"scripts": {
...
"build": "tsc --module commonjs && rollup -c rollup.config.ts && typedoc --out docs --target es6 --theme minimal --mode file src"
...
}

S jedním repo struktura také umožňuje udržovat společné skripty v jediném místě tak, aby se změny projevily napříč všemi balíčky (a také bychom měli stavět skript do samostatného skriptu, vzhledem k tomu, že se nyní stala docela složitá bash příkaz).

vývojový tok

vývojový tok kromě verzí se nemění. Vývojář vytvoří problém na GitLabu (nebo je mu přidělen), vytvoří novou větev pro problém a poté po kontrole kódu sloučí změny do master. Životní cyklus vydání nyní sleduje extrémně strukturovaný proces:

  1. Když milníkem je dokončena a plánujeme udělat novou verzi, jeden z vývojářů (v důvěře, že zvláštní zpráva) vytvoří novou verzi spuštěním lerna version.
  2. Lerna poskytuje velmi užitečné a snadné použití řádku pro přijít na další verzi
(master) mitter-js-sdk ツ lerna version --force-publish
lerna notice cli v3.8.1
lerna info current version 0.6.2
lerna info Looking for changed packages since v0.6.2
? Select a new version (currently 0.6.2) (Use arrow keys)
❯ Patch (0.6.3)
Minor (0.7.0)
Major (1.0.0)
Prepatch (0.6.3-alpha.0)
Preminor (0.7.0-alpha.0)
Premajor (1.0.0-alpha.0)
Custom Prerelease
Custom Version

Jakmile nová verze je vybrán, Lerna změny verzí balíčků, vytvoří tag ve vzdálené repo, a tlačí na změny v našem GitLab stupně. Kromě toho nejsou vývojáři povinni dělat nic jiného. Náš CI je nastaven tak, aby vytvářel všechny značky, které mají název podobný sémantickému verzovanému číslu.

na VĚDOMÍ, Jsme se spustit lerna version--force-publish protože chceme, aby všechny balíčky mají stejnou linii verze. Takže někdy budeme mít balíčky, které se neliší mezi různými verzemi. V závislosti na vašich preferencích, můžete se rozhodnout, že to neuděláte.

nastavení CI

používáme integrovaný CI GitLab pro vytváření, testování a publikování ve všech našich projektech (JS a Java). Pro nové JS monorepo, máme dvě fáze:

  1. Vybudovat
  2. Zveřejnit

build fáze je velmi jednoduchý a vede na následující dva skripty:

lerna bootstrap
lerna run build

Tato fáze se spouští na každý commit v podstatě ověření příčetnosti balíčku. Publikování fáze na druhé straně, běží následující:

git checkout master
lerna bootstrap
git reset --hard
lerna publish from-package --yes

zjistili Jsme, co jsme museli udělat, git checkout mastergit reset --hard protože GitLab klony (nebo stáhne, v závislosti na konfiguraci) repo, a pak kontroluje se zavazují, že má být postaven. Tím se nastaví pracovní adresář v režimu „oddělené hlavy“, tj. ref HEAD nikam neukazuje. Lerna používá HEAD chcete-li zjistit aktuální verzi balíčku a chyby v samostatně stojící hlavy státu.

Musíme také spustit lerna publish from-package na rozdíl od lerna publish, jako vykonávající jednoduché lerna publish by Lerna si stěžují, že současná verze je již zveřejněna, jako byla aktualizována metadata, kdy developer běžel lerna version lokálně. from-package argument říká, Lerna, aby zveřejnila všechny verze, které nejsou v současné době v npm pro daný balíček. To také pomáhá, pokud publikování z nějakého důvodu selhalo a znovu se pokoušíte o potrubí.

publikovat fáze je nakonfigurován tak, aby spustit pouze na kategorie, které odpovídají následující regex kredit:

^v(0|\d*)\.(0|\d*)\.(0|\d*)(-(0|\d*|\d**)(\.(0|\d*|\d**))*)?(\++(\.+)*)?$

To je trochu fantazie, a pro většinu týmů a pro většinu účelů, jednoduše ^v*$ by měl fungovat. 🙂

POZNÁMKA: i když jsme to zatím neudělali, protože jsme malý tým, dalo by se také označit všechny kategorie v návaznosti na výše uvedené regex jako chráněné v GitLab omezit, kdo může publikovat balíčků npm.

můžete se podívat na naše monorepo na https://github.com/mitterio/js-sdk (To se odráží naše vnitřní GitLab repo).

Rychlé poznámky

Při spuštění společné skripty (jako my pro publikování strojopis docs), to je docela užitečné znát podrobnosti o balíčku spuštění skriptu. To platí pro skripty v npm životního cyklu, stejně jako skripty, jeden by mohl spustit pomocí lerna run nebo lerna exec. Pro daný balíček v npm, npm dělá celý package.json k dispozici skriptu pomocí proměnných prostředí. Tak, pro daný balíček s následující package.json:

{
"name": "@mitter-io/core",
"version": "0.6.28",
"repository": {
"type": "git"
}
}

následující proměnné budou k dispozici při každém spuštění cyklu skript:

npm_package_name=@mitter-io/core
npm_package_version=0.6.28
npm_package_repository_type=git

Vtípky/Problémy

pár věcí, které stále pracujeme na s novým nastavením (některé z nich jsou problémy, zatímco někteří asi jsme prostě nevím to lepší):

  • Nejsem si jistý, jestli to je možné, ale chtěli bychom být schopni mít společné životního cyklu skripty pro všechny naše balíčky. Deklarovat je v kořenovém adresáři package.json nefunguje.
  • je velmi obtížné otestovat nastavení Lerny úplně, aniž byste něco publikovali npm. Nejste si jisti, zda někde existuje --dry-run.
  • Lerna má způsob, jak udržet společný config-blok devDependencies tak, že všechny devDependencies jsou stejné verze pro každý z podbalíčky. To je docela skvělá funkce, ale trvalo by nám nějaký čas, než bychom vyřadili všechny běžné.
  • totéž by mohlo platit i pro jiné závislosti, stejně tak, když nebudeme chtít společný dependencies config blok, mít způsob, jak vyjádřit proměnné dostupné přes projekty by bylo hezké. Například, v našem Java/Kotlin monorepo, budeme používat gradle.properties obsahovat proměnné, jako je springBootVersionspringCoreVersion, atd., které pak používají jednotlivé gradle skripty.

Naše myšlenky na monorepos
To byl docela vášnivou debatu v poslední době s monorepos a zda jsme svědky obrovské číslo skákání na rozjetého vlaku znovu, docela připomíná dobu, kdy microservices bylo v módě.

struktura, kterou zde sledujeme, má více monorepo a toto není
naše první správa monorepo. Celá naše platforma a backend je monorepo, který obsahuje soukromý, nasazení kódu a více veřejné balíčky, které jsou zveřejněny na bintray. Máme také naše hlavní stránky běží s Spring backend, frontend dodáván s webpack podporuje hot reloading (webpack watch), atd. Nikdy jsme se nerozhodli jít s jediným mono-repo napříč organizací, protože nástroje tam prostě nebyly.

to, Že většina našeho kódu v jazyce Java v jednom repo funguje skvěle, protože gradle poskytuje všechny nástroje potřebné pro Java monorepo a lerna a npm životního cyklu poskytuje nástroje pro JS SDK monorepo. Jednoduše řečeno, monorepos jsou skvělé, jakmile zjistíte pokrytí změn, které jdou ve vašem repo. Pro naše Java backend, jsme viděli více Paní přes projekty pro jeden rys, který se přiklání nás přejít na monorepo pouze pro tento konkrétní projekt, s všechny naše ostatní kód stále v samostatné repo operace. A jakmile jsme viděli, že podobný vzor se objeví i pro naše JS SDK, přestěhovali jsme se do Lerny.

uvědomte si, že jsme malý tým asi 9 inženýrů; takže to, co funguje pro nás, nemusí fungovat pro týmy různých velikostí. To, co bychom většinou chtěli zdůraznit, je, že přijetí jakéhokoli řešení nemusí být binární, přičemž buď to uděláme tak, jak je předepsáno, nebo to neuděláme vůbec.

některé motivace, které jsme viděli pro monorepo, se na nás určitě vztahovaly a mnoho z nich ne. Například, my prostě nemůžeme ušetřit čas vybudovat nástroje, pokud celé naší codebase byla přesunuta do jediného repo — bez ohledu na přínos, jsme může nebo nemusí dojít. Takže debata opravdu není o tom mít „jediné repo“ – samo o sobě to není nic jiného než nová adresářová struktura. Jejich předpisem je zmírnit určité problémy a jako u každé „stříbrné kulky“ existují námitky.

debata je o běžných problémech, kterým čelí v softwarovém průmyslu, a o tom, jaká řešení byla běžně přijata;“ běžné “ je klíčové slovo. Oblast, kde se odchýlíte od „společné“ aplikace, je místem, kde můžete inovovat, provádět změny a trochu stavět.