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.
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.
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.
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.
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:
opened (PR wurde erstellt)synchronize (neue Commits im PR)reopened (geschlossener PR wurde wieder geöffnet)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.
GitHub Actions bietet mehrere Filter, um Events noch präziser zu
steuern. Die wichtigsten sind branches, paths
und tags.
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.
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.
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].
workflow_dispatchDas 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: falseDiese 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:
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:
push: Die Commit-SHA, der Branch-Name, der
Pusherpull_request: PR-Nummer, Titel, Labels, Autorissue_comment: Kommentartext, Issue-NummerDiese 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 }}"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.
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.
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.
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:
Self-Hosted Runners sind Maschinen, die du selbst bereitstellst und verwaltest. Sie bieten maximale Kontrolle, bedeuten aber auch 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!
Self-Hosted Runner können auf Repository-, Organization- oder Enterprise-Ebene registriert werden.
Runner werden über Labels identifiziert. Self-Hosted Runner bekommen automatisch diese Standard-Labels:
self-hostedlinux, windows,
macosx64, arm64Zusätzlich können custom Labels vergeben werden:
./config.sh --labels gpu,high-memoryIm 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.
| 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:
Ein GitHub Actions Workflow ist eine YAML-Datei im Verzeichnis
.github/workflows/. Die grundlegende Struktur folgt einem
festen Schema.
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 testSchauen wir uns die Komponenten im Detail an.
name – der
Workflow-Namename: CI PipelineDer 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”.
on –
Event-Konfigurationon:
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.
jobs – die
ArbeitseinheitenEin 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 buildruns-on –
Runner-AuswahlDie runs-on-Direktive legt fest, auf welchem Runner der
Job laufen soll:
runs-on: ubuntu-latestFü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]steps – die
AktionenSteps 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 -la2. 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).
Steps können auf Umgebungsvariablen und Secrets zugreifen:
steps:
- name: Deploy
run: ./deploy.sh
env:
API_TOKEN: ${{ secrets.API_TOKEN }}
ENVIRONMENT: productionsecrets sind verschlüsselt in den
Repository-Einstellungen gespeichert und werden nie in Logs
angezeigt.
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> }}.
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.
Das Tool soll:
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
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/main/develop und bei PRs gegen
mainactions/checkout@v4 lädt den
Repository-Codeactions/setup-python@v5
installiert Python 3.11pip install holt Linter
(Ruff) und Typen-Checker (mypy)Beachte die Branch-Filter: Der Workflow ignoriert Feature-Branches
bei direkten Pushes, reagiert aber auf PRs gegen main. Das
ist eine gängige Strategie
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 obenMatrix-Strategien behandeln wir ausführlich in Kapitel 2.
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:
Ein Workflow läuft nicht wie erwartet? Die wichtigste Anlaufstelle sind die Workflow-Logs in der GitHub UI:
Logs zeigen Zeitstempel, Stdout/Stderr-Ausgabe, Exit-Codes und Runner-Informationen.
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.
In diesem Kapitel haben wir die konzeptionellen Grundlagen von GitHub Actions gelegt:
push über pull_request bis zu
scheduleon, jobs, runs-on,
steps