CI/CD Pipeline

GitHub Actions Workflows für automatisiertes Testing, Quality Gates und Production Deployment

Kit enthält drei GitHub Actions Workflows, die den gesamten Weg von der Code-Änderung bis zum Production Deployment automatisieren. Jeder Push führt Typprüfung, Linting, Unit-Tests und E2E-Smoke-Tests aus. Merges auf master lösen ein vollständiges Deployment auf Vercel aus.

Workflow-Übersicht

WorkflowDateiAuslöserZweck
Deploy to Productiondeploy.ymlPush auf master, manuellVollständige Test-Suite + Vercel Deployment
Testtest.ymlPRs auf master, Push auf developQuality Gate für Pull Requests
Full E2E Suitee2e-full.ymlTäglich um 2 Uhr UTC, manuellUmfassendes nächtliches Regressions-Testing
Alle drei Workflows verwenden Turborepo Remote Caching, um Builds über Runs hinweg zu beschleunigen. Cache-Treffer können die Build-Zeiten um 50-70% reduzieren.

Production Deployment Workflow

Der deploy.yml-Workflow ist deine Production-Pipeline. Er läuft bei jedem Push auf master und kann auch manuell über den GitHub Actions Tab ausgelöst werden.

Pipeline-Struktur

Der Workflow hat zwei sequenzielle Jobs:
test (Job 1)                      deploy (Job 2)
  ├── Install dependencies          ├── Install dependencies
  ├── Validate customer files       ├── Check required secrets
  ├── Setup test database           ├── Pull Vercel environment
  ├── Generate Prisma client        ├── Build with Vercel CLI
  ├── Type checking                 ├── Deploy to production
  ├── Linting                       ├── Comment on commit
  ├── Unit tests                    └── Create deployment status
  ├── Build application
  └── E2E smoke tests
Der Deploy-Job läuft nur, wenn alle Tests bestehen. Das stellt sicher, dass fehlerhafter Code niemals Production erreicht.

Test-Job

Der Test-Job läuft gegen eine echte PostgreSQL-Datenbank mit der pgvector-Erweiterung (entspricht deinem Production Supabase-Setup):
deploy.yml — Test Job with PostgreSQL Service
test:
    name: Run Tests
    runs-on: ubuntu-latest

    services:
      postgres:
        image: pgvector/pgvector:pg15
        env:
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: testdb
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
        ports:
          - 5432:5432

    steps:
      - uses: actions/checkout@v4

      - uses: pnpm/action-setup@v3
        with:
          version: 10.15.1
Der PostgreSQL-Service-Container verwendet pgvector/pgvector:pg15, das die gleiche Vektor-Erweiterung bietet, die du in Production für RAG-Features verwendest. Das stellt sicher, dass Datenbankabfragen mit Vektoroperationen gegen echtes PostgreSQL-Verhalten getestet werden.

Kundendatei-Validierung

Bevor Tests ausgeführt werden, prüft der Workflow, ob automatisch generierte Kundendateien aktuell sind:
deploy.yml — Customer File Validation (env.test)
- name: Validate customer .env.test is up-to-date
        run: |
          echo "Validating customer .env.test generation..."

          # Check if generated file exists
          if [ ! -f "apps/boilerplate/.env.test" ]; then
            echo "❌ Error: apps/boilerplate/.env.test does not exist"
            echo "This file should be generated and committed to git"
            echo "Run: cd apps/boilerplate && pnpm generate:env-test"
            exit 1
          fi

          # Compare files (must be identical - no transformations)
          if ! diff -q .env.test apps/boilerplate/.env.test > /dev/null 2>&1; then
            echo "❌ Error: apps/boilerplate/.env.test is outdated"
            echo ""
            echo "The root .env.test was updated but the generated customer version wasn't."
            echo "Please run: cd apps/boilerplate && pnpm generate:env-test"
            echo "Then commit the updated apps/boilerplate/.env.test"
            echo ""
            echo "Differences:"
            diff .env.test apps/boilerplate/.env.test || true
            exit 1
          fi

          echo "✅ Customer .env.test is up-to-date"
