Amennyiben már gondolkodsz azon, hogy a projektjeidet a folyamatos integráció (CI) módszerével fejleszd, akkor itt az idő, hogy elkezd. Ez a blogbejegyzés egy minimál projekten keresztül mutatja be, hogy hogyan teheted ezt meg könnyedén a GitHub Actions segítségével.

A projekt

A projekt egy nagyon minimális NodeJS alkalmazás, melynek a kódja igazából a Jest teszt keretrendszer dokumentációjából származik. Az ott leírt lépések segítségével hoztam létre a fájlokat. A sum.js a működést biztosító kód, a sum.test.js pedig ezt a kódot teszteli. A package.json a függőségeken túl a projekt információit és a teszt futtatásának módját tartalmazza. Egy sort kellett csak módosítanom benne, de ez egyébként a Jest dokumentációjában is le van írva:

{
  ...
  "scripts": {
    "test": "jest"
  },
  ...
}

Erre azért van szükség, hogy az npm run test parancsra a Jest teszt futtató környezet induljon el. A package-lock.json az aktuálisan telepített kiegészítő csomagok verzió információit tartalmazza, de ez gondolom nem túl nagy meglepetés azoknak, akik NodeJS fejlesztéssel foglalkoznak. A jest.config.js a Jest segítségével generálódott. Számunkra érdekes pontjai a collectCoverage: true és coverageDirectory: “coverage”. Előbbi utasítja a Jest-et, hogy gyűjtse össze azt, hogy a teszt futtatása során milyen sorok voltak érintettek és ezeket tárolja el az utóbbi által mutatott könyvtárba.

A coverageReporters értéket alapértelmezetten hagytam, így minden lehetséges riport elkészül. Számunkra most egy érdekes kimenet lesz majd a html, ami a coverage/lcov-report könyvtárba készül el.

Említésre méltó még a clover.xml és az lcov.info fájlok. Mindkettő egy-egy coverage megjelenítő és böngésző program bemeneti állománya, melyek ma már kvázi szabványként szolgálnak a területen. Számos teszt futtató környezet tud egyik vagy másik formában kimenetet generálni, vagy mint esetünkben a Jest mindkettő formátumot ismeri. Természetesen a túloldalon, a megjelenítésnél is számos lehetőség közül választhatunk. Nem csak ez a két program az, ami segítségével böngészhetjük a teszt lefedettségi adatokat. A Jenkinsben például a Clover plugin segítségével 2007 óta jeleníthetjük meg ezeket az adatokat a CI felületén. A Codecov oldalán projektjeink tesztlefedettségét historikusan is követhetjük, de lehetőségünk nyílik arra is, hogy több mikroszervízből álló projektünk különálló kódjainak adatait együtt böngésszük. Természetesen nem kell ilyen messzire mennünk, hisz ma már lehetőségünk van arra is, hogy a fejlesztő környezetünkben is megjelenítsük ezeket az adatokat. A JetBrains termékei (PHPStorm, WebStorm stb.) beépítetten tudják ezt, de a Coverage Gutters pluginnal a VSCode, a Coverage Highlight pluginnal pedig akár a Vim is képessé tehető erre.

GitHub Actions

Térjünk most vissza ahhoz a részhez, ami miatt ez az egész bejegyzés született. A GitHub Actions-hoz. Mielőtt a fájl sorait elkezdenénk vizsgálni készíts egy repot a GitHub felületén, pusshold be a fenti fájlokat vagy a sajátjaidat, majd kattints az Actions fülre. Ekkor a GitHub felajánl egy lehetséges mintát. Ha NodeJS projekted van akkor valószínűleg a Node.js nevűt. Ha nem ez lenne az, akkor keresd ki a listából és választ azt. Ekkor a GitHub online editorában találod magadat, ahol már csak a Start Commit gombot kell megnyomnod. Ekkor létrejön a .github/workflows könyvtárban egy node.js.yml fájl, ami a szükséges információkat fogja tartalmazni. Ne felejts el a lokális reponál pullolni egyet, hogy a fejlesztői gépeden is ugyanazok a fájlok legyenek.

Amennyiben mindent jól csináltál, már el is indul a pipeline és láthatod ahogy dolgozik. Ne félj, olyan sok mindent nem tudsz elrontani. Sokkal egyszerűbb ez a gyakorlatban, mint ahogyan azt én itt le tudom írni.

Most nézzük meg mi van a fájlban.

Az első részében az Action neve szerepel és az, hogy milyen esemény hatására fusson az le.

name: Node.js CI

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

Utána jönnek a job-ok, melyből most csak egy van, melynek neve build.

  jobs:
    build:

Három jellemzője van ennek a job-nak.

    runs-on: ubuntu-latest

A runs-on mondja meg, hogy a legfrissebb ubuntut tartalmazó gépen fussanak le a lépések.

    strategy:
      matrix:
        node-version: [12.x, 14.x, 16.x]
        # See supported Node.js release schedule at https://nodejs.org/en/about/releases/

A strategy rész lehetővé teszi, hogy egy job több különböző variációban fusson le. A használt minta három különböző NodeJS verziót tartalmaz.

Végül jönnek a lépések, vagyis a steps, mely a szükséges lépéseket tartalmazza. Picit zavaró lehet elsőre ez a fajta leírás, de egy idő után megszokja a szemünk, hogy az egyes kötőjelek(-) mutatják meg az egyes step-eket. Minden step egy vagy akár több kulcs-érték párból áll és ezek mondják meg, hogy hogyan kell végrehajtani az adott lépést. Kell hozzá egy pici gyakorlat, hogy tudd értelmezni ezeket, de szerintem viszonylag hamar rá tud állni erre az ember szeme és ki tudja szúrni azokat a kulcsokat, amelyekkel könnyen érthető lesz számára az adott lépés. Az én kódomban hat lépést láthatsz, menjünk ezeken végig.

    - uses: actions/checkout@v2

