GitLab CI¶
Szerkesztés alatt!
Az oldal további része szerkesztés alatt áll, a tartalma minden további értesítés nélkül többször, gyakran, jelentősen megváltozhat!
CI/CD: Continuous Integration / Continuous Delivery / Continuous Deployment¶
DevOps¶
A DevOps a Development és Operations szavak összevonásából keletkezett. Egy olyan fejlesztési filozófiát takar, ami a "hagyományosabb" fejlesztési modelleket (amik a szoftver tervezésétől a release-ig tartanak, esetleg karbantartással kiegészítve) egy működ(tet)ési résszel egészíti ki, és ezt (ennek tapasztalatait) visszacsatolja a fejlesztésbe. A DevOps jól illeszkedik az agilis fejlesztéshez abban az elvben, hogy a szoftver soha nincs készen, mindig lesz mit javítani/fejleszteni rajta.
A DevOps-nak része a Continuous Integration / Continuous Delivery / Continuous Deployment (CI/CD), aminek az alapja, hogy a szoftver mindig "release" állapotban van. Vagyis (a CI rész szerint) úgy kell fejleszteni, hogy a szoftver fő verzióján eszközölt bármely módosítás után is a szoftver használható, kiadható állapotban legyen, és (a CD rész szerint) a "release" (lehetőleg automatikusan) meg is történjen, és ha lehet, akkor az új release be is legyen üzemelve. A CI/CD tehát magában foglalja a folyamatok nagyfokú automatizálását is.
Ehhez az automatizáláshoz nyújt segítséget a GitLab CI mechanizmus.
A GitLab CI¶
A GitLab CI alapvetően úgy működik, hogy meghatározhatunk, előírhatunk feladatokat, és ha a GitLab a repository-ban változást észlel (volt egy push, voltak commit-ok), akkor végrehajtja ezeket a feladatokat. Ehhez külön erőforrást kell rendelnünk, egy gépet ("runner"), ami képes elvégezni ezeket a feladatokat, de ennek a beállításával, felkonfigurálásával most nem foglalkozunk -- feltesszük, hogy adott, és a projektünkből elérhető, használható.
A GitLab CI Docker alapokon működik. Ez egy virtualizációs technika (kicsit komolyabb, mint a venv, de nem olyan teljes, mint egy VirtualBox vagy VMware). A lényeg, hogy meg kell adnunk egy "image"-et, egy előre meghatározott környezetet, amiben majd a parancsaink, szkriptjeink futnak. A runner tulajdonképpen ezt az image-et indítja el és futtatja benne a parancsainkat (a repository adott verziójának gyökérkönyvtárából).
A GitLab CI/CD pipeline használata¶
A GitLab CI használatához a projektünk gyökerében létre kell hozni egy .gitlab-ci.yml
nevű konfigurációs fájlt, a GitLab ez alapján fog dolgozni.
Ebben elég sok mindent be lehet konfigurálni, akit érdekel, az részletes leírást talál a https://docs.gitlab.com/ci/yaml oldalon.
Példa .gitlab-ci.yml
konfigurációs fájl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
|
A konfiguráció főbb elemei:
Stages¶
A stages:
pont határozza meg, hogy a teljes CI/CD folyamatnak milyen fázisai lesznek.
Az egyes fázisok a megadott sorrendben egymás után jönnek, de egy fázisban több feladatot is elvégezhetünk.
A fázison belül a feladatok (elvileg) párhuzamosítva futnak, hacsak nincs közöttük függőség megadva.
Stages
1 2 3 4 5 |
|
Ez a kódrészlet négy fázist határoz meg, setup, build, test és deploy néven, amelyek ilyen sorrendben követik egymást. A fázisok nevei tetszőlegesek, de azért nem árt, ha utalnak arra, hogy mire valók.
Általános beállítások¶
A stages:
beállítással egy szinten olyan dolgokat tudunk megadni, amelyek minden feladatra érvényesek lesznek.
Egy adott feladaton belül természetestn ezek a beállítások felülbírálhatók.
Általános beállítások
1 2 3 4 5 6 7 8 |
|
A default-on belül az image megadja a használni kívánt docker image-et környezetet. Ha a feladatban ezt nem írjuk felül, akkor ezt a default image-et fogja használni.
A before_script és after_script minden feladatban a saját szkript/parancsok előtt/után lefut. A feladatban ez is felülírható.
Konkrét feladatok¶
A konkrét feladatokat mindig úgy kell elképzelni, hogy elindul a megfelelő docker image, ezután checkout-oljuk a repository aktuális verzióját (amelyik verzióban a változás történt), majd ennek a gyökerében kezdünk el dolgozni.
Feladat (job): setup
1 2 3 4 5 6 7 8 |
|
A setup
itt a feladat neve, ami majd a setup
fázisban (stage) fut le.
Ha a GitLab runner(ek) úgy van(nak) konfigurálva, hogy bizonyos runnerek csak bizonyos feladatokat képesek futtatni, akkor a tags
beállítással tudjuk megadni, hogy ez a feladat mely runnereken futhat.
Egy runner többféle feladat végrahajtására is képes lehet.
A script
-ben lehet megadni a futtatandó parancsokat, ezeket a rendszer szépen sorban fogja végrehajtani.
Ha az allow_failure
beállítással megengedjük a hibát, akkor a többi feladat és fázis hiba esetén is lefut; különben a teljes CI pipeline hibával leáll.
Feladat (job): egység teszt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
A unit
itt a feladat neve, ami majd a test
fázisban (stage) fut le.
A script
-ben most úgy indítjuk el a pytest
modult, hogy az az eredményeket a test_unit.xml
-be (is) beleírja, és csak a test/test_geometry_unit.py
-ben található teszteket futtassa.
A dependencies
megadja, hogy mely feladatoknak kell sikeresen lefutnia ahhoz, hogy ez a feladat értelmesen elindítható legyen.
Az atrifacts
beállítások azt mondják meg, hogy a GitLab mely fájlokat őrízze meg (és meddig) ebből a feladatból.
A when: always
azt írja elő, hogy ha a feladat bukik (van hibás teszt), akkor is legyen eltárolva a riport.
A paths
megadja, hogy mely fájlokról van szó, az expire_in
pedig, hogy milyen hosszan kell tárolni.
A reports
és az alatti junit
rész speciális, a GitLab ezek után ezt a fájlt egységteszt-futtatás junit formátumú eredményeként tudja kezelni.
Feladat (job): integrációs teszt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
Az integration
itt a feladat neve, ami majd a test
fázisban (stage) fut le.
A script
-ben most úgy indítjuk el a pytest
modult, hogy az az eredményeket a test_integ.xml
-be (is) beleírja, és csak a test/test_geometry_integ.py
-ben található teszteket futtassa.
A dependencies
megadja, hogy mely feladatoknak kell sikeresen lefutnia ahhoz, hogy ez a feladat értelmesen elindítható legyen.
Jelen esetben az integrációs teszteknek nyilván nincs értelme, ha már az egységtesztek sem futnak.
Kicsit furcsának tűnhet, hogy a reports
megadásával ezt az integrációs tesztet is unit tesztként kezeljük, de nincs ellentmondás.
A junit az egy elterjet (Java-s) unit teszt keretrendszer -- hasonló a pytest-hez (bár történelmileg lehet, hogy a "pytest hasonlít a junit-hoz" lenne hitelesebb állítás) -- és a formátum erről kapta a nevét.
(Ahogy eredetileg a "trafipax" sem a sebességmérő eszközök vagy a "kuka" sem a szemétgyűjtők általános elnevezése, hanem a gyártóik neve volt.)
Azt pedig már megbeszéltük, hogy az egységteszt keretrendszerekkel integrációs teszteket is lehet készíteni.
Feladat (job): deploy
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
A deploy
itt a feladat neve, ami majd a deploy
fázisban (stage) fut le.
Az only
-val jelezhetjük, hogy ezt a feladatot csak akkor kell futtatni, ha a main-ben van változás.
A tesztek mindig, minden változásra lefutottak, de a szoftvernek csak a main verzióját tesszük elérhetővé; és a dependencies
alapján azt is csak akkor, ha a tesztek sikeresen lefutottak.
A script
részben kellene kiadni azokat a parancsokat, amik feltöltik valahová a szoftver aktuális verzióját (ha olyan a szoftver, akkor a build
fázisban belőle készített valamilyen artifact-et -- Python projektet feltételezve, ahol a forrás maga futtatható, így nincs szükség külön build lépésre, most a build
feladat ki is maradt), de erre most nem adunk példát.
Példa¶
Adott a következő -- fejlesztés alatt álló, nem kész -- chess
python package:
chess/__init__.py
1 2 3 4 5 6 7 8 9 10 11 |
|
chess/board.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
|
chess/constants.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
|
chess/figures.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 |
|
Adott továbbá a csomaghoz pár kezdeti teszt:
test/conftest.py
1 2 3 4 5 6 7 8 |
|
test/test_board.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
test/test_figures.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
|
Szeretnénk, ha a main branch minden változása után lefutnának a tesztek, és a teszteredmények egy darabig elérhetőek maradnának.
Mivel a test_figures.py
használ egy olyan feature-t, amit a test_board.py
-ban tesztelünk, ezért ha utóbbi hibával fut le, az előbbi futtatása már már felesleges.
A fentieket (a git-okt.sed.inf.szte.hu szerveren a teljes SDP25-SzoftverfejlesztesiFolymatok csoportra éppen érvényes konfiguráció szerint legalábbis) a következő .gitlab-ci.yml
konfigurációs fájl oldja meg:
.gitlab-ci.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
|
Ehhez persze szükség lesz a requirements.txt
fájlra is:
requirements.txt
1 2 3 4 5 6 7 8 |
|