Developer Tools

GitHub Actions CI/CD: Build-, Test- und Deploy-Workflows von Grund auf

Lerne, wie du GitHub Actions für Continuous Integration und Deployment einrichtest. Behandelt Trigger, Jobs, Matrizen, Secrets, Caching, Docker und praxisnahe Workflow-Beispiele.

10 Min. Lesezeit

GitHub-Code-Zusammenarbeit auf dem Bildschirm

GitHub Actions verwandelt jeden Push, Pull Request und Merge in eine automatisierte Pipeline – Tests werden ausgeführt, Artefakte gebaut, Sicherheitslücken gescannt und Deployments in die Produktion durchgeführt. Es ist die CI/CD-Plattform, die direkt in deinem Repository lebt, ohne jegliche Infrastruktur, die du verwalten müsstest.

Wie GitHub Actions funktioniert

Jeder Workflow ist eine YAML-Datei in .github/workflows/. Wenn ein Trigger-Ereignis ausgelöst wird, startet GitHub eine neue virtuelle Maschine, führt deine Jobs aus und meldet die Ergebnisse – alles innerhalb von Sekunden.

Push to main
  ↓
Workflow triggered
  ↓
Job: Build & Test (ubuntu-latest)
  ├── Step 1: Checkout code
  ├── Step 2: Install Node.js
  ├── Step 3: npm ci
  ├── Step 4: npm test
  └── Step 5: npm run build
  ↓
✅ All checks passed

Dein erster Workflow

Erstelle .github/workflows/ci.yml:

name: CI

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

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: npm
      - run: npm ci
      - run: npm test
      - run: npm run build

Das war's. Jeder Push auf main und jeder PR führt nun automatisch deine Test-Suite aus.

Trigger: Wann Workflows ausgeführt werden

Code-Ereignisse

on:
  push:
    branches: [main, develop]
    paths: ['src/**', 'package.json']  # Only run when these change
  pull_request:
    types: [opened, synchronize]

Geplant (cron)

on:
  schedule:
    - cron: '0 6 * * 1'  # Every Monday at 6:00 UTC

Manueller Dispatch

on:
  workflow_dispatch:
    inputs:
      environment:
        description: 'Deploy target'
        required: true
        type: choice
        options: [staging, production]

Dies fügt in der GitHub-Oberfläche eine Schaltfläche „Run workflow" hinzu, über die du Parameter auswählen kannst.

Wiederverwendbare Workflows

on:
  workflow_call:
    inputs:
      node-version:
        type: string
        default: '20'

Rufe ihn aus einem anderen Workflow mit uses: ./.github/workflows/reusable.yml auf.

Jobs und Steps

Parallele Jobs

Jobs werden standardmäßig parallel ausgeführt:

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm run lint

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

  build:
    runs-on: ubuntu-latest
    needs: [lint, test]  # Waits for both to pass
    steps:
      - uses: actions/checkout@v4
      - run: npm run build

Matrix-Strategie – testen über mehrere Versionen

Denselben Job über mehrere Konfigurationen hinweg ausführen:

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [18, 20, 22]
        os: [ubuntu-latest, windows-latest]
      fail-fast: false
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
      - run: npm ci
      - run: npm test

Dies erzeugt 6 parallele Jobs (3 Versionen × 2 Betriebssysteme).

Secrets und Umgebungsvariablen

Secrets verwenden

Sensible Werte unter Settings → Secrets and variables → Actions speichern:

steps:
  - name: Deploy
    env:
      API_KEY: ${{ secrets.API_KEY }}
      DATABASE_URL: ${{ secrets.DATABASE_URL }}
    run: ./deploy.sh

Secrets werden in Logs maskiert und niemals an Forks weitergegeben.

Umgebungsvariablen

# Workflow-level
env:
  NODE_ENV: production

jobs:
  build:
    # Job-level
    env:
      CI: true
    steps:
      - name: Build
        # Step-level
        env:
          VITE_API_URL: https://api.example.com
        run: npm run build

Caching für schnellere Builds

Ohne Caching lädt jeder Durchlauf alle Abhängigkeiten neu herunter. Caching reduziert die Build-Zeiten um 50–80 %:

- uses: actions/setup-node@v4
  with:
    node-version: 20
    cache: npm  # Built-in npm caching

