Continuous-Integration-Tutorial: GitHub Actions einrichten

Erst seit kurzem bietet GitHub mit den „GitHub Actions“ eine Möglichkeit, Continuous Integration (CI) innerhalb von GitHub in unseren Software-Entwicklungsprozess einfließen zu lassen. Bisher war man hierfür auf Dienste von Dritten angewiesen. Werfen wir einen Blick darauf, wie du für dein Projekt die GitHub Actions einrichten kannst.

Was ist das Ziel?

Wir möchten, dass unsere Tests, die wir für Fizzbuzz geschrieben haben, nach einem Push ins Repository automatisch ausgeführt werden. Davor möchten wir sichergehen, dass unser Code dem unter PEP 8 festgelegten Styleguide entspricht. Hierfür lassen wir nach jedem Push automatisch Pylint laufen. GitHub Actions bieten für unsere beiden Anforderungen Möglichkeiten, die wir uns im folgenden Beispiel ansehen wollen.

GitHub Actions erstellen

  1. Wir öffnen unser Fizzbuzz-Projekt in Visual Studio Code.
  2. In unserem Projektverzeichnis legen wir ein verstecktes Verzeichnis namens „.github“ an. Das können wir im Terminal von VS Code machen – mkdir .github – oder wir rufen links in der Leiste von VS Code den „Explorer“ auf, fahren mit dem Mauszeiger über den Namen unseres Verzeichnisses und klicken von den sich einblendenden Knöpfen auf den „New Folder“-Knopf. Dort nennen wir das Verzeichnis .github.
  3. Darin – cd .github – erzeugen wir ein weiteres Verzeichnis mit dem Namen „workflows“: mkdir workflows oder per Klick auf „New Folder“-Knopf, während das neu erstellte „.github“-Verzeichnis ausgewählt ist. In diesem „workflows“-Verzeichnis legen wir unsere GitHub-Actions an.
  4. Nun erschaffen wir eine Konfigurationsdatei mit der Endung „.yml“; die Bezeichnung können wir selbst wählen, beispielsweise „ci_workflow“: touch ci_workflow.yml. Oder wir klicken auf den „New File“-Knopf während das „workflows“-Verzeichnis ausgewählt ist und nennen die neue Datei „ci_workflow.yml“. Wir können auch weitere Workflows anlegen, diese müssen wir auch innerhalb des selben Verzeichnisses speichern.
  5. Fertig mit den Vorbereitungen. Jetzt gilt es in YAML-Syntax den Workflow zu beschreiben.

Workflow beschreiben

GitHub hat die Workflow-Syntax dokumentiert. Ein Workflow beginnt mit dem Namen: name: Lint and Test It; unser Workflow heißt „Lint and Test It“. Damit drücken wir aus, dass wir zunächst den Linter über unseren Code gucken und anschließend unsere Testfälle ausführen lassen möchten. In der nächsten Zeile geben wir an, dass der Workflow beim Ereignis „push“ loslegen soll: on: [push]. Die Angabe weiterer Ereignisse ist möglich und ebenfalls seitens GitHub dokumentiert. Die eckigen Klammern deuten es bereits an: Darin lassen sich auch weitere Auslöser festlegen.

Jobs

Als nächstes folgen die „Jobs“: jobs:. Ein Workflow kann mehrere Jobs haben, die parallel ausgeführt werden. Wir begnügen uns zunächst mit dem Standard-Job build: Nun legen wir mit dem Schlüsselwort „runs-on“ fest, auf welchem System unser Job laufen soll. Für unser Python-Programm spielt das zwar keine Rolle, aber weil wir schon auf dem Mac entwickelt haben, lassen wir alles unter einer aktuellen Version von Ubuntu-Linux laufen: runs-on: ubuntu-latest. Damit haben wir einen kleinen Test, dass unser Python-Programm von keiner bestimmten Plattform abhängig ist.

Steps

Jobs bestehen aus einer Abfolge von Aufgaben, die „steps“ genannt werden. Unser Step beginnt mit dem Auschecken unseres Codes aus dem Repository. Da wir derzeit keine weitergehenden Ansprüche an das Auschecken stellen, gehen wir den Standardweg: - uses: actions/checkout@v1.

Nun müssen wir uns um die Umgebung kümmern, in der unser Programm laufen soll. Hierfür richten wir Python ein, daher hat der Schritt den Namen - name: Set up Python 3.7. Abermals nehmen wir die Standardeinrichtung von GitHub uses: actions/setup-python@v1, aber wir möchten sicherstellen, dass es die Version 3.7 ist. Das erreichen wir mit der Eingabe von with: und in der darauffolgenden Zeile python-version: 3.7.

Unsere Abhängigkeiten spielen bei der Einrichtung eine wichtige Rolle. Darum kümmern wir uns im nächsten Schritt mit - name: Install dependencies. Hier führen wir direkt Python-Code aus. Dafür verwenden wir in unserer YAML-Datei das Schlüsselwort run:, da wir allerdings mehrere aufeinanderfolgende Befehle ausführen möchten ergänzen wir unser „run:“ mit „|“, so dass die Zeile folgendermaßen aussieht: run: |. Darunter folgen die auszuführenden Kommandos python -m pip install --upgrade pip und pip install -r requirements.txt. Mit der ersten Zeile sorgen wir dafür, dass das Python-Modul „pip“ in einer aktuellen Version zur Verfügung steht. In der zweiten Zeile spielen wir die in der „requirements.txt“ gelisteten Abhängigkeiten ein.

