GitHub Actions - Abhängigkeiten automatisch aktualisieren

in  Builds & Tests , ,

GitHub Actions - Abhängigkeiten automatisch aktualisieren

Nach der allgemeinen Einführung in GitHub Actions kommen wir nun zu dem ersten Anwendungsfall, der in vielen Projekten angewendet werden kann. Fast jedes Softwareprojekt bedient sich heutzutage zusätzlicher Drittkomponenten. Diese müssen regelmäßig geprüft und auf Stand gehalten werden, um sowohl Fehlerkorrekturen als auch neue Funktionen zu erhalten. In den meisten Programmiersprachen und Umgebungen kommen Paketmanager zum Einsatz, mit denen diese Abhängigkeiten verwaltet und gepflegt werden. Das ist eine geeignete Voraussetzung dafür, die Updates zu automatisieren.

Verschiedene Varianten möglich

Eine wichtige Entscheidung betrifft die Frage, was passieren soll, sobald Aktualisierungen verfügbar sind. Denkbar wäre es, eine Benachrichtigung via E-Mail, Slack oder anderen Kanälen zu versenden. Möglich wäre es auch, die Änderungen automatisch in das Repository zu committen, allerdings könnte dies zu ungewollten Nebeneffekten führen. Wir bevorzugen hingegen einen Zwischenschritt: der Workflow soll einen Pull Request mit den Änderungen anlegen, der dann - nach manueller Sichtung und zum gewünschten Zeitpunkt - per Knopfdruck in den Quellcode überführt werden kann.

Ein Beispiel

Hier ist ein Workflow, den wir in verschiedenen Repositories mit PHP-Code einsetzen:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
name: Update dependencies
on:
  schedule:
    - cron:  '0 */12 * * *' # run every 12 hours
jobs:
  update-deps:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout sources from main branch
      uses: actions/checkout@v2
      with:
        ref: main

    - name: Update dependencies
      run: |
        cd extensions/Zikula/ContentModule/
        composer update        

    - name: Commit changes and create/update pull request
      id: cpr
      uses: peter-evans/create-pull-request@v3
      with:
        token: ${{ secrets.GITHUB_TOKEN }}
        commit-message: 'update dependencies :balloon:'
        title: Vendor update
        body: This is an auto-generated PR with dependency updates.
        labels: dep-updates
        branch: dep-updates

    - name: Check outputs
      run: |
        echo "Pull Request Number: ${{ steps.cpr.outputs.pull-request-number }}"
        echo "Pull Request URL: ${{ steps.cpr.outputs.pull-request-url }}"        

Erklärung der einzelnen Schritte

Im ersten Abschnitt (on) wird definiert, wann der Workflow ausgeführt werden soll: in diesem Beispiel wird die Ausführung nicht über ein Event (wie ein Push oder ein Pull Request) ausgelöst, sondern zeitgesteuert - alle 12 Stunden. Die Notation folgt der Cron-Syntax.

Der zweite Bereich (jobs) beschreibt nun die im Workflow enthaltenen Aufgaben, in unserem Fall ist das nur eine. Jeder Job besteht aus verschiedenen Schritten (Steps), wobei diese sich entweder einer bestehenden Action bedienen oder simple Dinge direkt tun.

Hier die einzelnen Schritte im Beispiel-Workflow im Detail:

  1. Zunächst wird der Quellcode des main-Branches ausgecheckt (Details zu actions/checkout).
  2. Mittels Konsole wird in das gewünschte Verzeichnis gewechselt, um Composer auszuführen. Statt Composer kann hier jeder beliebige Paketmanager zum Einsatz kommen. Ggf. muß geprüft werden, ob das gewünschte Tool vorinstalliert ist - falls das nicht der Fall ist, muß es im Workflow heruntergeladen werden, zum Beispiel mit Hilfe von wget.
  3. Falls es in Folge der Composer-Ausführungen Änderungen im Git gibt, werden diese in einen Branch namens dep-updates committet, aus dem dann ein Pull Request erstellt wird (Details zu peter-evans/create-pull-request).
  4. Abschließend wird eine Nachricht mit der Nummer des erstellten Pull Requests ausgegeben. Diese wird über eine Umgebungsvariable ermittelt, die durch die im vorherigen Schritt ausgeführte Aktion gesetzt wird.

Wie man sieht, ist das Beschreiben eines Workflows ziemlich intuitiv, kann aber auch umfangreichere Funktionen beinhalten. Im nächsten Artikel geht es weiter mit einem weiteren Use Case.