Fonal:

A JavaScript közösségben a mérnökök több százezer kódrészletet osztanak meg egymással, hogy elkerüljük az alapvető komponensek, könyvtárak vagy keretrendszerek saját újraírását. Minden egyes kóddarab viszont függhet más kóddaraboktól, és ezeket a függőségeket csomagkezelők kezelik. A legnépszerűbb JavaScript csomagkezelő az npm kliens, amely hozzáférést biztosít az npm regiszterben található több mint 300 000 csomaghoz. Több mint 5 millió mérnök használja az npm registry-t, amelyet havonta akár 5 milliárdszor is letöltenek.

A Facebooknál évekig sikeresen használtuk az npm klienst, de ahogy nőtt a kódbázisunk mérete és a mérnökök száma, úgy ütköztünk problémákba a konzisztenciával, a biztonsággal és a teljesítménnyel kapcsolatban. Miután minden egyes felmerülő problémát megpróbáltunk megoldani, nekiláttunk egy új megoldás létrehozásának, amely segít megbízhatóbban kezelni a függőségeket. Ennek a munkának a terméke a Yarn – egy gyors, megbízható és biztonságos alternatív npm kliens.

Örömmel jelentjük be a Yarn nyílt forráskódú kiadását, amely az Exponent, a Google és a Tilde együttműködésével jött létre. A Yarn segítségével a mérnökök továbbra is hozzáférhetnek az npm regiszterhez, de gyorsabban telepíthetik a csomagokat, és következetesen kezelhetik a függőségeket gépenként vagy biztonságos offline környezetben. A Yarn lehetővé teszi a mérnökök számára, hogy gyorsabban és magabiztosabban mozogjanak a megosztott kód használata során, így arra koncentrálhatnak, ami számít – új termékek és funkciók létrehozására.

A JavaScript csomagkezelés fejlődése a Facebooknál

A csomagkezelők előtti időkben a JavaScript mérnökök számára általános volt, hogy közvetlenül a projektjeikben tárolt vagy egy CDN által kiszolgált kis számú függőségre támaszkodtak. Az első nagy JavaScript csomagkezelő, az npm nem sokkal a Node.js bevezetése után készült el, és gyorsan a világ egyik legnépszerűbb csomagkezelőjévé vált. Több ezer új nyílt forráskódú projekt jött létre, és a mérnökök több kódot osztottak meg, mint valaha.

A Facebooknál számos projektünk, például a React, függ az npm-regiszterben található kódtól. A belső skálázás során azonban problémák merültek fel a függőségek különböző gépekre és felhasználókra történő telepítésekor a konzisztenciával, a függőségek behúzásához szükséges idővel, és biztonsági aggályaink voltak azzal, ahogyan az npm kliens automatikusan végrehajtja a függőségek némelyikéből származó kódot. Megpróbáltunk megoldásokat építeni ezekre a problémákra, de ezek gyakran maguk is új problémákat vetettek fel.

Kísérletek az npm-kliens skálázására

Az előírt legjobb gyakorlatokat követve kezdetben csak a package.json bejelölését végeztük el, és megkértük a mérnököket, hogy kézzel futtassák a npm install. Ez a mérnökök számára elég jól működött, de a folyamatos integrációs környezetünkben, amelyet biztonsági és megbízhatósági okokból homokdobozba kell zárni és el kell választani az internettől, nem működött.

A következő megoldás, amelyet megvalósítottunk, az volt, hogy az összes node_modules-t be kellett csekkolnunk a tárolóba. Ez ugyan működött, de néhány egyszerű műveletet eléggé megnehezített. Például a babel egy minor verziójának frissítése egy 800 000 soros commitot generált, amit nehéz volt landolni, és lint szabályokat váltott ki érvénytelen utf8 bájtsorozatok, windowsos sorvégek, nem png-zúzott képek és egyebek miatt. A node_modules módosításainak egyesítése gyakran egy egész napot vett igénybe a mérnököknek. A forrásellenőrző csapatunk arra is rámutatott, hogy a bevizsgált node_modules mappánk hatalmas mennyiségű metaadatot tartalmazott. A React Native package.json jelenleg mindössze 68 függőséget listáz, de a npm install futtatása után a node_modules könyvtár 121 358 fájlt tartalmaz.

