siirtyminen useista arkistoista Lerna-js mono-repoon
At mitter.io, meillä on pari julkista npm
pakettia, jotka meidän on julkaistava, ja siirryimme hiljattain Lernan hallinnoimaan mono-reporakenteeseen, josta jokaisella on erilliset arkistot. Tänään haluan jakaa kokemuksemme tästä muuttoliikkeestä ja asetelmastamme uuden monorepo-rakenteen kanssa. Kaikki pakettimme ovat joko SDK-kohteita tai riippuvaisia SDK – tavoitteistamme:
-
@mitter-io/core
– Mitterin ydintoiminnot.io SDKs -
@mitter-io/models
– konekirjoitusmallit (luokat, tyyppinimet, liitännät jne.) -
@mitter-io/react-native
– React Native SDK -
@mitter-io/node
– the node.JS SDK (käytetään solmua.js backends) -
@mitter-io/react-scl
– ReactJS-sovellusten vakiokomponenttikirjasto
@mitter-io/web
– web SDK
kaikki pakettimme on kirjoitettu Konekirjoituksella ja typingit on niputettu itse pakettien kanssa, emmekä jaa erillisiä konekirjoituspaketteja (niitä, jotka yleensä alkavat @types/
). Käytämme rollup niputtaa nämä paketit sekä UMD ja ES5 moduuli formaatteja. Tämän lisäksi käytämme Typedocia tuottaaksemme dokumentaatiomme, joka sitten julkaistaan julkisella ämpärillä AWS S3: ssa.
ennen Lernan käyttöä Meillä oli erillinen arkisto jokaiselle paketillemme ja se toimi hyvin, kun meillä oli vain web SDK. Kun edistyimme ja SDK: n parissa työskenteli lisää kehittäjiä, meillä alkoi olla muutamia ongelmia asennuksessamme:
- ottaen huomioon, että suurin osa SDK: n logiikasta on
@mitter-io/core
, lähes kaikki muutokset, jotka tapahtuivatcore
paketissa ja kaikki muut paketit jouduttiin päivittämään uuden version osoittamiseksi. Eli vaikka olisi vika, joka piti korjata, sano React Native, muutos menisicore
, mutta päivityksen piti nyt heijastua kaikkiin muihin kohteisiin, eliweb
node
jareact-native
. Oli melko tavallista, että rakennuttaja ei osunut kohteeseen. - lähes jokainen SDK: n muutos johtaisi muutoksiin vähintään kolmessa viidestä paketista.
- näimme valtavan hyödyn siitä, että sama versio säilyi paketeissa (kehittäjien on helpompi arvata, mikä Targetin uusin versio olisi), mutta manuaalisesti tämän seuraaminen kävi hankalaksi.
-
npm link
(taiyarn link
jos haluat) oli oma ongelmakokonaisuutensa, jossa varmistettiin, että kaikki riippuvuudet olivat yhteydessä toisiinsa, sitten ei voitu käyttää oikeaanpm
ja takaisin paikalliseen linkkiin kehitystä varten. - oli melko yleistä suorittaa skriptejä eri paketeissa (esim., julkaista typescript docs), ja käytimme hauras tukiranka symlinks ja bash skriptejä hallita samaa.
noihin aikoihin törmäsimme Lernaan ja se tuntui sopivan täydellisesti vaatimuksiimme.
päätimme seurata yksinkertaisinta polkua, joka on olemassa, yrittäen käyttää oletuksia niin paljon kuin mahdollista. Kokemamme perusteella muutto Lernaan oli helppoa. Aloita luomalla uusi Lerna repo:
mkdir my-new-monorepo && cd my-new-monorepo
git init .
lerna init
vastaa pariin yksinkertaiseen kysymykseen (jossa turvauduimme aina oletukseen) ja olet valmis. Vanhojen pakettien siirtäminen repoistaan uuteen (jota pelkäsimme, koska luulimme sen olevan valtava tuska) oli paljon odotettua helpompaa:
lerna import ~/projects/my-single-repo-package-1 --flatten
huomaa, että
--flatten
voi olla tai ei tarvita, mutta kohtasimme ongelmia ilman sitä.
hämmästyttävää Lernassa on se, että se tuo mukanaan kaikki git-toimitukset (saatat menettää jonkin verran historiaa --flatten
) siten, että Uuden repon kohdalla historia näyttää siltä, että kehitystä on tapahtunut aina tässä monorepossa. Tämä on aivan välttämätöntä, koska joudut git blame
jonkun vian takia, jonka löysit monorepoon muuton jälkeen.
nykyiset asetukset
Lernan avulla hallinnoimme nyt kaikille paketeille yhtä arkistoa, jonka hakemistorakenne näyttää tältä:
packages/
core/
models/
node/
react-native/
web/
lerna.json
package.json
muuttuneiden pakettien julkaisemiseksi on nyt yksinkertaisesti pakko:
lerna boostrap
lerna publish
sinun ei tarvitse tehdä lerna bootstrap
joka kerta; vain jos tämä on ensimmäinen kerta, kun olet tarkistamassa Repoa. Mitä se tekee on yksinkertaisesti asentaa kaikki riippuvuudet kunkin paketteja tämän repo.
samalla päätimme myös hieman virtaviivaistaa prosessiamme ja lisäsimme kaikki pakkaustehtävät npm
itse elinkaaren sisällä. Huomaa, että tällä ei ole mitään tekemistä Lernan kanssa.; tämä on jotain, jonka pitäisi mieluiten olla missä tahansa npm-paketissa reporakenteesta riippumatta. Kunkin paketin kohdalla seuraavat skriptit ovat yksittäisessä pacakge.json
:
"scripts": {
...
"prepare": "yarn run build",
"prepublishOnly": "./../../ci-scripts/publish-tsdocs.sh"
...
}
Tämä rakentaa paketin konekirjoituksella, niputtaa sen rullalla ja luo docs typedoc:
"scripts": {
...
"build": "tsc --module commonjs && rollup -c rollup.config.ts && typedoc --out docs --target es6 --theme minimal --mode file src"
...
}
, joilla on yksi reporakenne, mahdollistaa myös yhteisten skriptien pitämisen yhdessä paikassa siten, että muutokset koskevat kaikkia paketteja (meidän pitäisi myös siirtää build script erilliselle skriptille, koska siitä on nyt tullut melko monimutkainen bash-komento).
kehittäjävirta
kehittäjävirta julkaisuja lukuun ottamatta on ennallaan. Kehittäjä luo ongelman Gitlabiin (tai sille annetaan sellainen), luo uuden haaran ongelmalle ja yhdistää muutokset masteriksi koodin tarkastelun jälkeen. Julkaisun elinkaari seuraa nyt erittäin jäsenneltyä prosessia:
- kun virstanpylväs on valmis ja suunnittelemme uuden julkaisun tekemistä, yksi kehittäjistä (vastaa kyseisestä julkaisusta) luo uuden version ajamalla
lerna version
. - Lerna tarjoaa erittäin hyödyllisen ja helppokäyttöisen kehotuksen seuraavan version hahmottamiseen
(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
kun uusi versio on valittu, Lerna vaihtaa pakettien versiot, luo tunnisteen etärepoon ja työntää muutokset GitLab-instanssiimme. Tämän lisäksi kehittäjien ei tarvitse tehdä mitään muuta. Meidän CI on setup rakentaa kaikki tunnisteet, joilla on nimi samanlainen semanttinen versioitu numero.
huomaamme, että ajamme
lerna version
kanssa--force-publish
, koska haluamme, että kaikilla paketeilla on täsmälleen sama versiolinja. Joten joskus meillä on paketteja, jotka eivät eroa eri versioiden välillä. Riippuen mieltymyksestäsi, saatat päättää olla tekemättä sitä.
CI setup
käytämme gitlabin integroitua CI: tä rakentamiseen, testaamiseen ja julkaisemiseen kaikissa projekteissamme (JS ja Java). Uudessa JS monorepossa on kaksi vaihetta:
- Rakenna
- Julkaise
rakentamisvaihe on äärimmäisen yksinkertainen ja se suorittaa seuraavat kaksi komentosarjaa:
lerna bootstrap
lerna run build
Tämä vaihe toimii jokaisessa yksittäisessä toimikunnassa, jonka tarkoituksena on lähinnä validoida paketin järki. Julkaisuvaihe sen sijaan kulkee seuraavasti:
git checkout master
lerna bootstrap
git reset --hard
lerna publish from-package --yes
tajusimme, että jouduimme tekemään
git checkout master
jagit reset --hard
, koska GitLab kloonaa (tai hakee kokoonpanosta riippuen) repon, ja sitten tarkistaa, mikä sitoumus on tarkoitus rakentaa. Tämä asettaa työhakemiston ”detached HEAD” – tilaan, eli refHEAD
ei osoita mihinkään. Lerna käyttääHEAD
selvittääkseen paketin nykyisen version ja irtopäätilassa olevat virheet.
myös lerna publish from-package
vastakohtana lerna publish
, koska yksinkertaisen lerna publish
olisi Lerna valittanut, että nykyinen versio on jo julkaistu, koska metatiedot olivat päivitetty, kun Kehittäjä juoksi lerna version
paikallisesti. from-package
argumentti käskee Lernaa julkaisemaan kaikki versiot, joita ei tällä hetkellä ole npm: ssä tietylle paketille. Tämä auttaa myös, Jos julkaisu epäonnistui jostain syystä ja olet uudelleen putki.
julkaisuvaihe on määritetty toimimaan vain tageilla, jotka vastaavat seuraavaa regex-krediittiä:
^v(0|\d*)\.(0|\d*)\.(0|\d*)(-(0|\d*|\d**)(\.(0|\d*|\d**))*)?(\++(\.+)*)?$
Tämä on hieman hienostunutta, ja useimmille joukkueille ja useimmille tarkoituksille yksinkertaisesti ^v*$
pitäisi toimia. 🙂
huomaa, että vaikka emme ole vielä tehneet sitä, koska olemme pieni tiimi, voi myös merkitä yllä olevaa regexiä seuraavat tägit suojatuiksi Gitlabissa rajoittamaan sitä, kuka voi julkaista paketteja npm: ään.
voit tarkistaa monorepomme osoitteesta https://github.com/mitterio/js-sdk (tämä on peilattu sisäisestä GitLab repostamme).
Pikamuistiinpanot
ajettaessa yleisiä skriptejä (kuten kirjoituskonekirjoitusten julkaisemista varten) on varsin hyödyllistä tietää skriptiä ajavan paketin tiedot. Tämä koskee skriptejä npm: n elinkaaressa sekä skriptejä, joita voidaan suorittaa käyttämällä lerna run
tai lerna exec
. Kun kyseessä on annettu paketti npm, npm antaa koko package.json
ympäristömuuttujia käyttävän skriptin käyttöön. Niinpä tietylle paketille, jolla on seuraava package.json
:
{
"name": "@mitter-io/core",
"version": "0.6.28",
"repository": {
"type": "git"
}
}
seuraavat muuttujat ovat käytettävissä ajettaessa mitä tahansa elinkaarikomppaniaa:
npm_package_name=@mitter-io/core
npm_package_version=0.6.28
npm_package_repository_type=git
Quirks/Issues
pari asiaa, joita vielä työstämme uuden kokoonpanon kanssa (osa niistä on ongelmia, kun taas osaa emme luultavasti vain tiedä paremmin):
- ei ole varma, onko se mahdollista, mutta haluaisimme saada yhteiset elinkaarikäsikirjoitukset kaikkiin paketteihimme. Näiden julistaminen juuressa
package.json
ei toimi. - on äärimmäisen vaikeaa testata Lerna-setupia kokonaan ilman, että NPM: lle oikeasti julkaistaan jotain. Ei ole varma, onko
--dry-run
jossain. - Lernalla on tapana pitää yhteinen config-lohko
devDependencies
siten, että kaikkidevDependencies
ovat samaa versiota jokaiselle alapaketille. Tämä on melko viileä ominaisuus,mutta veisi meiltä jonkin aikaa karsia kaikki yhteiset. - sama voisi päteä myös muihin riippuvuuksiin, joten vaikka emme halua yhteistä
dependencies
config-lohkoa, olisi mukavaa, että eri projekteissa olisi mahdollisuus ilmaista muuttujia. Esimerkiksi Java/Kotlin monorepossamme käytetäängradle.properties
sisältämään muuttujia, kutenspringBootVersion
springCoreVersion
jne., joita sitten käytetään yksittäisten gradle skriptejä.
ajatuksemme monoreposista
viime aikoina on käyty aika kiivasta väittelyä monoreposin kanssa ja siitä, näemmekö taas valtavan määrän hyppivän kelkkaan, mikä muistuttaa aika paljon aikaa, jolloin mikropalvelut olivat kaikki raivona.
tässä seuraamallamme rakenteella on useita monorepoja, eikä tämä ole
ensimmäinen kertamme monorepoksen hallinnassa. Koko alustamme ja taustajärjestelmämme on monorepo, joka sisältää yksityisiä, käyttöönotettavia koodeja ja useita julkisia paketteja, jotka julkaistaan bintraylle. Meillä on myös pääsivusto käynnissä Jousitaustan kanssa, jossa frontend on nipussa webpackin kanssa, joka tukee kuumaa uudelleenlatausta (webpack watch
) jne. Emme koskaan päättäneet mennä Yhden mono-repo koko organisaation, koska työkalut yksinkertaisesti ei ollut siellä.
suurin osa Java-koodistamme yhdessä repossa toimii hyvin, koska gradle
tarjoaa kaikki Java monorepon ja lerna
ja NPM: n elinkaaren, joka tarjoaa työkalun JS SDK: n monorepolle. Niin, yksinkertaisesti sanottuna, monorepos ovat suuria, kun tunnistat kattavuus muutoksia, jotka menevät repo. Java-taustajärjestelmässämme näimme useita MRs: ää eri projekteissa yhden ominaisuuden vuoksi, mikä houkutteli meitä siirtymään monorepoon vain tässä nimenomaisessa projektissa, jossa kaikki muut koodimme ovat edelleen erillisissä repoissa. Ja kun näimme samanlaisen kuvion syntyvän myös JS SDKs: llemme, muutimme Lernaan.
huomaa, että olemme pieni noin 9 insinöörin tiimi, joten se mikä toimii meille, ei välttämättä toimi erikokoisissa tiimeissä. Haluamme lähinnä huomauttaa, että minkään ratkaisun ei tarvitse olla kaksijakoinen, jolloin joko teemme sen kuten on määrätty tai emme tee sitä lainkaan.
jotkut monorepolle näkemämme motiivit pätivät varmasti meihin ja monet eivät. Esimerkiksi, emme yksinkertaisesti voi säästää aikaa rakentaa työkaluja, jos meidän koko codebase siirrettiin yhden repo – riippumatta hyödystä saatamme tai ehkä ei koeta. Keskustelussa ei siis todellakaan ole kyse ”yksittäisestä reposta” — sinänsä kyse ei ole mistään muusta kuin uudesta hakemistorakenteesta. Niiden resepti on lievittää tiettyjä asioita ja kuten jokaisen ”Hopealuoti”, on olemassa varoituksia.
keskustelu käsittelee ohjelmistoalan yhteisiä kysymyksiä ja sitä, mitä ratkaisuja on yleisesti tehty; ”yhteinen” on avainsana. Alue, jossa poiketaan ”yhteisestä” sovelluksesta, on se, jossa saa innovoida, tehdä muutoksia ja rakentaa vähän.