Die Validierung prüft vier kundenkritische Dateikategorien:
ValidierungWas geprüft wird
.env.testTest-Umgebungsvorlage stimmt mit Root überein
CLAUDE.mdGenerierte Kunden-CLAUDE.md ist aktuell
.gitignoreMonorepo-Muster in der Standalone-Version entfernt
docs/Generierte Docs haben keine Monorepo-Pfad-Referenzen
Falls eine Validierung fehlschlägt, beendet der Workflow mit einer klaren Fehlermeldung, die erklärt, welche Datei neu generiert werden muss, und dem genauen Befehl dazu.

Deploy-Job

Nachdem die Tests bestanden haben, baut der Deploy-Job die Anwendung und deployt sie auf Vercel:
deploy.yml — Vercel Build and Deploy
deploy:
    name: Deploy to Vercel
    needs: test
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Check Required Secrets
        run: |
          echo "Checking for required secrets..."

          if [ -z "${{ secrets.VERCEL_TOKEN }}" ]; then
            echo "❌ Error: VERCEL_TOKEN secret is not set"
            echo "Please add VERCEL_TOKEN to your GitHub repository secrets"
            echo "See docs/DEPLOYMENT_SETUP.md for instructions"
            exit 1
          fi

          if [ -z "${{ secrets.VERCEL_ORG_ID }}" ]; then
            echo "❌ Error: VERCEL_ORG_ID secret is not set"
            echo "Please add VERCEL_ORG_ID to your GitHub repository secrets"
            echo "Required value: team_Bgh9zp308TcrlrYLqGmuGLHN"
            exit 1
          fi

          if [ -z "${{ secrets.VERCEL_PROJECT_ID }}" ]; then
            echo "❌ Error: VERCEL_PROJECT_ID secret is not set"
            echo "Please add VERCEL_PROJECT_ID to your GitHub repository secrets"
            echo "Required value: prj_jf8vKRszL6Xt7IF8EwqxzfnwRXsl"
            exit 1
          fi

          echo "✅ All required secrets are configured"

      - uses: pnpm/action-setup@v3
        with:
          version: 10.15.1

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

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Install Vercel CLI
        run: pnpm add -g vercel@latest

      - name: Pull Vercel Environment Information
        run: |
          echo "Pulling Vercel environment configuration..."
          vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }}
          echo "✅ Successfully pulled Vercel configuration"

      - name: Build Project Artifacts
        run: |
          echo "Building boilerplate app for production..."
          vercel build --prod --token=${{ secrets.VERCEL_TOKEN }}
          echo "✅ Build completed successfully"

      - name: Deploy to Vercel
        id: deploy
        run: |
          echo "Deploying to Vercel production..."
          # --archive=tgz compresses files before upload to avoid rate limits (>5000 files)
          DEPLOYMENT_URL=$(vercel deploy --prebuilt --prod --archive=tgz --token=${{ secrets.VERCEL_TOKEN }})
          echo "deployment-url=$DEPLOYMENT_URL" >> $GITHUB_OUTPUT
          echo "✅ Successfully deployed to: $DEPLOYMENT_URL"
Wichtige Implementierungsdetails:
  • vercel pull lädt die Umgebungskonfiguration von Vercel herunter und stellt sicher, dass der Build dieselben Variablen wie Production verwendet
  • vercel build --prod baut die Anwendung lokal in CI (schneller als das Bauen auf Vercels Servern)
  • --archive=tgz komprimiert die Build-Ausgabe vor dem Upload. Das ist entscheidend für Monorepo-Projekte mit 5.000+ Dateien — ohne diese Option können Vercels API-Rate-Limits zu Upload-Fehlern führen
  • Commit-Kommentare bieten direkte Links zur Deployment-URL für schnelle Überprüfung

Erforderliche GitHub Secrets