Jetzt können wir endlich den Linter losschicken, denn es ist alles angerichtet: - name: Lint with Pylint. Auch hier führen wir Kommandos direkt aus, daher schreiben wir: run: | und darunter pylint *.py. Wir könnten die Pipe („|“) weglassen, denn wir führen nur einen Befehl aus, aber es stört nicht und so sind wir zumindest auf Erweiterungen vorbereitet.

Zu guter Letzt fehlen noch unsere Tests, die wir mit einem beherzten - name: Test all the Things einleiten. Wiederum brauchen wir unsere run: |-Angabe und in der Zeile darunter fordern wir zur Ausführung auf: python test*.py. Fertig.

Die YAML-Datei in Gänze

So sieht die benötigte YAML-Datei in Gänze aus. Wichtig sind die Einrückungen, die aus Leerzeichen und nicht aus Tabs bestehen. Kommentare lassen sich eingliedern; wie auch in Python beginnen einzeilige Kommentare mit „#“. Die Kommentare erläutern die einzelnen Vorgänge:

name: Lint and Test It

# Unsere Automatisierung legt nach einem push los.
on: [push]

jobs:
  build:

    # Wir laufen auf dem aktuellen Ubuntu.
    runs-on: ubuntu-latest

    steps:
    # Wir checken zuerst das Repository aus.
    - uses: actions/checkout@v1

    # Wir richten die benötigte Umgebung ein.
    - name: Set up Python 3.7
      uses: actions/setup-python@v1
      with:
        python-version: 3.7

    # Wir sorgen dafür, dass die Abhängigkeiten bereit sind.
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install -r requirements.txt

    # Jetzt können wir den Linter losschicken.
    - name: Lint with Pylint
      run: |
        pylint *.py

    # Jetzt können wir die Tests ausführen.
    - name: Test all the Things
      run: |
        python test*.py

GitHub Actions in Aktion

Wir committen und pushen unsere Änderungen in unser GitHub-Repository und sehen uns die Konsequenzen auf der Webseite des Repos an. Auf der zugehörigen Webseite unseres Repos gibt es ziemlich in der Mitte einen Knopf namens „Actions“. Dahinter sind alle Workflows namentlich auf geführt und auch die jeweiligen Ergebnisse mitsamt den Logs sind dort festgehalten.

Läuft im Augenblick des Besuchs der Actions-Seite gerade ein Workflow, können wir uns den Lauf live ansehen. Dabei wird analog einem Terminal-Fenster ausgegeben, welche Schritte mit welchen Ergebnissen im jeweiligen Augenblick ausgeführt werden.

Vorgefertigte GitHub Actions

Einen solchen Workflow kann man wie hier gezeigt selbst erstellen, man kann aber auch auf bereitgestellte Workflows für das eigene Umfeld zurückgreifen. Klickt man auf der Actions-Webseite auf den Knopf „New workflow“, bietet GitHub Zugriff eine Vielzahl von Workflows und weist insbesondere auf passende hin: In unserem Fall bietet die Seite zu „GitHub Actions“ zwei explizit für Python-Projekte passende Workflows hin. Diese lassen sich einfach ins eigene Repository übernehmen und anschließend gemäß den eigenen Vorstellungen anpassen.

GitHub hat erkannt, dass wir mit Python arbeiten und bietet uns passende Workflows an.
GitHub hat erkannt, dass wir mit Python arbeiten und bietet uns passende Workflows an.

Preise

Während öffentliche Repositories die GitHub Actions kostenlos einsetzen können, ist die Zeit für private Repos begrenzt. So hast du für dein privates Repo 2000 Minuten zur Verfügung, in denen deine Actions laufen können. Solltest du das Limit überschreiten, bucht GitHub minütlich ab – die Preise liegen da bei 1–8 Cent pro Minute, je nachdem, auf welcher Maschine deine Actions laufen. Hast du ein Pro-Account bei GitHub, bekommst du 3000 Minuten geschenkt. Repositories von Team-Accounts haben sogar 10.000 Minuten inbegriffen.

Fazit

GitHub Actions ähneln stark der bereits beschriebenen Variante von GitLab CI/CD. Auch hier haben wir eine Anleitung in einer YAML-Datei, die ins Repository eingecheckt wird und auf diese Weise der Versionskontrolle unterliegt. Auch wenn sich die beiden Git-Hoster in der hier thematisierten Hinsicht kaum unterscheiden, merkt man jedoch, dass GitLab bereits länger CI/CD-Workflows anbietet, denn die Integration ist tiefer. GitHub Actions haben das Zeug, ähnlich mächtig zu werden und einen der Vorteile für den Einsatz von GitLab aufzuheben; noch muss GitHub allerdings ein wenig Einsatz dafür aufbringen, denn GitLab CI/CD wirkt zurzeit – Oktober 2019 – ausgereifter.