Egy utolsó kísérletet tettünk az npm kliens skálázására, hogy a Facebook mérnökeinek számához és a telepítendő kód mennyiségéhez mérten működjön. Úgy döntöttünk, hogy a teljes node_modules mappát összezippeljük, és feltöltjük egy belső CDN-be, hogy mind a mérnökök, mind a folyamatos integrációs rendszereink következetesen le tudják tölteni és kinyerni a fájlokat. Ez lehetővé tette számunkra, hogy több százezer fájlt eltávolítsunk a forráskontrolból, de a mérnököknek így nemcsak az új kód lehívásához, hanem az építéshez is internet-hozzáférésre volt szükségük.

Az npm shrinkwrap funkciójával kapcsolatos problémákat is meg kellett oldanunk, amelyet a függőségi verziók lezárására használtunk. A shrinkwrap fájlok alapértelmezés szerint nem generálódnak, és kiesnek a szinkronból, ha a mérnökök elfelejtik generálni őket, ezért írtunk egy eszközt annak ellenőrzésére, hogy a shrinkwrap fájl tartalma megegyezik-e a node_modules-ban lévővel. Ezek az állományok azonban hatalmas JSON-tömbök, rendezetlen kulcsokkal, így a bennük végzett változtatások hatalmas, nehezen átnézhető commitokat generálnának. Hogy ezt enyhítsük, egy további szkriptet kellett hozzáadnunk az összes bejegyzés rendezéséhez.

Végezetül, egyetlen függőség frissítése az npm segítségével sok független függőséget is frissít a szemantikus verziókezelési szabályok alapján. Ez minden egyes módosítást sokkal nagyobb méretűvé tesz a vártnál, és az olyan dolgok, mint a node_modules commitolása vagy a CDN-be való feltöltés, a mérnökök számára nem tették ideálisabbá a folyamatot.

Új kliens építése

Ahelyett, hogy tovább építettük volna az infrastruktúrát az npm kliens köré, úgy döntöttünk, hogy megpróbáljuk holisztikusabban szemlélni a problémát. Mi lenne, ha ehelyett megpróbálnánk egy új klienst építeni, amely az általunk tapasztalt alapvető problémákat kezeli? Sebastian McKenzie a londoni irodánkban elkezdte ezt az ötletet meghekkelni, és hamar lelkesedni kezdtünk a benne rejlő lehetőségekért.

Amíg ezen dolgoztunk, beszélgetni kezdtünk mérnökökkel az egész iparágban, és azt tapasztaltuk, hogy hasonló problémákkal szembesültek, és sokszor ugyanazokkal a megoldásokkal próbálkoztak, gyakran egyetlen probléma megoldására összpontosítva egyszerre. Nyilvánvalóvá vált, hogy a közösség előtt álló problémák összességén együttműködve olyan megoldást tudnánk kidolgozni, amely mindenki számára működőképes. Az Exponent, a Google és a Tilde mérnökeinek segítségével kiépítettük a Yarn klienst, és teszteltük és validáltuk a teljesítményét minden fontosabb JS-keretrendszeren, valamint a Facebookon kívüli további felhasználási esetekben. Ma örömmel osztjuk meg a közösséggel.”

A Yarn bemutatása

A Yarn egy új csomagkezelő, amely felváltja az npm kliens vagy más csomagkezelők meglévő munkafolyamatát, miközben kompatibilis marad az npm registryvel. Ugyanazokkal a funkciókkal rendelkezik, mint a meglévő munkafolyamatok, miközben gyorsabban, biztonságosabban és megbízhatóbban működik.

Minden csomagkezelő elsődleges funkciója, hogy valamilyen csomagot – egy adott célt szolgáló kóddarabot – telepítsen egy globális registryből a mérnök helyi környezetébe. Az egyes csomagok függhetnek vagy nem függhetnek más csomagoktól. Egy tipikus projekt függőségi fájában több tíz, száz vagy akár több ezer csomag is lehet.

Ezek a függőségek a szemantikus verziókezelés (semver) alapján kerülnek verziószámozásra és telepítésre. A semver egy olyan verziószámozási sémát határoz meg, amely minden egyes új verzióban tükrözi a változtatások típusait, vagyis azt, hogy a változtatás megszakít-e egy API-t, új funkciót ad-e hozzá, vagy hibát javít. A semver azonban arra támaszkodik, hogy a csomagfejlesztők ne kövessenek el hibákat – a törő változások vagy új hibák utat találhatnak a telepített függőségekbe, ha a függőségek nincsenek lezárva.

Architektúra

A Node ökoszisztémában a függőségek egy node_modules könyvtárba kerülnek a projektben. Ez a fájlszerkezet azonban eltérhet a tényleges függőségi fától, mivel a duplikált függőségek összevonásra kerülnek. Az npm kliens nem determinisztikusan telepíti a függőségeket a node_modules könyvtárba. Ez azt jelenti, hogy a függőségek telepítésének sorrendje alapján a node_modules könyvtár szerkezete személyenként eltérő lehet. Ezek a különbségek “az én gépemen működik” hibákat okozhatnak, amelyek felkutatása hosszú időbe telik.

