rebreak-monorepo/docs/internal/CI_CD_DEPLOYMENT_WORKFLOW.md

14 KiB

CI/CD Deployment Workflow

Diese Dokumentation beschreibt den kompletten Deployment-Workflow für das rebreak-monorepo nach der Migration weg von GitHub Actions hin zu einem selbstgehosteten Setup mit Gitea und Woodpecker CI.

TL;DR


Infrastruktur-Übersicht

┌─────────────────┐     push      ┌──────────────────┐
│  Entwickler     │ ────────────► │  Gitea           │
│  (lokal)        │               │  git.rebreak.org │
└─────────────────┘               └────────┬─────────┘
                                           │
                           Webhook (push)  │  Woodpecker CI
                                           │  (pull + build)
                                           ▼
                              ┌─────────────────────┐
                              │  Woodpecker Server  │
                              │  ci.rebreak.org     │
                              └──────────┬──────────┘
                                         │
                                         │ gRPC
                                         ▼
                              ┌─────────────────────┐
                              │  Woodpecker Agent   │
                              │  raynis-builder     │
                              │  128.140.47.53      │
                              └──────────┬──────────┘
                                         │
                                         │ scp + ssh
                                         ▼
                              ┌─────────────────────┐
                              │  Staging-Server     │
                              │  staging.rebreak.org│
                              └─────────────────────┘

Server

Server IP / Domain Rolle
raynis-builder 128.140.47.53, api.trucko.org Gitea, Woodpecker, Build-Agent
staging.rebreak.org 91.99.225.223 Ziel für Staging-Deploys, Webhook-Listener

Dienste auf raynis-builder

Dienst Container Port (intern) Extern
Gitea gitea 3000 https://git.rebreak.org
Woodpecker Server woodpecker-server 8000 https://ci.rebreak.org
Woodpecker Agent woodpecker-agent
Postgres gitea-db 5432

Pfade auf raynis-builder

  • Gitea + Woodpecker Stack: /mnt/HC_Volume_103985481/gitea/
  • Deploy-SSH-Key: /home/runner/.ssh/rebreak-deploy
  • Gitea-Daten: /mnt/HC_Volume_103985481/gitea/data/
  • Woodpecker-Daten: /mnt/HC_Volume_103985481/gitea/woodpecker-server/

Gitea

Zugang

  • URL: https://git.rebreak.org
  • Admin-User: chahine
  • Repos:
    • chahine/hello-ci (Test-Repo)
    • chahine/rebreak-monorepo (Hauptrepo)

SSH-Zugriff

Gitea-SSH läuft auf Port 2222:

git clone ssh://git@git.rebreak.org:2222/chahine/rebreak-monorepo.git

Für HTTPS:

git clone https://git.rebreak.org/chahine/rebreak-monorepo.git

Deploy-Key

  • Auf dem Staging-Server liegt /home/runner/.ssh/rebreak-deploy
  • Der Public Key (rebreak-deploy.pub) ist in Gitea als Deploy-Key für chahine/rebreak-monorepo hinterlegt
  • Wird vom Staging-Server verwendet, um Code-Updates von Gitea zu pullen

Woodpecker CI

Zugang

Pipeline-Definition

Die Pipeline ist in .woodpecker.yml definiert:

when:
  - event: push
    branch: main
  - event: pull_request

steps:
  install:
    image: node:24-slim
    commands:
      - corepack enable
      - corepack prepare pnpm@10.23.0 --activate
      - pnpm install --frozen-lockfile

  build-backend:
    image: node:24-slim
    commands:
      - corepack enable
      - corepack prepare pnpm@10.23.0 --activate
      - cd backend && NODE_OPTIONS=--max-old-space-size=4096 pnpm build
    depends_on: [install]

  build-admin:
    image: node:24-slim
    commands:
      - corepack enable
      - corepack prepare pnpm@10.23.0 --activate
      - cd apps/admin && pnpm build
    depends_on: [install]

  deploy-backend:
    image: alpine:3.21
    commands:
      - apk add --no-cache openssh-client
      - mkdir -p ~/.ssh
      - cp /root/ssh-keys/rebreak-deploy ~/.ssh/id_ed25519
      - chmod 600 ~/.ssh/id_ed25519
      - ssh-keyscan -H staging.rebreak.org > ~/.ssh/known_hosts 2>/dev/null
      - tar czf backend-output.tar.gz -C backend/.output .
      - scp -i ~/.ssh/id_ed25519 backend-output.tar.gz root@staging.rebreak.org:/srv/rebreak/backend/.output-incoming.tar.gz
      - ssh -i ~/.ssh/id_ed25519 root@staging.rebreak.org 'bash /srv/rebreak/scripts/deploy-from-artifact.sh'
    depends_on: [build-backend]
    when:
      - event: push
        branch: main

  deploy-admin:
    image: alpine:3.21
    commands:
      - apk add --no-cache openssh-client
      - mkdir -p ~/.ssh
      - cp /root/ssh-keys/rebreak-deploy ~/.ssh/id_ed25519
      - chmod 600 ~/.ssh/id_ed25519
      - ssh-keyscan -H staging.rebreak.org > ~/.ssh/known_hosts 2>/dev/null
      - tar czf admin-output.tar.gz -C apps/admin/.output .
      - scp -i ~/.ssh/id_ed25519 admin-output.tar.gz root@staging.rebreak.org:/srv/rebreak/apps/admin/.output-incoming.tar.gz
      - ssh -i ~/.ssh/id_ed25519 root@staging.rebreak.org 'bash /srv/rebreak/scripts/deploy-admin-from-artifact.sh'
    depends_on: [build-admin]
    when:
      - event: push
        branch: main

