20 Workflow-Grundlagen und Event-Modell

Wer mit GitHub Actions startet, steht schnell vor der Frage: Wann läuft eigentlich ein Workflow? Die Antwort ist gleichzeitig einfach und komplex: Ein Workflow reagiert auf Events – aber welche Events es gibt, wie man sie filtert und wo diese Workflows überhaupt ausgeführt werden, erfordert ein solides Verständnis der zugrunde liegenden Mechanismen.

Dieses Kapitel baut die konzeptionelle Grundlage für alle weiteren Automatisierungen mit GitHub Actions. Wir klären, was Events sind, wie Runner funktionieren und welche Rolle die Workflow-Syntax spielt. Am Ende erstellen wir den ersten produktiven Workflow für unser Beispielprojekt – einen Badge Generator für Repository-Metriken.

20.1 Events – der Puls von GitHub Actions

GitHub Actions folgt einem ereignisgesteuerten Modell. Workflows werden nicht “einfach so” ausgeführt, sondern immer als Reaktion auf ein konkretes Ereignis. Dieses Konzept unterscheidet GitHub Actions fundamental von klassischen Cronjob-Systemen oder manuell getriggerten CI-Tools.

20.1.1 Was ist ein Event?

Ein Event ist eine Aktivität, die in einem Repository stattfindet – etwa ein Push, das Öffnen eines Pull Requests oder das Erstellen eines Issues. GitHub Actions lauscht kontinuierlich auf solche Ereignisse und startet Workflows, die für das jeweilige Event konfiguriert wurden.

Wichtig zu verstehen: Ein Event ist nicht der Workflow selbst. Das Event ist lediglich der Auslöser. Der Workflow definiert, was als Reaktion auf dieses Event geschehen soll.

20.1.2 Die wichtigsten Event-Typen

GitHub Actions kennt über 40 verschiedene Event-Typen. Für den Einstieg sind diese besonders relevant:

Event Wann wird es ausgelöst Typischer Anwendungsfall
push Bei jedem Commit auf einen Branch oder Tag CI-Builds, Tests, Linting
pull_request Bei Aktivitäten an Pull Requests PR-Validierung, Code Reviews
workflow_dispatch Manuell über UI oder API On-Demand Deployments, Datenverarbeitung
schedule Zeitgesteuert (Cron-Syntax) Nächtliche Builds, Reports, Cleanup
workflow_call Von einem anderen Workflow aufgerufen Wiederverwendbare Workflows
workflow_run Nach Abschluss eines anderen Workflows Verkettete Pipelines
issue_comment Bei Kommentaren in Issues/PRs Bot-Reaktionen, ChatOps
release Bei Release-Aktivitäten Automatisches Publishing

Ein vollständiger Workflow beginnt immer mit der on-Direktive, die festlegt, auf welche Events reagiert werden soll:

on:
  push:
  pull_request:
  workflow_dispatch:

Dieses Beispiel konfiguriert einen Workflow, der bei Pushes, Pull Requests und auf manuelle Anforderung läuft.

20.1.3 Activity Types – Feinsteuerung für Events

Viele Events haben unterschiedliche Activity Types, die genauer spezifizieren, welche Art von Aktivität den Workflow triggern soll. Das pull_request-Event kennt beispielsweise Activity Types wie opened, synchronize, closed, reopened und viele mehr.

Ohne explizite Angabe nutzt GitHub Actions sinnvolle Standardwerte. Für pull_request sind das:

Häufig übersehen werden labeled und unlabeled – diese sind nicht in den Defaults enthalten. Wer auf Label-Änderungen reagieren möchte, muss sie explizit angeben.

Möchte man abweichend davon nur auf bestimmte Activity Types reagieren, nutzt man das types-Keyword:

on:
  pull_request:
    types: [opened, ready_for_review]

Dieser Workflow läuft ausschließlich, wenn ein PR erstellt wird oder von Draft zu Ready wechselt – nicht aber bei neuen Commits (synchronize).