A Yarn a lockfile-ok és egy determinisztikus és megbízható telepítési algoritmus használatával oldja meg ezeket a verziókezeléssel és a nem-determinizmussal kapcsolatos problémákat. Ezek a lockfile-ok a telepített függőségeket egy adott verzióhoz kötik, és biztosítják, hogy minden telepítés pontosan ugyanazt a node_modules fájlstruktúrát eredményezi minden gépen. Az írott lockfile tömör formátumot használ, rendezett kulcsokkal, hogy a változások minimálisak és a felülvizsgálat egyszerű legyen.

A telepítési folyamat három lépésre oszlik:

  1. Felbontás: A Yarn elkezdi a függőségek feloldását azzal, hogy kéréseket intéz a registryhez és rekurzívan megnézi az egyes függőségeket.
  2. Letöltés: Ezután a Yarn megnézi egy globális cache könyvtárban, hogy a szükséges csomagot már letöltötte-e már. Ha nem, akkor a Yarn lekérdezi a csomag tarballját, és elhelyezi a globális gyorsítótárban, hogy offline is dolgozhasson, és ne kelljen többször letöltenie a függőségeket. A függőségek a teljes offline telepítéshez tarballként is elhelyezhetők a forráskezelésben.
  3. Linkelés: Végül a Yarn mindent összekapcsol azáltal, hogy a globális gyorsítótárból a helyi node_modules könyvtárba másolja az összes szükséges fájlt.

Azáltal, hogy ezeket a lépéseket tisztán lebontja és determinisztikus eredményekkel rendelkezik, a Yarn képes párhuzamosítani a műveleteket, ami maximalizálja az erőforrás-kihasználást és gyorsabbá teszi a telepítési folyamatot. Néhány Facebook-projektnél a Yarn nagyságrenddel csökkentette a telepítési folyamatot, több percről mindössze másodpercekre. A Yarn egy mutexet is használ annak biztosítására, hogy több futó CLI-példány ne ütközzön és ne szennyezze egymást.

A teljes folyamat során a Yarn szigorú garanciákat ír elő a csomagok telepítése körül. Ön irányíthatja, hogy mely életciklus-szkriptek mely csomagok esetében kerüljenek végrehajtásra. A csomagok ellenőrző összegeit is tárolja a lockfile, hogy minden egyes alkalommal ugyanazt a csomagot kapja.

Tulajdonságok

A Yarn amellett, hogy sokkal gyorsabbá és megbízhatóbbá teszi a telepítést, további funkciókkal is rendelkezik a függőségkezelési munkafolyamat további egyszerűsítése érdekében.

  • Kompatibilitás mind az npm, mind a bower munkafolyamatokkal, és támogatja a nyilvántartások keverését.
  • A telepített modulok licencének korlátozására való képesség és a licencinformációk kiadásának eszköze.
  • Stabil, nyilvános JS API-t tesz közzé, a naplózást absztrahálva a build eszközökön keresztül történő fogyasztáshoz.
  • Olvasható, minimális, szép CLI-kimenet.

Yarn a termelésben

A Facebooknál már használjuk a Yarnt a termelésben, és nagyon jól működik nálunk. Számos JavaScript-projektünk függőség- és csomagkezelését hajtja végre. Minden egyes migrációval lehetővé tettük a mérnökök számára az offline építkezést, és segítettünk felgyorsítani a munkafolyamatokat. You can see how install times for Yarn and npm compare on React Native under different conditions, which you can find here.

Getting started

The easiest way to get started is to run:

npm install -g yarn

yarn

The yarn CLI replaces npm in your development workflow, either with a matching command or a new, similar command:

  • npm installyarn

    With no arguments, the yarn command will read your package.json, fetch packages from the npm registry, and populate your node_modules folder. It is equivalent to running npm install.

  • npm install --save <name>yarn add <name>

    We removed the “invisible dependency” behavior of npm install <name> and split the command. Running yarn add <name> is equivalent to running npm install --save <name>.

Future

Many of us came together to build Yarn to solve common problems, and we knew that we wanted Yarn to be a true community project that everyone can use. Yarn is now available on GitHub and we’re ready for the Node community to do what it does best: Use Yarn, share ideas, write documentation, support each other, and help build a great community to care for it. We believe that Yarn is already off to a great start, and it can be even better with your help.