Dawno już nie było wpisu o Gitlab-ie. Jeszcze dawniej nie jadłem sałatki, acz nie przesadzajmy ze skrajnościami. Zafascynowany ostatnio możliwościami jakie daje .NET core chciałbym wam krótko przedstawić sposób na wykorzystanie Gitlab CI w aplikacjach opartych tę technologię.
W tym wpisie poruszę wykorzystanie:
- Gitlab CI Runnera do uruchamiania procesu lokalnie
- Docker-a i Gitlab CI do budowania obrazu na każdym commit-cie
- Gitlab CI do uruchamiania testów
Zacznijmy od wygenerowania aplikacji
Nie zaskoczę informując, że należy rozpocząć od wygenerowania aplikacji za pomocą dotnet CLI. Na swoim blogu już kilka razy opisywałem jak to zrobić, więc nie będę tutaj dublował treści i wypisywał wszystkich komend. Zapraszam do lektury wcześniejszych postów.
Pokrótce opiszę strukturę swojego projektu. Będzie składał się z trzech części:
- scripts -> do przechowywania skryptów *.sh, które będziemy uruchamiać za pomocą naszego pipeline Gitlab-a
- src -> miejsce do trzymania kodu źródłowego naszej aplikacji
- tests -> miejsce do trzymania testów
Po założeniu wygodnych kapci, zrobieniu ciepłego kakałka i wykonaniu wstępnych czynności otrzymujemy następującą strukturę projektu:
. |- scripts |- src | |- Application.csproj |- tests | |- Application.Tests.csproj |- Application.sln
Jako aplikacje testową polecam wygenerować pustą aplikację mvc bez autoryzacji, natomiast jako aplikację do testów polecam stworzyć projekt xUnit. Następnie, jeżeli chcecie korzystać z Visual Studio lub Visual Studio for Mac, polecam stworzenie pliku solucji, do którego trzeba dodać dwa pozostałe projekty jako referencję.
Tworzymy konfigurację Gitlab CI
Aby wykorzystać możliwości Gitlab CI musimy najpierw w głównym katalogu projektu stworzy ć plik konfiguracji .gitlab-ci.yml. Uzupełniamy go zgodnie z notacją YAML. Dokumentację, jak i opis możliwości, możecie znaleźć tutaj.
Jednym z fajniejszych feater-ów dostępnych w tej konfiguracji jest budowanie pipeline na podstawie ogólnodostępnego obrazu docker-a znajdującego się w serwisie docker hub. Wystarczy pod kluczem "image" podać nazwę obrazu, na podstawie którego chcemy zbudować naszą aplikację.
Inne, bardzo przydatne, narzędzie możemy znaleźć pod kluczem "before_script". Pozwala nam na uruchomienie pewnych działań jeszcze przed wykonaniem procesu budowania aplikacji. Jest to idealne miejsce na pobranie zależności projektu czyli wykonanie komendy "dotnet restore", "npm i" czy każdego innego package manager-a, który tego wymaga.
Przykładowy plik .gitlab-ci.yml może wyglądać następująco:
image : microsoft/dotnet:latest
stages:
- build
before_script:
- 'dotnet restore'
build:
stage: build
script:
- 'dotnet build'
only:
- master
Pod kluczem "before_script" znajduje się zdefiniowane zadanie o nazwie "build", które należy do fazy "build". Ma na celu uruchomienie komendy "dotnet build" i zadziałać tylko na branchu "master".
Lokalne uruchomienie Gitlab CI Runner
Jeżeli lubicie tak jak ja "poczuć" na własnej lokalnej maszynie, jak to jest używać takich narzędzi automatyzujących pewne procesy, to pewnie spodoba wam się możliwość uruchomienia całego procesu Gitlab CI lokalnie. Jest to banalnie proste... wymaga to raptem 3 kroków, aby sprawdzić czy przypadkiem nasze zmiany w kodzie nie wywalą pipeline-a :). Ok, wiem, że nikt poza mną nie jara się jak ja takimi rzeczami, acz ta wiedza może okazać się przydatna.
Instalacja Gitlab CI Multi Runner
Na początku instalujemy "runnera", który pozwoli nam uruchamiać lokalne pipeline-y.
Jest kilka sposobów by go zainstalować, ja nie lubiąc sobie utrudniać wybrałem najprostszy. Ogarnąłem to za pomocą brew, packet manager-a dla Mac OS-a.
$ brew install gitlab-ci-multi-runner
Inne sposoby instalacji jak i tutoriale instalacji na innych platformach możecie znaleźć w oficjalnej dokumentacji Gitlab-a
Stworzenie repozytorium GIT-a
Drugim krokiem jest stworzenie lokalnego repozytorium GIT i dodaniu do niego naszego projektu. Możemy to zrobić za pomocą komend
$ git init
$ git add .
$ git commit -m "Initial Commit"
Bez tego otrzymamy dość nieprzyjemnie wyglądający błąd
Uruchomienie lokalnego runner-a
W tym momencie zauważyliście, że definicja słowa "krótko" jest zależna od punktu widzenia. Budujcie jednak w sobie siły, gdyż zbliżamy się do końca. Uruchomiamy runner-a Gitlab CI. Wykonujemy to za pomocą komendy:
$ gitlab-runner exec docker build
Argument exec oznacza uruchomienie runner-a o silniku (executor) docker, który ma uruchomić zadanie "build" zdefiniowane w pliku .gitlab-ci.yml.
Mimo tego że mam dość mocnego laptop-a, to uruchomienie całego procesu razem ze ściągnięciem wszystkich jego zależności, podstawowej wersji obrazu docker-a, zbudowanie obrazu, następnie projektu.... to musi trochę zająć. No, chyba, że mamy do dyspozycji światłowód i 16 rdzeniowy procesor...
Jak widać na powyższym obrazku kilka minut cierpliwości pozwoliło uzyskać potwierdzenie, iż wszystko się udało.:)
Dzięki lokalnemu uruchamianiu runner-ów Gitlab CI możemy zdecydowanie zmniejszyć zużycie darmowych minut w pipeline poprzez uniknięcie commit-ów typu "Missing **service in gitlab-ci" (które jeszcze do niedawna często mi się zdarzały).
Uruchomienie Testów w Pipeline
Najprostszą implementacją uruchamiania testów w usłudze CI jest po prostu dopisanie odpowiednich poleceń konsoli w zadaniu "build" znajdującym się w pliku .gitlab-ci.yml. Wygląda to następująco:
build:
stage: build
script:
- 'dotnet build'
- 'cd tests/Application.Tests'
- 'dotnet test'
Oczywiście możemy to wynieść do osobnego pliku nazwanego na przykład run-tests.sh i uruchomić go za pomocą konsoli. Wtedy zawartość wyglądała by następująco:
cd tests/Application.Tests
dotnet test
Natomiast zadanie "build" musielibyśmy zmodyfikować do następującej formy:
build:
stage: build
script:
- 'dotnet build'
- 'chmod -R +x ./scripts'
- 'scripts/run-tests.sh'
Musimy tutaj pamiętać o nadaniu praw do uruchamiania katalogowi scripts inaczej nasz pipeline może się wywalać na próbie uruchamiania pliku bez odpowiednich praw w systemie.
Teraz pewnie nasuwa się pytanie co takim zabiegiem osiągnęliśmy? Przecież nie dość, że nasze zadanie znajdujące się w pliku .gitlab-ci.yml (nie jest nawet o linijkę krótsze), to posiadamy dodatkowo plik, który musimy utrzymywać.
Wartością dodaną w takim rozwiązaniu jest to, że możemy w prosty sposób tworzyć nowe pliku *.sh w katalogu scripts i je uruchamiać, np. tworzenie plików odpowiedzialnych za uaktualnienie obrazu w serwisie docker hub. Ewentualnie otrzymaliśmy możliwość stworzenia automatycznego deploy-a dokumentacji na wybrany serwer.
Podsumowanie
W dzisiejszym poście pokazałem wam jak za pomocą Gitlab CI na każdym commit-cie, zbudować aplikację, uruchomić testy, a także jak za pomocą paczki gitlab-ci-multi-runner uruchomić to wszystko na waszej lokalnej maszynie.
A w niedługim czasie wracam do opisywania możliwości samego docker-a :)
Do następnego!
Cześć :)