Praxis-Hinweis: Die Dokumentation jedes Events listet alle verfügbaren Activity Types auf. Es lohnt sich, diese zu kennen, um unnötige Workflow-Läufe zu vermeiden und damit Rechenzeit und Kosten zu sparen.

20.1.4 Filter für Events – Branches, Paths und Tags

GitHub Actions bietet mehrere Filter, um Events noch präziser zu steuern. Die wichtigsten sind branches, paths und tags.

20.1.4.1 Branch-Filter

Der branches-Filter beschränkt Event-Trigger auf bestimmte Branches:

on:
  push:
    branches:
      - main
      - 'releases/**'

Dieser Workflow läuft nur bei Pushes auf main oder Branches, die mit releases/ beginnen. Das Glob-Pattern releases/** matcht dabei beliebig viele Unterverzeichnisse (z.B. releases/v1.0, releases/v2.0/hotfix).

Das Gegenstück ist branches-ignore:

on:
  push:
    branches-ignore:
      - 'experimental/**'
      - 'wip-*'

Wichtig: Man kann branches und branches-ignore nicht gleichzeitig für dasselbe Event verwenden. GitHub Actions erlaubt entweder Positivliste oder Negativliste, nicht beides.

20.1.4.2 Path-Filter

Mit paths reagiert der Workflow nur auf Änderungen in bestimmten Dateien oder Verzeichnissen:

on:
  push:
    paths:
      - 'src/**'
      - '**.py'

Das ist besonders nützlich in Monorepos: Ein Frontend-Workflow muss nicht laufen, wenn nur Backend-Code geändert wurde.

Kombinierte Filter: branches und paths können kombiniert werden – dann müssen beide Bedingungen erfüllt sein:

on:
  pull_request:
    branches:
      - main
    paths:
      - 'docs/**'

Dieser Workflow läuft nur bei PRs gegen main, die Änderungen im docs/-Verzeichnis enthalten.

20.1.4.3 Tag-Filter

Für Release-Workflows sind Tag-Filter praktisch:

on:
  push:
    tags:
      - 'v*'
      - 'release-*'

Läuft bei allen Tags, die mit v oder release- beginnen.

Wichtiger Unterschied: push.tags triggert bei jedem Git-Tag, das release-Event jedoch nur, wenn ein GitHub Release (mit Release Notes, Assets, etc.) erstellt wird. Für reine Tag-basierte Workflows ist push.tags die richtige Wahl, für Release-Publishing eher release: [published].

20.1.5 Special Case: workflow_dispatch

Das workflow_dispatch-Event verdient besondere Aufmerksamkeit, da es manuelle Workflow-Läufe ermöglicht – mit optionalen Eingabeparametern:

on:
  workflow_dispatch:
    inputs:
      environment:
        description: 'Deployment target'
        required: true
        type: choice
        options:
          - development
          - staging
          - production
      dry_run:
        description: 'Perform dry run?'
        required: false
        type: boolean
        default: false

Diese Inputs erscheinen dann in der GitHub UI als Formularfelder. Der Workflow kann auf die Werte mit ${{ inputs.environment }} bzw. ${{ inputs.dry_run }} zugreifen.

Manuelle Workflows sind unverzichtbar für:

20.1.6 Das Konzept der Event-Metadaten

Jedes Event bringt einen Kontext mit, der über github.event zugänglich ist. Dieser Kontext enthält alle relevanten Informationen zum auslösenden Ereignis.

Beispiele:

Diese Daten können in Conditionals genutzt werden:

jobs:
  conditional-job:
    runs-on: ubuntu-latest
    if: github.event.pull_request.draft == false
    steps:
      - run: echo "PR ist kein Draft"

Oder in Skripten:

steps:
  - name: Print PR info
    run: |
      echo "PR #${{ github.event.number }}"
      echo "Author: ${{ github.event.pull_request.user.login }}"

20.2 Runner – wo Workflows ausgeführt werden

Events definieren wann ein Workflow läuft, Runner definieren wo. Ein Runner ist eine Maschine (physisch oder virtuell), die Jobs ausführt. GitHub Actions kennt zwei grundlegend verschiedene Runner-Typen.

20.2.1 GitHub-Hosted Runners

GitHub-Hosted Runners sind von GitHub bereitgestellte virtuelle Maschinen, die vollständig verwaltet werden. Sie bieten:

Die Standard-Runner haben folgende Eigenschaften:

Runner-Label Betriebssystem Hardware
ubuntu-latest Ubuntu Linux (aktuell 22.04) 2-core CPU, 7 GB RAM, 14 GB SSD
windows-latest Windows Server 2022 2-core CPU, 7 GB RAM, 14 GB SSD
macos-latest macOS 14 (Sonoma) 3-core CPU, 14 GB RAM, 14 GB SSD

Hinweis: Diese Hardware-Angaben sind “Best Effort” – GitHub garantiert diese Specs nicht vertraglich. In der Praxis sind sie jedoch stabil. Für Workloads mit harten Hardware-Anforderungen sind Larger Runners oder Self-Hosted Runner die bessere Wahl.

Wichtig: GitHub-Hosted Runners werden auf Azure-VMs in Microsoft-Rechenzentren ausgeführt. Die macOS-Runner laufen ebenfalls in Azure-Datacentern.

20.2.1.1 Preinstalled Software

Die Runner-Images enthalten hunderte vorinstallierte Tools. Die genaue Liste ist im Repository actions/runner-images einsehbar und wird wöchentlich aktualisiert.

Beispiele für vorinstallierte Software auf Ubuntu-Runnern:

Best Practice: Nutze Actions aus dem GitHub Marketplace (z.B. actions/setup-python), um spezifische Tool-Versionen zu installieren, statt dich auf vorinstallierte Versionen zu verlassen. Das macht Workflows robuster gegenüber Image-Updates.

20.2.1.2 Kosten und Limits

GitHub-Hosted Runners sind für öffentliche Repositories kostenlos. Private Repositories haben ein monatliches Kontingent:

Danach wird minutengenau abgerechnet. Die Preise variieren je nach Betriebssystem:

20.2.2 Self-Hosted Runners

Self-Hosted Runners sind Maschinen, die du selbst bereitstellst und verwaltest. Sie bieten maximale Kontrolle, bedeuten aber auch Verantwortung.

20.2.2.1 Vorteile von Self-Hosted Runners

20.2.2.2 Nachteile und Verantwortung

Kritischer Sicherheitshinweis: Self-Hosted Runner sollten niemals in öffentlichen Repositories eingesetzt werden. Code aus Forks kann auf dem Runner ausgeführt werden und hat potenziell Zugriff auf:

Für öffentliche Repos immer GitHub-Hosted Runner verwenden!

20.2.2.3 Lifecycle eines Self-Hosted Runners

  1. Installation: Runner-Applikation auf der Maschine installieren
  2. Registrierung: Mit einem Token bei GitHub registrieren
  3. Polling: Runner fragt GitHub kontinuierlich nach Jobs
  4. Ausführung: Job wird heruntergeladen und ausgeführt
  5. Cleanup: (optional) Aufräumen nach dem Job

Self-Hosted Runner können auf Repository-, Organization- oder Enterprise-Ebene registriert werden.

20.2.2.4 Runner-Labels

Runner werden über Labels identifiziert. Self-Hosted Runner bekommen automatisch diese Standard-Labels:

Zusätzlich können custom Labels vergeben werden:

./config.sh --labels gpu,high-memory

Im Workflow nutzt man dann:

jobs:
  ml-training:
    runs-on: [self-hosted, linux, gpu]

Dieser Job läuft nur auf Self-Hosted Linux-Runnern mit GPU-Label.

20.2.3 Vergleich: GitHub-Hosted vs. Self-Hosted

Kriterium GitHub-Hosted Self-Hosted
Setup Keine Konfiguration nötig Installation & Registrierung erforderlich
Wartung Vollautomatisch Selbst verantwortlich
Umgebung Ephemeral (jeder Job frisch) Persistent (manuelles Cleanup nötig)
Software Vordefinierte Images Völlig frei konfigurierbar
Skalierung Automatisch Manuell oder mit Tools wie ARC
Sicherheit Isoliert, managed Selbst härtbar, aber Risiko bei Forks
Kosten Pro Minute (private Repos) Infrastrukturkosten
Latenz Abhängig von GitHub-Region Kann lokal minimiert werden
Netzwerk Öffentliches Internet Zugriff auf interne Netze

Faustregel: Starte mit GitHub-Hosted Runnern. Wechsle zu Self-Hosted nur, wenn du:

20.3 Die Workflow-Syntax – YAML-Struktur verstehen

Ein GitHub Actions Workflow ist eine YAML-Datei im Verzeichnis .github/workflows/. Die grundlegende Struktur folgt einem festen Schema.

20.3.1 Anatomie eines Workflows

name: CI Pipeline
on:
  push:
    branches: [main]
  pull_request:

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Run tests
        run: npm test

Schauen wir uns die Komponenten im Detail an.

20.3.1.1 name – der Workflow-Name

name: CI Pipeline

Der Name erscheint in der GitHub UI. Er ist optional – ohne name nutzt GitHub den Dateinamen.

Best Practice: Verwende aussagekräftige Namen, die den Zweck beschreiben: “Build and Test”, “Deploy to Production”, “Security Scan”.

20.3.1.2 on – Event-Konfiguration

on:
  push:
    branches: [main, develop]
  pull_request:
  workflow_dispatch:

Die on-Sektion definiert, welche Events den Workflow auslösen. Sie kann eine einzelne Event-Angabe sein oder eine komplexe Struktur mit Filtern.

20.3.1.3 jobs – die Arbeitseinheiten

Ein Job ist eine Sammlung von Steps, die auf demselben Runner ausgeführt werden. Jobs laufen parallel, es sei denn, Abhängigkeiten werden explizit definiert (mehr dazu in Kapitel 2).

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm install
      - run: npm run build
20.3.1.3.1 runs-on – Runner-Auswahl

Die runs-on-Direktive legt fest, auf welchem Runner der Job laufen soll:

runs-on: ubuntu-latest

Für GitHub-Hosted Runner: - ubuntu-latest, ubuntu-22.04, ubuntu-20.04 - windows-latest, windows-2022, windows-2019 - macos-latest, macos-14, macos-13

Für Self-Hosted Runner:

runs-on: [self-hosted, linux, gpu]
20.3.1.3.2 steps – die Aktionen

Steps sind die einzelnen Befehle oder Actions, die nacheinander ausgeführt werden. Es gibt zwei Arten:

1. run – Shell-Befehle

steps:
  - name: Show directory
    run: |
      pwd
      ls -la

2. uses – Actions aus dem Marketplace

steps:
  - uses: actions/checkout@v4
  - uses: actions/setup-python@v5
    with:
      python-version: '3.11'

Actions sind wiederverwendbare Bausteine. Die @v4-Syntax gibt die Version an (Git-Tag im Actions-Repository).

20.3.2 Environment Variables und Secrets

Steps können auf Umgebungsvariablen und Secrets zugreifen:

steps:
  - name: Deploy
    run: ./deploy.sh
    env:
      API_TOKEN: ${{ secrets.API_TOKEN }}
      ENVIRONMENT: production

secrets sind verschlüsselt in den Repository-Einstellungen gespeichert und werden nie in Logs angezeigt.

20.3.3 Conditionals – if-Bedingungen

Jobs und Steps können mit if bedingt ausgeführt werden:

jobs:
  deploy:
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    steps:
      - run: echo "Deploying to production"

Ausdrücke nutzen die GitHub Actions Expression Syntax: ${{ <expression> }}.

20.4 Unser erstes Projekt: Badge Generator

Genug Theorie – setzen wir das Gelernte in die Praxis um. Wir erstellen ein Python-CLI-Tool namens “badge-gen”, das Repository-Metriken sammelt und als SVG-Badges generiert.

20.4.1 Projektziel

Das Tool soll:

  1. Repository-Statistiken über die GitHub API abrufen (Stars, Forks, Open Issues)
  2. SVG-Badges generieren (ähnlich shields.io)
  3. Badges auf einer GitHub Pages-Site anzeigen

20.4.2 Repository-Setup

Erstelle ein neues Repository badge-gen mit dieser Struktur:

badge-gen/
├── .github/
│   └── workflows/
│       └── ci.yml
├── src/
│   └── badge_gen/
│       ├── __init__.py
│       └── cli.py
├── tests/
│   └── test_cli.py
├── pyproject.toml
└── README.md

20.4.3 Code-Qualitätsprüfung mit GitHub Actions

Unser erster Workflow prüft die Code-Qualität bei jedem Push und Pull Request. Erstelle .github/workflows/ci.yml:

name: CI - Code Quality

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  quality-check:
    runs-on: ubuntu-latest
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      
      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'
      
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install ruff mypy
      
      - name: Lint with Ruff
        run: ruff check src/ tests/
      
      - name: Type check with mypy
        run: mypy src/

20.4.4 Was passiert hier?

  1. Event-Trigger: Workflow läuft bei Pushes auf main/develop und bei PRs gegen main
  2. Runner: Ubuntu-Latest (kostenlos für Public Repos)
  3. Checkout: actions/checkout@v4 lädt den Repository-Code
  4. Python-Setup: actions/setup-python@v5 installiert Python 3.11
  5. Dependencies: pip install holt Linter (Ruff) und Typen-Checker (mypy)
  6. Quality Checks: Code wird auf Style-Violations und Type-Fehler geprüft

20.4.5 Filter in Aktion

Beachte die Branch-Filter: Der Workflow ignoriert Feature-Branches bei direkten Pushes, reagiert aber auf PRs gegen main. Das ist eine gängige Strategie

20.4.6 Erweiterung: Matrix-Testing

Später können wir den Workflow erweitern, um gegen mehrere Python-Versionen zu testen:

jobs:
  quality-check:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ['3.10', '3.11', '3.12']
    
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}
      # ... rest wie oben

Matrix-Strategien behandeln wir ausführlich in Kapitel 2.

20.5 Lokale Entwicklung vs. GitHub Actions

Eine häufige Frage: Was sollte lokal laufen (z.B. via Pre-Commit Hooks), was in GitHub Actions?

Pre-Commit Hooks (lokal):

GitHub Actions (remote):

Best Practice – kombinierter Ansatz:

Für unser Projekt:

20.6 Debugging von Workflows

Ein Workflow läuft nicht wie erwartet? Die wichtigste Anlaufstelle sind die Workflow-Logs in der GitHub UI:

  1. Navigiere zu “Actions” im Repository
  2. Wähle den Workflow-Run
  3. Klicke auf den Job
  4. Expandiere die Steps

Logs zeigen Zeitstempel, Stdout/Stderr-Ausgabe, Exit-Codes und Runner-Informationen.

20.6.1 Schnelle Debug-Hilfe

Wenn unklar ist, warum ein Workflow (nicht) läuft, hilft ein Debug-Step:

steps:
  - name: Debug Context
    run: |
      echo "Event: ${{ github.event_name }}"
      echo "Ref: ${{ github.ref }}"
      echo "SHA: ${{ github.sha }}"
      echo "Actor: ${{ github.actor }}"

Dies zeigt die wichtigsten Kontextinformationen – oft genug, um das Problem zu identifizieren.

Für fortgeschrittene Debugging-Techniken (lokale Runner mit act, Debug-Logging, systematische Fehlersuche) siehe Kapitel 4.

20.7 Rückblick

In diesem Kapitel haben wir die konzeptionellen Grundlagen von GitHub Actions gelegt: