#009 - GitLab - CI/CD embedded #01
CI/CD
Chciałbym poruszyć temat do którego nie znalazłem żadnej instrukcji “wprost” - jak dla małych hobbystycznych embedowych projektów uruchomić CI/CD - aby to serwer zajmował się budowaniem projektu + “podniesienie poprzeczki” trudności, ale też użyteczności - w oparciu o projekt z wykorzystaniem STMCubeIDE. Jeśli korzystasz w swoich projektach z make/CMake - to i tak ~80% tekstu może być przydatne dla Ciebie.
Jest to wpis podzielony na kilka mniejszych - zalecane jest przejrzenie od początku, aby zapoznać się z pojęciami i co z czego wynika.
Lista wpisów w serii (będą stopniowow uzupełniane linkami):
- wpis 1 - budowanie HelloWorld w C na GitLabie
- wpis 2 - Dodatkowe informacje na temat skryptów budujących
- wpis 3 - Własny PC/Raspberry jako runner
- wpis 4 - CubeIDE - przygotowanie testowego projektu
- wpis 5 - CubeIDE - przygotowanie obrazu dockera do budowania
- wpis 6 - CubeIDE - budowanie projektu
- wpis 7 - rozbudowanie skryptów budujących
- wpis 8 - automatyczne testy jednostkowe
- wpis 9 - raport pokrycia kodu testami
Na początek założenia tej serii wpisów i trochę teorii. Celem jest uzyskanie instrukcji/procedury jak uruchomić automatyczne budowanie projektu opartego o STMCubeIDE korzystając z serwisu GitLab.
Co do CI/CD - zajmiemy się tutaj podstawową implementacją - będzie można potraktować to jako szablon dla swoich projektów. Tematyka z tym związana jest na tyle rozległa, że od jakiegoś czasu DevOps to całkowicie osobne stanowisko w firmach - w webówce jest na to silny nacisk - w embedded hmm wydaje się mniejszy? Albo tylko ja mam takie wrażenie. Podczas opisu podstawowych rzeczy pojawi się kilka tematów, które potraktuję bardzo po łebkach - jak to czym jest docker i jak z niego korzystać - odeślę jedynie do dokumentacji.
Dlaczego platforma GitLab?
Nie będę ukrywał, że to z nią miałem najwięcej doświadczenia komercyjnego - więc dobrze wiem jak działa. Dodatkowo dostępne są darmowe plany - które także umożliwiają korzystanie z CI/CD - w trochę ograniczonym zakresie - ponieważ jest to 400minut pracy serwera (na miesiąc!) - dla małych projektów jest to w zupełności wystarczające. Istnieje też opcja dokupienia - jest to 1000minut za 10dolarów. Pokażę też jak zminimalizować użycie tego czasu - poprzez wyłączenie automatycznego budowania na rzecz “manualnie” uruchamianych.
GitLaba używam też jako moje główna platforma do hostowania swoich projektów - https://gitlab.com/embedownik - przeszedłem na niego z GitHuba - właśnie z powodu łatwej pracy z CI/CD - wcześniej GitHub nie oferował tego wprost, trzeba było korzystać z dodatkowych platform jak Travis - aktualnie dodają już takie usługi.
Baza projektu HelloWorld
Na start w pierwszym wpisie zajmiemy się zbudowaniem banalnego “hello worlda” w C, wszystkie poszczególne kroki będą osobnymi commitami w repozytorium - będzie można łatwo je prześledzić potem.
Więc po kolei.
Zakładamy repozytorium - w moim przypadku to będzie: https://gitlab.com/embedownik/cicd_simple_c_test
I klasyczna aplikacja w pliku main.c w głównym katalogu:
#include <stdio.h>
int main(void)
{
puts("Hello world - GitLab CICD");
return 0;
}
“dashboard” repozytorium po wypchnięciu tego pliku niczym się nie wyróżnia:

