Continuous-Integration-Tutorial: GitLab-CI/CD einrichten

Das Schöne auf der GitLab-Plattform ist, dass Continuous Integration von Anfang an bereits mitgedacht wurde. Insofern geht das Aufsetzen eines Continuous-Integration-Prozesses (CI) samt Continuous Delivery/Deployment (CD) vergleichsweise einfach von der Hand. Ganz von selbst erledigt sich das Ganze jedoch nicht und was dafür notwendig ist, damit der Prozess läuft, ist im Folgenden unser Thema.

Was ist das Ziel?

Einmal mehr ist unser Python-Programm „Fizzbuzz“ der Ausgangspunkt für unsere Experimente mit den verschiedenen Phasen der Software-Entwicklung. An dieser Stelle möchten wir, dass unsere Tests, die wir für Fizzbuzz geschrieben haben, automatisch nach einem push ausgeführt werden. Außerdem möchten wir, dass Pylint unseren Python-Code prüft und uns darauf hinweist, falls wir uns vom PEP-8-Styleguide entfernt haben. Wie sich das mit GitLab als Git-Hoster realisieren lässt, sehen wir uns nun im Detail an.

Voraussetzung

Die Basis für Continuous Integration (CI) ist eine Datei namens „.gitlab-ci.yml“, die wir auf der höchsten Ebene unseres Repositorys anlegen. Dafür klicken wir in Visual Studio Code links im Explorer auf den „New File“-Knopf, der sich einblendet, wenn man mit dem Mauszeiger über den Eintrag „FIZZBUZZ“ fährt. Die neue Datei nennen wir .gitlab-ci.yml. Darin schreiben wir, welche Schritte unser CI-Workflow durchlaufen soll, welche Skripte auszuführen sind, welche Abhängigkeiten es gibt und ähnliche Konfigurationsdetails. Das Praktische an diesem Vorgehen ist, dass .gitlab-ci.yml ebenfalls der Versionskontrolle unterliegt, weil es ganz einfach Teil unseres Repos ist. Diese Datei wird von GitLab automatisch gefunden und der GitLab Runner führt das darin Enthaltene aus. Angezeigt wird die Ausführung auf der Seite Pipelines. Der Runner ist wie eine Art Terminal, der bei der Ausführung auch anzeigt, was er gerade mit welchem Ergebnis tut.

Hat man einen Runner für das eigene Projekt eingerichtet, führt dieser die CI-Pipeline bei jedem Push oder Commit auf jeglichen Branch des Repos aus. Laut Voreinstellung führt der Runner die drei Stages „build“, „test“ und „deploy“ aus. Innerhalb dieser Stages arbeitet der Runner die darin enthaltenen Jobs ab. Liegen in einem Stage keine Jobs, ignoriert der Runner einfach diese Stage. Du kannst jedoch problemlos selbst Stages anlegen und diese relativ frei benennen.

Die Einrichtung eines Runners erfordert erst mehr Aufwand, wenn du GitLab selbst hostest. Setzt du GitLab über gitlab.com ein, so läuft ein Runner automatisch los, sobald du die „.gitlab-ci.yml“-Datei ins Repository pushst.

Eine GitLab-CI/CD-Pipeline konfigurieren

Das Konfigurieren einer CI/CD-Pipeline in der „.gitlab-ci.yml“-Datei erfolgt in YAML-Syntax. Für die Ausführung unseres Programms verwenden wir eine „Docker“-Umgebung. Docker ähneln virtuellen Maschinen, indem sie eine bestimmte Ausführungsumgebung bereitstellen. Sie sind im Vergleich jedoch viel ressourcensparender. An dieser Stelle tauchen wir nicht tiefer in die Docker-Technologie ein, sondern verwenden diese Container lediglich. Ausgeführt soll unser Programm mit Python in der Version 3.7: image: "python:3.7"