Wichtige Details

  • Build läuft in node:24-slim-Containern auf dem Woodpecker-Agent.
  • Deploy läuft in alpine:3.21-Containern.
  • Der SSH-Key wird über WOODPECKER_BACKEND_DOCKER_VOLUMES in jeden Pipeline-Container gemountet:
    • Host-Pfad: /home/runner/.ssh
    • Container-Pfad: /root/ssh-keys
  • Deploy-Steps laufen nur bei push auf main.
  • Pull Requests werden gebaut, aber nicht deployed.

Agent-Konfiguration

Wichtige Env-Variablen in /mnt/HC_Volume_103985481/gitea/docker-compose.yml:

woodpecker-agent:
  environment:
    - WOODPECKER_SERVER=woodpecker-server:9000
    - WOODPECKER_AGENT_SECRET=${WOODPECKER_AGENT_SECRET}
    - WOODPECKER_BACKEND_DOCKER_VOLUMES=/home/runner/.ssh:/root/ssh-keys:ro

WOODPECKER_BACKEND_DOCKER_VOLUMES mountet den Host-SSH-Key in alle Pipeline-Container.


Staging-Deploy-Mechanismus

Primärer Pfad: Woodpecker CI

  1. Push auf main in Gitea
  2. Gitea benachrichtigt Woodpecker
  3. Woodpecker startet die Pipeline
  4. Pipeline baut Backend und Admin
  5. Pipeline kopiert Artifacts per scp auf staging.rebreak.org
  6. Auf dem Staging-Server werden diese Scripts ausgeführt:
    • /srv/rebreak/scripts/deploy-from-artifact.sh (Backend)
    • /srv/rebreak/scripts/deploy-admin-from-artifact.sh (Admin)
  7. Die Scripts extrahieren das Artifact und starten die pm2-Prozesse neu

Legacy-Fallback: Gitea-Webhook

Ein zusätzlicher Webhook in Gitea triggert den alten Webhook-Listener auf dem Staging-Server:

  • Webhook-URL: https://staging.rebreak.org/webhook
  • Listener: scripts/deploy-webhook/server.mjs (Port 9000, reverse-proxied via Nginx)
  • Deploy-Script: scripts/deploy.sh
  • Funktion:
    • Validiert HMAC-SHA256-Signatur
    • Prüft, ob Branch main ist
    • Startet scripts/deploy.sh
    • Führt git pull, pnpm install, prisma migrate deploy (falls nötig), Build und pm2-Restart aus

Warum zwei Pfade?

  • Woodpecker ist der bevorzugte Pfad: Build auf leistungsfähigem Server, Artifact-Deploy.
  • Webhook ist ein Legacy-Fallback, der z.B. Migrationen direkt auf dem Staging-Server ausführt.
  • Der .deploy-ga.lock verhindert, dass beide gleichzeitig laufen.

Signatur-Validierung

Der Listener unterstützt beide Formate:

  • GitHub: x-hub-signature-256: sha256=...
  • Gitea: x-gitea-signature: ... (nur Hex, kein sha256= Präfix)

Secret: GITHUB_WEBHOOK_SECRET aus /etc/environment auf dem Staging-Server.


SSH-Key / Secrets

rebreak-deploy Key

  • Privater Key: /home/runner/.ssh/rebreak-deploy auf raynis-builder
  • Public Key: /home/runner/.ssh/rebreak-deploy.pub
  • Fingerprint: SHA256:Wkw1O4YGEM9q++dbCd3+CjAfILtjhPasE71wG5wnH4Q
  • Verwendung:
    • Woodpecker-Deploy-Steps verwenden ihn für scp/ssh zum Staging-Server
    • Staging-Server pullt damit Code von Gitea (Deploy-Key im Repo)
  • Auf Staging-Server authorisiert: Ja, in ~root/.ssh/authorized_keys

Gitea-OAuth-App für Woodpecker

  • Name: Woodpecker CI
  • Type: confidential_client
  • Redirect URI: https://ci.rebreak.org/authorize
  • Scopes: Standard (keine expliziten nötig)

Woodpecker Secrets

  • staging_deploy_key wurde ursprünglich in Woodpecker hinterlegt, wird aber nicht mehr verwendet.
  • Der Key wird stattdessen über WOODPECKER_BACKEND_DOCKER_VOLUMES gemountet.

Wichtige Dateien

