GitHub Actions ermöglicht CI/CD direkt in GitHub-Repositories. Workflows werden in YAML definiert und bei Events wie Push oder Pull Request ausgeführt.

Grundlagen

Workflow-Struktur

# .github/workflows/ci.yml

name: CI Pipeline

on: [push, pull_request]

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

Konzepte

| Begriff | Beschreibung | |---------|--------------| | Workflow | Automatisierter Prozess | | Job | Gruppe von Steps | | Step | Einzelne Aktion | | Action | Wiederverwendbare Einheit | | Runner | Ausführungsumgebung |

Trigger (Events)

Push und Pull Request

on:
  push:
    branches: [main, develop]
    paths:
      - 'src/**'
      - 'package.json'
  pull_request:
    branches: [main]

Scheduled (Cron)

on:
  schedule:
    - cron: '0 6 * * *'  # Täglich um 06:00 UTC

Manual (workflow_dispatch)

on:
  workflow_dispatch:
    inputs:
      environment:
        description: 'Deploy environment'
        required: true
        default: 'staging'
        type: choice
        options:
          - staging
          - production

Release

on:
  release:
    types: [published]

Jobs und Steps

Mehrere Jobs

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

  build:
    runs-on: ubuntu-latest
    needs: test  # Wartet auf test-Job
    steps:
      - uses: actions/checkout@v4
      - run: npm run build

  deploy:
    runs-on: ubuntu-latest
    needs: [test, build]  # Wartet auf beide
    steps:
      - run: echo "Deploying..."

Matrix-Builds

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

Bedingte Ausführung

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

  test:
    runs-on: ubuntu-latest
    steps:
      - run: echo "Always runs"
      - run: echo "Only on failure"
        if: failure()
      - run: echo "Always, even on cancel"
        if: always()

Umgebungsvariablen

Definition

env:
  NODE_ENV: production

jobs:
  build:
    runs-on: ubuntu-latest
    env:
      DATABASE_URL: ${{ secrets.DATABASE_URL }}
    steps:
      - name: Print env
        run: echo $NODE_ENV
        env:
          STEP_VAR: value

GitHub Context

steps:
  - name: Info
    run: |
      echo "Repository: ${{ github.repository }}"
      echo "Branch: ${{ github.ref_name }}"
      echo "SHA: ${{ github.sha }}"
      echo "Actor: ${{ github.actor }}"
      echo "Event: ${{ github.event_name }}"

Secrets

Secrets verwenden

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Deploy
        run: |
          echo "${{ secrets.SSH_KEY }}" > key.pem
          chmod 600 key.pem
          ssh -i key.pem user@server deploy.sh
        env:
          API_KEY: ${{ secrets.API_KEY }}

GITHUB_TOKEN

jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Create Release
        uses: actions/create-release@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          tag_name: ${{ github.ref }}

Beliebte Actions

Checkout

- uses: actions/checkout@v4
  with:
    fetch-depth: 0  # Vollständige Historie
    submodules: true

Setup Node.js

- uses: actions/setup-node@v4
  with:
    node-version: '20'
    cache: 'npm'

Setup Python

- uses: actions/setup-python@v5
  with:
    python-version: '3.12'
    cache: 'pip'

Docker Build & Push

- uses: docker/setup-buildx-action@v3

- 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 }}:latest

Cache

- uses: actions/cache@v4
  with:
    path: ~/.npm
    key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
    restore-keys: |
      ${{ runner.os }}-node-

Praktische Workflows

Node.js CI

name: Node.js CI

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

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Run linter
        run: npm run lint

      - name: Run tests
        run: npm test

      - name: Build
        run: npm run build

      - name: Upload artifact
        uses: actions/upload-artifact@v4
        with:
          name: build
          path: dist/

Docker Deploy