Konfiguriere diese Secrets in deinem GitHub-Repository unter Settings > Secrets and variables > Actions:
SecretZweckWo zu finden
VERCEL_TOKENAuthentifiziert Vercel CLIVercel Dashboard > Settings > Tokens
VERCEL_ORG_IDIdentifiziert dein Vercel-TeamVercel Dashboard > Settings > General
VERCEL_PROJECT_IDIdentifiziert dein Vercel-ProjektVercel Dashboard > Project Settings > General
TURBO_TOKENTurborepo Remote Cache Authvercel.com/account/tokens
TURBO_TEAMTurborepo Team-BezeichnerDein Vercel Team Slug

PR-Validierungs-Workflow

Der test.yml-Workflow läuft bei jedem Pull Request auf master und bei Pushes auf develop. Er fungiert als Quality Gate — PRs können nicht gemergt werden, bis alle Checks bestehen.

PR-Pipeline-Struktur

test (Single Job)
  ├── Install dependencies
  ├── Setup PostgreSQL + pgvector
  ├── Generate Prisma client
  ├── Push database schema
  ├── Type checking
  ├── Linting
  ├── Unit tests
  ├── Validate customer files
  ├── E2E smoke tests
  └── Upload test artifacts

Test-Artefakt-Upload

Nach jedem Durchlauf lädt der Workflow Test-Ergebnisse mit 30-tägiger Aufbewahrung hoch:
test.yml — Artifact Upload
- name: Upload test results
        uses: actions/upload-artifact@v4
        if: always()
        with:
          name: test-results
          path: |
            coverage/
            playwright-report/
          retention-days: 30
Artefakte umfassen:
  • coverage/ — Code-Coverage-Berichte von Vitest (HTML, JSON, Text)
  • playwright-report/ — E2E-Testergebnisse mit Screenshots und Traces
Lade diese aus dem Bereich Actions > Artifacts in GitHub herunter, wenn du Fehler debuggst.

Nächtliche E2E-Suite

Der e2e-full.yml-Workflow führt die vollständige E2E-Test-Suite täglich um 2:00 Uhr UTC aus. Das erkennt Regressionen, die Smoke-Tests möglicherweise verpassen.

Was ausgeführt wird

e2e-full.yml — Schedule and Trigger
name: Full E2E Test Suite

on:
  # Run daily at 2:00 AM UTC
  schedule:
    - cron: '0 2 * * *'
  # Allow manual trigger
  workflow_dispatch:
AspektWert
ZeitplanTäglich um 2:00 Uhr UTC
TestsVollständige Suite auf Chromium
Timeout30 Minuten
Quality GatesTypprüfung + Lint + Unit-Tests + vollständige E2E

Automatische Issue-Erstellung

Wenn die nächtliche Suite fehlschlägt, erstellt der Workflow automatisch ein GitHub Issue mit den Fehlerdetails:
e2e-full.yml — Auto-Create Issue on Failure
- name: Create issue on failure
        if: failure()
        uses: actions/github-script@v7
        with:
          script: |
            const title = '🔴 Full E2E Test Suite Failed';
            const body = `
            The scheduled full E2E test suite has failed.

            **Workflow Run:** ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
            **Triggered by:** ${{ github.event_name }}
            **Branch:** ${{ github.ref_name }}
            **Commit:** ${{ github.sha }}

            Please review the test results and fix the failing tests.

            [View Playwright Report](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})
            `;

            // Check if there's already an open issue
            const issues = await github.rest.issues.listForRepo({
              owner: context.repo.owner,
              repo: context.repo.repo,
              state: 'open',
              labels: 'e2e-test-failure'
            });

            if (issues.data.length === 0) {
              await github.rest.issues.create({
                owner: context.repo.owner,
                repo: context.repo.repo,
                title: title,
                body: body,
                labels: ['e2e-test-failure', 'automated']
              });
            }
Das Issue enthält:
  • Direkten Link zum fehlgeschlagenen Workflow-Durchlauf
  • Branch und Commit-SHA zum Debuggen
  • Link zum Playwright-Report-Artefakt
  • Labels (e2e-test-failure, automated) zum Filtern
Es wird nur ein Issue erstellt — falls bereits ein offenes Issue mit dem Label e2e-test-failure existiert, überspringt der Workflow die Erstellung, um Duplikate zu vermeiden.

