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 - rozbudowanie podstawowego skryptu
  • wpis 3 - CubeIDE - przygotowanie testowego projektu
  • wpis 4 - CubeIDE - przygotowanie obrazu dockera do budowania
  • wpis 5 - CubeIDE - budowanie projektu
  • wpis 6 - rozbudowanie skryptów budujących
  • wpis 7 - automatyczne testy jednostkowe
  • wpis 8 - 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:

fasdf

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:

fasdf

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

fasdf

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:

fasdf

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:

fasdf

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:

fasdf

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