name: Deploy

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          push: true
          tags: |
            user/app:latest
            user/app:${{ github.sha }}

      - name: Deploy to server
        uses: appleboy/ssh-action@v1.0.0
        with:
          host: ${{ secrets.HOST }}
          username: ${{ secrets.USERNAME }}
          key: ${{ secrets.SSH_KEY }}
          script: |
            docker pull user/app:latest
            docker-compose up -d

Release Workflow

name: Release

on:
  push:
    tags:
      - 'v*'

jobs:
  release:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Build
        run: npm run build

      - name: Create Release
        uses: softprops/action-gh-release@v1
        with:
          files: |
            dist/*.js
            dist/*.css
          generate_release_notes: true

Pull Request Checks

name: PR Check

on:
  pull_request:
    types: [opened, synchronize, reopened]

jobs:
  check:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Check PR title
        run: |
          if [[ ! "${{ github.event.pull_request.title }}" =~ ^(feat|fix|docs|style|refactor|test|chore): ]]; then
            echo "PR title must follow conventional commits"
            exit 1
          fi

      - name: Run tests
        run: npm test

      - name: Comment on PR
        uses: actions/github-script@v7
        if: failure()
        with:
          script: |
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: '❌ Tests failed. Please check the logs.'
            })

Environments

Environment definieren

jobs:
  deploy-staging:
    runs-on: ubuntu-latest
    environment: staging
    steps:
      - run: echo "Deploying to staging"

  deploy-production:
    runs-on: ubuntu-latest
    environment:
      name: production
      url: https://example.com
    needs: deploy-staging
    steps:
      - run: echo "Deploying to production"

Mit Approval

GitHub → Repository → Settings → Environments → production
→ Required reviewers hinzufügen

Self-hosted Runners

Runner installieren

# Download
mkdir actions-runner && cd actions-runner
curl -o actions-runner-linux-x64.tar.gz -L https://github.com/actions/runner/releases/download/v2.311.0/actions-runner-linux-x64-2.311.0.tar.gz
tar xzf ./actions-runner-linux-x64.tar.gz

# Konfigurieren (URL und Token von GitHub)
./config.sh --url https://github.com/user/repo --token TOKEN

# Als Service installieren
sudo ./svc.sh install
sudo ./svc.sh start

Self-hosted Runner verwenden

jobs:
  build:
    runs-on: self-hosted
    steps:
      - uses: actions/checkout@v4
      - run: ./build.sh

Reusable Workflows

Caller Workflow

# .github/workflows/deploy.yml

name: Deploy

on:
  push:
    branches: [main]

jobs:
  call-deploy:
    uses: ./.github/workflows/reusable-deploy.yml
    with:
      environment: production
    secrets:
      ssh_key: ${{ secrets.SSH_KEY }}

Reusable Workflow

# .github/workflows/reusable-deploy.yml

name: Reusable Deploy

on:
  workflow_call:
    inputs:
      environment:
        required: true
        type: string
    secrets:
      ssh_key:
        required: true

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Deploy to ${{ inputs.environment }}
        run: deploy.sh
        env:
          SSH_KEY: ${{ secrets.ssh_key }}

Zusammenfassung

| Event | Beschreibung | |-------|--------------| | push | Bei Push | | pull_request | Bei PR | | schedule | Zeitgesteuert | | workflow_dispatch | Manuell | | release | Bei Release |

| Action | Verwendung | |--------|------------| | actions/checkout | Repository klonen | | actions/setup-node | Node.js einrichten | | actions/cache | Cache verwalten | | docker/build-push-action | Docker Build |

| Context | Beispiel | |---------|----------| | github.ref | refs/heads/main | | github.sha | Commit-SHA | | github.actor | Username | | secrets.NAME | Secret-Wert |

Fazit

GitHub Actions bietet CI/CD direkt in GitHub integriert. Die YAML-Syntax ist gut lesbar, und der Marketplace bietet tausende fertige Actions. Matrix-Builds ermöglichen paralleles Testen verschiedener Konfigurationen. Mit Environments und Approvals wird auch komplexes Deployment Management möglich. Für Projekte auf GitHub ist es oft die einfachste CI/CD-Lösung.