# Or manual caching for other tools
- uses: actions/cache@v4
  with:
    path: ~/.cache/pip
    key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }}
    restore-keys: |
      ${{ runner.os }}-pip-

Artefakte: Daten zwischen Jobs teilen

Build-Ausgaben aus einem Job hochladen und in einem anderen herunterladen:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm run build
      - uses: actions/upload-artifact@v4
        with:
          name: dist
          path: dist/

  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/download-artifact@v4
        with:
          name: dist
      - run: ./deploy.sh

Praxisnahe Workflow-Beispiele

Docker Build und Push

name: Docker

on:
  push:
    tags: ['v*']

permissions:
  packages: write

jobs:
  docker:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - uses: docker/build-push-action@v5
        with:
          push: true
          tags: ghcr.io/${{ github.repository }}:${{ github.ref_name }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

Deployment auf Cloudflare Pages

name: Deploy

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: npm
      - run: npm ci && npm run build
      - uses: cloudflare/wrangler-action@v3
        with:
          apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          command: pages deploy dist --project-name=my-site

Release mit Changelog

name: Release

on:
  push:
    tags: ['v*.*.*']

permissions:
  contents: write

jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Generate changelog
        run: |
          git log $(git describe --tags --abbrev=0 HEAD^)..HEAD \
            --pretty=format:"- %s" > CHANGELOG.md
      - uses: softprops/action-gh-release@v2
        with:
          body_path: CHANGELOG.md
          generate_release_notes: true

Bedingte Ausführung

Steuern, wann Steps und Jobs ausgeführt werden:

steps:
  - name: Deploy to production
    if: github.ref == 'refs/heads/main'
    run: ./deploy-prod.sh

  - name: Deploy to staging
    if: github.event_name == 'pull_request'
    run: ./deploy-staging.sh

  - name: Notify on failure
    if: failure()
    run: curl -X POST ${{ secrets.SLACK_WEBHOOK }} -d '{"text":"Build failed!"}'

Sicherheits-Best-Practices

1. Action-Versionen auf SHA pinnen

# ❌ Mutable tag — could be compromised
- uses: actions/checkout@v4

# ✅ Pinned to exact commit SHA
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11

2. Minimale Berechtigungen verwenden

permissions:
  contents: read    # Only what's needed
  packages: write

3. Secrets niemals ausgeben

# ❌ NEVER do this
- run: echo ${{ secrets.API_KEY }}

# ✅ Use environment variables
- env:
    API_KEY: ${{ secrets.API_KEY }}
  run: ./script-that-uses-api-key.sh

4. Berechtigungen für PRs aus Forks einschränken

on:
  pull_request_target:  # Runs in context of base branch
    types: [opened, synchronize]

Fehlgeschlagene Workflows debuggen

  1. Logs prüfen – Klicke auf einen fehlgeschlagenen Step, um die vollständige Ausgabe zu sehen
  2. Debug-Logging aktivieren – Setze das Secret ACTIONS_STEP_DEBUG auf true
  3. act lokal nutzen – Workflows auf deiner Maschine ausführen mit nektos/act
  4. Per SSH in den Runner einloggenmxschmitt/action-tmate für interaktives Debugging verwenden

Workflows visuell erstellen

Du möchtest kein YAML von Hand schreiben? Nutze unseren GitHub Actions Generator, um Workflows visuell mit Templates für Node.js, Python, Docker, Deployments und mehr zu erstellen – und lade dann produktionsfertige YAML-Dateien herunter.

Fazit

GitHub Actions bietet dir CI/CD, das direkt in deinem Repository lebt:

  1. Einfach starten – Eine Workflow-Datei mit Build + Test
  2. Caching hinzufügen – Build-Zeiten mit actions/cache oder integriertem Sprach-Caching reduzieren
  3. Matrix nutzen – Über Versionen und Betriebssysteme hinweg testen
  4. Secrets schützen – Nie hardcoden, immer verschlüsselte Secrets verwenden
  5. Alles automatisieren – Releases, Deployments, Sicherheitsscans, geplante Aufgaben

Die beste CI/CD-Pipeline ist die, die bei jedem Commit läuft, ohne dass du daran denken musst. Richte sie einmal ein und überlass den Rest GitHub.