Datei Zweck
.woodpecker.yml Pipeline-Definition für Woodpecker
scripts/deploy-webhook/server.mjs Webhook-Listener auf Staging-Server
scripts/deploy.sh Legacy Deploy-Script (inkl. Prisma-Migrationen)
scripts/deploy-from-artifact.sh Artifact-Deploy für Backend
scripts/deploy-admin-from-artifact.sh Artifact-Deploy für Admin
ecosystem.config.js pm2-Konfiguration auf Staging-Server
/mnt/HC_Volume_103985481/gitea/docker-compose.yml Gitea + Woodpecker Stack

Prisma-Migrationen

Migrationen werden an zwei Stellen geprüft:

  1. In scripts/deploy-from-artifact.sh (Woodpecker-Deploy):

    • Vergleicht .last-deployed-sha mit HEAD
    • Führt prisma migrate deploy aus, wenn sich etwas unter backend/prisma/migrations/ oder backend/prisma/schema.prisma geändert hat
  2. In scripts/deploy.sh (Webhook-Deploy):

    • Gleicher Mechanismus
    • Wird als Fallback ausgeführt

⚠️ Wichtig: Migrationen werden nur ausgeführt, wenn sich die Prisma-Dateien seit dem letzten Deploy geändert haben. Bei manuellem Eingriff muss ggf. .last-deployed-sha gelöscht werden.


Betrieb: Pipelines ansehen

Woodpecker-Logs

ssh root@128.140.47.53
docker logs woodpecker-server --tail 100
docker logs woodpecker-agent --tail 100

Pipeline-Status in DB

sqlite3 /mnt/HC_Volume_103985481/gitea/woodpecker-server/woodpecker.sqlite \
  "SELECT id, repo_id, number, status, message FROM pipelines WHERE repo_id=2 ORDER BY id DESC LIMIT 5;"

Webhook-Logs auf Staging

ssh root@staging.rebreak.org
pm2 logs rebreak-webhook --lines 50

Troubleshooting

Pipeline-Clone schlägt fehl mit Submodule-Fehler

Ursache: Verwaister Submodule-Eintrag ohne .gitmodules. Lösung:

git rm --cached <pfad-zum-submodule>
echo '<pfad-zum-submodule>/' >> .gitignore
git commit -m "Remove broken submodule entry"

Deploy-Step: "Load key ... error in libcrypto"

Ursache: Alpine-OpenSSH konnte das Key-Format nicht lesen. Lösung: image: alpine:3.21 statt image: alpine verwenden.

"Permission denied (publickey)" beim Deploy

Ursache: SSH-Key nicht im Container verfügbar oder nicht auf Staging authorisiert. Prüfung:

# Ist der Key auf Staging authorisiert?
ssh -i /home/runner/.ssh/rebreak-deploy root@staging.rebreak.org 'echo OK'

# Ist WOODPECKER_BACKEND_DOCKER_VOLUMES gesetzt?
docker inspect woodpecker-agent --format '{{json .Config.Env}}'

Webhook gibt 401

Ursache: Signatur stimmt nicht. Prüfung:

  • Ist GITHUB_WEBHOOK_SECRET in /etc/environment auf Staging gesetzt?
  • Stimmt das Secret im Gitea-Webhook?
  • Verwendet Gitea den richtigen Signatur-Header? Der Listener akzeptiert sowohl x-hub-signature-256 (GitHub) als auch x-gitea-signature (Gitea).

Woodpecker zeigt keine Repos

Ursache: OAuth-App falsch konfiguriert oder User nicht angemeldet. Lösung:

  • Auf https://ci.rebreak.org ausloggen und wieder einloggen
  • In Gitea unter Settings → Applications prüfen, ob die OAuth-App existiert
  • OAuth-App muss confidential_client: true sein

Zwei Deploys kollidieren

Der .deploy-ga.lock verhindert parallele Deploys. Wenn ein Deploy hängt:

ssh root@staging.rebreak.org
rm -f /srv/rebreak/.deploy-ga.lock

Migration von GitHub

Was wurde gemacht?

  1. Repo auf Gitea erstellt
  2. Code von GitHub nach Gitea gepusht
  3. Woodpecker mit Gitea verbunden
  4. .woodpecker.yml erstellt
  5. Staging-Server remote auf Gitea umgestellt
  6. Gitea-Webhook für Legacy-Deploy eingerichtet
  7. Webhook-Listener für Gitea-Signaturen angepasst

Was noch zu tun ist?

  • Lokale Entwickler-Repos sollten den origin-Remote auf Gitea umstellen:
git remote set-url origin https://git.rebreak.org/chahine/rebreak-monorepo.git
# oder SSH:
git remote set-url origin ssh://git@git.rebreak.org:2222/chahine/rebreak-monorepo.git
  • Falls GitHub komplett abgeschaltet werden soll, den GitHub-Remote entfernen und keinen git push origin main mehr ausführen.
  • Der staging_deploy_key-Secret in Woodpecker kann gelöscht werden (wird nicht mehr verwendet).

Kontakte / Verantwortlichkeiten

  • Infrastruktur: raynis-builder (Hetzner VPS)
  • Gitea / Woodpecker Admin: chahine
  • Staging-Server: staging.rebreak.org

Letzte Aktualisierung: 2026-06-18