Nun legen wir fest, welche Voraussetzungen bestehen müssen, damit unsere Anwendung fehlerfrei läuft. Dafür gibt es die „before_script:“-Phase, in der wir sicherstellen, dass die von uns benötigten Abhängigkeiten vorhanden sind: before_script: Die hier eingetragenen Kommandos werden vom Runner vor der Ausführung eines jeden Jobs zuerst bearbeitet.

Wir beginnen mit - python --version, um einmal Klarheit über die verwendete Python-Version zu haben. Anschließend stellen wir sicher, dass unsere Abhängigkeiten geklärt sind. Die für uns im Einsatz befindliche Umgebung soll zunächst die benötigten Module installieren: - pip install -r requirements.txt

Als nächstes kommen wir zu den Stages, die wir bearbeitet wissen möchten: stages:. Hier notieren wir unsere Lint- - Lint und unsere Test-Stage - Test.

Als nächstes beschreiben wir die auszuführenden Jobs. Wir fangen mit linting: an. In der Zeile darunter heben wir hervor, auf welche Stage – stage: Lint – wir uns beziehen und schließlich darunter, was ausgeführt werden soll: script: und darunter - pylint *.py. Damit sind wir mit dem ersten Job fertig.

Der zweite Job – unittest: – bezieht sich auf die stage: Test. Auszuführen ist script: mit - python3 test*.py. Fertig.

Die YAML-Datei in Gänze

So sieht die benötigte „.gitlab-ci.yml“-Datei in Gänze aus. Wichtig sind die Zeilenumbrüche und die Einrückungen, die aus Leerzeichen und nicht aus Tabs bestehen. Sollte die YAML-Datei syntaktisch fehlerhaft sein, weist GitLab auf der Webseite darauf hin. Kommentare lassen sich eingliedern. Wie auch in Python beginnen einzeilige Kommentare mit „#“:

# Wir legen fest, dass wir unser Programm mit Python in der Version 3.7 ausführen möchten.
 image: "python:3.7"

# Wir stellen sicher, dass unsere Umgebung für die Ausführung unseres Programms bereit ist.
before_script:
  - python --version
  - pip install -r requirements.txt

# Wir haben zwei Stages: Das Linting und das Testing
stages:
  - Lint
  - Test

# Wir prüfen, ob der eingecheckte Code auch den PEP-8-Richtlinien entspricht.
linting:
  stage: Lint
  script:
    - pylint *.py

# Wir lassen alle Tests laufen. Die Dateien dafür beginnen mit "test". 
unittest:
  stage: Test
  script:
    - python3 test*.py
Beispiel für verschiedene Durchläufe der CI-Pipeline
Beispiel für verschiedene Durchläufe der CI-Pipeline

Fazit

Bei GitLab ist der in der Programmiersprache „Go“ geschriebene „GitLab Runner“ für die Ausführung der Continuous-Integration-Schritte zuständig. Der Runner läuft auf den gängigen Systemen wie macOS, Linux sowie Windows und kann sehr viele Programmiersprachen – wie beispielsweise C, .Net, Java, Python, PHP – testen. Netterweise bietet GitLab an, dass jedes Repository 2000 Minuten pro Monat kostenlos den Runner verwenden kann. Erst danach muss man in einen bezahlten Abschnitt wechseln.

Verwendet man GitLab über gitlab.com und nicht auf einer selbst-gehosteten Instanz, geht das Einrichten einer CI-Pipeline relativ einfach von der Hand, denn du benötigst lediglich eine „.gitlab-ci.yml“-Datei mit entsprechendem Inhalt. Das Konfigurieren innerhalb von „.gitlab-ci.yml“ ist im ersten Moment zwar etwas ungewohnt, aber einmal eingerichtet, hast du einen sehr hilfreichen Schritt innerhalb deines Software-Entwicklungsprozesses. Dank deiner allzeit bereiten CI-Pipeline machst du dir viel weniger Sorgen über die Qualität deiner Software.