Quality Gates Zusammenfassung

Jeder Code-Pfad muss diese Gates passieren, bevor er Production erreicht:
GateWorkflowBlockiert
TypeScript-Kompilierungdeploy.yml, test.ymlTypfehler
ESLintdeploy.yml, test.ymlCode-Qualitätsverstöße
Unit-Tests (800+)deploy.yml, test.ymlLogik-Regressionen
E2E-Smoke-Tests (19)deploy.yml, test.ymlKritische Flow-Fehler
Kundendatei-Validierungdeploy.yml, test.ymlVeraltete generierte Dateien
Vollständige E2E-Suite (186)e2e-full.ymlUmfassende Regressionen

Turborepo Remote Caching

Alle Workflows verwenden Turborepo Remote Caching, um Build-Artefakte über CI-Runs hinweg zu teilen. Das wird über zwei Umgebungsvariablen konfiguriert:
yaml
env:
  TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
  TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
Wenn ein vorheriger Durchlauf denselben Code bereits gebaut hat, lädt Turborepo das gecachte Ergebnis herunter, anstatt neu zu bauen. Das bringt die größten Einsparungen bei:
  • Typprüfung — TypeScript-Kompilierungsergebnisse werden gecacht
  • Linting — ESLint-Ergebnisse werden gecacht
  • Building — Next.js-Build-Ausgabe wird gecacht

Eigene Workflow-Schritte hinzufügen

Um eigene Schritte zur Pipeline hinzuzufügen, bearbeite die Workflow-Dateien in .github/workflows/. Häufige Ergänzungen:

Lighthouse-Audit hinzufügen

yaml
- name: Run Lighthouse CI
  uses: treosh/lighthouse-ci-action@v11
  with:
    urls: |
      ${{ steps.deploy.outputs.deployment-url }}
    budgetPath: ./lighthouse-budget.json
    uploadArtifacts: true

Datenbank-Migration hinzufügen

yaml
- name: Run database migrations
  run: pnpm --filter=@nextsaasai/boilerplate prisma migrate deploy
  env:
    DATABASE_URL: ${{ secrets.DATABASE_URL }}

Slack-Benachrichtigung hinzufügen

yaml
- name: Notify Slack
  if: success()
  uses: slackapi/slack-github-action@v1
  with:
    payload: |
      {"text": "Deployed to production: ${{ steps.deploy.outputs.deployment-url }}"}
  env:
    SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}

Fehlerbehebung

Tests bestehen lokal, schlagen aber in CI fehl

Die häufigsten Ursachen sind:
  1. Fehlende Umgebungsvariablen — CI verwendet apps/boilerplate/.env.test, während lokal apps/boilerplate/.env.local verwendet wird. Stelle sicher, dass alle für Tests benötigten Variablen in .env.test vorhanden sind
  2. Timing-Probleme — CI-Runner sind langsamer als lokale Maschinen. Falls Tests fest codierte Timeouts verwenden, erhöhe diese oder nutze Playwrights eingebaute waitFor-Muster
  3. Datenbankzustand — CI pusht ein frisches Schema mit prisma db push. Stelle sicher, dass deine Tests nicht von Seed-Daten abhängen, die nur lokal existieren

Cache-bedingte Build-Fehler

Falls ein Build nach einem Dependency-Update mit unerwarteten Fehlern fehlschlägt:
bash
# In deinem Workflow, vor install hinzufügen:
- name: Clear Turborepo cache
  run: pnpm turbo run build --force
Das umgeht den Remote-Cache für einen einzelnen Durchlauf. Falls der Build erfolgreich ist, war der alte Cache veraltet.

Deployment erfolgreich, aber Website zeigt Fehler

  1. Prüfe, ob alle Umgebungsvariablen im Production-Scope von Vercel gesetzt sind
  2. Vergewissere dich, dass Datenbank-Migrationen angewendet wurden: prisma migrate deploy
  3. Prüfe die Vercel Functions-Logs auf Laufzeitfehler