Ez a lépés szinte mindig szükséges. Ez tölti le a git tárolóból a kódunkat. Bár ezt a lépést szinte mindig így használjuk, nézzük meg részletesebben azért. Egy konfigurációs kulcsunk van, ez a uses és egy értékünk ez az actions/checkout@v2. A uses kulcs mondja meg a futtatónak, hogy ez egy előre elkészített lépés lesz, az actions/checkout@v2 pedig azt, hogy pontosan melyik sablon lépésről van szó. Bármely lépésnél találunk egy uses kulcsszót, ott érdemes onnan kiindulni és megnézni, hogy pontosan milyen sablon lépésről van szó. Ha megnézzük ennek a lépésnek a forrását: https://github.com/actions/checkout látjuk annak részletes leírását, valamint különböző mintákat a felhasználására és konfigurálására. Ennél a lépésnél olyan sok mindent nem kell nekünk konfigurálni, de nézzük a következőt, az már izgalmasabb lesz ilyen szempontból:

    - name: Use Node.js ${{ matrix.node-version }}
      uses: actions/setup-node@v2
      with:
        node-version: ${{ matrix.node-version }}
        cache: 'npm'

Remélem már egyből kiszúrtad, hogy itt is van uses kulcs, tehát ez egy sablon lépés. A with kulcsszó segítségével adhatunk át további paramétereket, melyekre itt szükségünk is lesz. A actions/setup-node@v2 telepíti a rendszerünkre a NodeJS futtatókörnyezet megfelelő verzióját. Jelen esetben a mátrixnál megadott 12-es, 14-es vagy 16-os verziót. Mint fentebb írtam, ez a job háromszor fog lefutni, három különböző node verzióval és itt történik meg az adott futásnak megfelelő verzió telepítése.

    - run: npm ci

A következő lépésnél egy új kulcsunk van, a run. Az ez után megadott értéket fogja a rendszer futtatni. Valahogy úgy kell elképzelni, mintha kaptunk volna egy frissen telepített ubuntus gépet, amire először is letöltjük a kódunkat a git segítségével, vagyis magyarosan kicsekoutoljuk azt. Ezután telepítjük rá a node futtatásához szükséges csomagokat és végül a terminálba begépeljük az itt látható parancsot: npm ci. Aki nem ismerné, ez igazából egy npm install, csak fel van turbozva pár okossággal, ami a CI-hoz kell. Többek között pl. mindig frissen telepíti a csomagokat és nem nyúl a packages.json-hoz vagy bármelyik lock fájlhoz. További információk a parancs leírásában található.

    - run: npm run build --if-present

Ez a lépés futtatja a build parancsot, már ha létezik. Mivel kis projektünkben nem létezik, ez a lépés nem csinál semmit se, akár ki is törölhetnénk.

    - run: npm test

Ebben a lépésben fognak lefutni a tesztek, és fog elkészülni a lefedettségi riport. (Itt működik az npm run test is. Ez az npm működésének sajátossága, hogy elhagyható)

    - name: Deploy
      uses: peaceiris/actions-gh-pages@v3
      if: matrix.node-version == '16.x'
      with:
        github_token: ${{ secrets.GITHUB_TOKEN }}
        publish_dir: ./coverage/lcov-report

No ez már egy érdekesebb lépés, már csak azért is, mert ez az egyetlen lépés, amit én adtam hozzá a mintából generált fájlhoz. Remélem most már kiszúrod a uses kulcsszót és már keresel is rá az adott action-re, hogy megtudd mi is ez és hogyan lehet használni. Gondolom nem okozott nehézséget megtalálni az oldalát: https://github.com/peaceiris/actions-gh-pages

Ebből láthatod, hogy ez a sablon lépés a GitHub Pages segítségével publikál weboldalakat. Na nézzük milyen beállítások vannak még itt. Első az if kulcs, amely arra utasítja a végrehajtót, hogy ez a lépést csak a 16.x-es verzión futtassa le. Erre azért van szükség, mert egyrészt a különböző verziókból ugyan azok a riportok fognak kiesni, másrészt a különböző node verziók építése egyszerre zajlik, így a publikálás néha bizony elhal, hisz ugyanoda próbálnak meg írni ilyenkor a különböző jobok. Röviden tehát nem nyerünk vele semmit, ha minden verzióra futtatjuk a jobot, cserébe viszont néha sikertelen lesz a build. Ez utóbbi viszont egy olyan dolog amit mindenképpen kerülnünk kell. Ha a build eltörik, akkor akárhány futtatásra el kell törni és sikeres esetben is azt várjuk el, hogy mindig atomstabilan jelezze a jóságot, vagy ahogyan Szász Zoli mondaná: Legyen szuperzöld.

Fentebb már írtam, hogy a tesztlefedettség riport html formátuma a coverage/lcov-report könyvtárba készül el, ezért ez az a könyvtár amit publikálni szeretnénk, ezért szerepel ez a publish_dir kulcsnál. Az eredményt egyébként a https://tanarurkerem.github.io/node-action-test/ urlen meg is nézhetitek.

Remélem minél többen ki fogjátok próbálni a fentieket és elkezditek bevezetni a projektjeitekbe a folyamatos integrációt. Amennyiben segítségre van szükséged keress nyugodtan.

Image by Amanda McConnell from Pixabay