Konfiguracja budowania
W platformie GitLab konfiguracja buildu znajduje się w pliku .gitlab-ci.yml, który domyślnie powinien znajdować się w głównym katalogu projektu. Ogólnie dokumentacja CI/CD → https://docs.gitlab.com/ee/ci/. Po rozszerzeniu widać, że jest to YAML - GitLab udostępnia stronkę do walidacji tych plików - żeby nie trzeba było testować ich przez wypychanie na serwer - jej opis: https://docs.gitlab.com/ee/ci/lint.html.
Podstawowy plik jaki użyjemy wygląda tak:
stages:
- build
release:
stage: build
image: gcc
script:
- gcc main.c -o app
artifacts:
paths:
- app
Po wypchnięciu w dashboardzie projektu zobaczymy nową ikonkę - reprezentuje ona, że aktualny commit jest właśnie budowany:

Po chwili zmieni się na “zielonego ptaszka” - procedura budowania przeszła pomyślnie.

Zacznijmy od omówienia użytego pliku .gitlab-ci.yml - pełna dokumentacja odnośnie tego pliku: https://docs.gitlab.com/ee/ci/yaml/#stage.
Na samym początku listujemy jakie etapy budowania używamy w projekcie - w tym przypadku jest to tylko build - stage będą wykonywać się po sobie. Możemy je nazwać dowolnie, jednak warto zaznaczyć, że przyjęło już się kilka “standardowych nazw” - są to kolejno:
- .pre
- build
- test
- deploy
- .post
Następnie mamy “release” - jest to tak zwany “job” - każdy “job” jest wykonywany w obrębie jakiegoś “stage”, jeden stage może mieć wiele “jobów” - przy czym o ile “stage” wykonywały się kolejno po sobie - “joby” w obrębie stage wykonują się równolegle (o ile mamy tyle maszyn itp - to osobna kwestia). Nazwa joba może być dowolna. Przypisanie do stage build odbywa się w linijce:
stage: build
Następnie podajemy z jakiego obrazu dockera chcemy korzystać - to jest kolejny temat rzeka - nie wchodzę tu totalnie w szczegóły - trochę opiszę gdy to będzie potrzebne dla CubeIDE. Na tym etapie wystarczy stwierdzenie, że obraz “gcc” dostarczy nam “maszynę linuxową” z wgranym toolchainem, który umożliwi kompilację aplikacji.
Kolejną sekcją jest “script” - podajemy tutaj kolejne komendy do wykonania w terminalu dla zbudowania aplikacji. Można je wpisywać tutaj od nowych linii z myślnikami lub można zebrać je w osobnym skrypcie np “build.sh” i tylko go uruchomić. W tym przypadku uruchamiamy tylko gcc dla skompilowania main.c do pliku app.
Ostatnią sekcją w przykładowym pliku są “artefakty” - są to nasze wynikowe pliki z builda - będą dostępne na GitLabie jako archiwum do pobrania. W tym przypadku używam bezpośrednio ścieżki do pliku - można tu używać wildcardów itp.
Jak to wygląda na GitLabie - po kliknięciu w “zielonego ptaszka” na dashboardzie zobaczymy podsumowanie procesu buildu - “wysokopoziomowo” jako pipeline:

W przypadku bardziej zaawansowanych buildów - z wieloma stage i jobs wyświetlą się tutaj relacje pomiędzy nimi, które etapy się budują/zakonczyły się, a także które wykonały się z powodzeniem lub nie. W tym przypadku w stage “Build” job o nazwie “release” przeszedł poprawnie.
W zakładce “Jobs” można znaleźć szczegóły poszczególnych “jobów” i to co nas najbardziej interesuje - ich artefakty do pobrania:

Po kliknięciu ikonki pobierania pobierzemy archiwum o nazwie “artifacts.zip” z plikiem “app” w środku.
Klikając na wybranego job’a zostaniemy przeniesieni na podsumowanie buildu - gdzie znajdziemy log z kompilacji:

Link do tego okna w serwisie GitLab: https://gitlab.com/embedownik/cicd_simple_c_test/-/jobs/1115767213
Z tego możemy też wywnioskować jak wygląda podstawowe flow dla takiego buildu:
- uruchomienie dockerowego kontenera z podanego image
- pobranie repozytorium danego projektu
- uruchomienie buildu
- pobranie artefaktów
- posprzętanie
To koniec pierwszego wpisu z serii.
Podsumowanie co uzyskaliśmy na tym etapie:
- mamy skonfigurowany serwer budowania dla prostej aplikacji helloWorld napisanej w języku C, kolejne buildy będą się wykonywać dla każdego przyszłego commita na serwerze, w każdej chwili możemy pobrać wynikowy plik zbudowany przez serwer