Files
tessera-ctl/docs/ci-cd-setup.md
T
schalli c5f4adeeed
Tessera CI/CD / Lint & Type Check (push) Successful in 45s
Tessera CI/CD / Tests (push) Successful in 1m0s
Tessera CI/CD / Build & Publish Images (push) Successful in 2m51s
docs(quick-260916-dcz): Betriebshandbuch Kapitel 9 (Changelog-Schritt, Gitea-Release, Seite Was ist neu), Anwender-, Entwicklungs- und CI-Handbuch
- Betrieb Kapitel 9: Vorschritt CHANGELOG.md vor dem Tag, automatischer Gitea-Release samt Verhalten bei fehlendem Abschnitt, Erstfreigabe v1.0.0 in der Vergangenheit, vierter Erkennungsweg "Was ist neu"
- Anwender: Satz zur Versionszeile in "Aufbau der Oberflaeche", neuer Abschnitt "Was ist neu" vor den Stolpersteinen, Inhaltsverzeichnis; Abschnitt "Dashboard" (dyv) unangetastet
- Entwicklung: Regel "Aenderungsliste" unter Konventionen und Fallstricke (Bauzeit-Einbettung, Importdisziplin, Kanalregel, Release-Skript)
- CI-Setup (ASCII): REGISTRY_TOKEN mit repository: write, vier Schritte im Job publish, Release je Tag, API-Basis im Job-Container

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018N9CD3ebPKm1b32bPpBknY
2026-09-16 11:25:10 +02:00

9.0 KiB

Tessera CI/CD Setup

Runbook fuer die Einrichtung der Gitea-basierten CI/CD-Pipeline.

Voraussetzungen

  • Docker und Docker Compose installiert
  • Gitea-Instanz mit aktivierten Actions
  • Zugriff auf die Gitea-Administrationsoberflaeche

1. Gitea-Repository einrichten

  1. In Gitea ein neues Repository erstellen (z.B. tessera-ctl)
  2. Das Repository als Git-Remote hinzufuegen:
git remote add origin https://<gitea-url>/<user>/tessera-ctl.git
  1. In den Repository-Einstellungen unter Settings > Actions sicherstellen, dass Actions aktiviert ist.

2. act_runner registrieren

Der act_runner fuehrt Gitea Actions Workflow-Jobs in Docker-Containern aus.

Runner-Token generieren

  1. In Gitea navigieren zu: Repository > Settings > Actions > Runners (oder Admin-Panel > Actions > Runners fuer globale Runner)
  2. Create registration token klicken
  3. Token kopieren

Runner starten

Die Umgebungsvariablen setzen (z.B. in .env im Projektverzeichnis oder direkt in der Shell):

export GITEA_INSTANCE_URL=https://git.vicolab.de
export GITEA_RUNNER_REGISTRATION_TOKEN=<token-aus-schritt-oben>

Runner starten:

docker compose -f docker-compose.ci.yml up -d

Runner-Status pruefen:

docker compose -f docker-compose.ci.yml logs act_runner

Der Runner registriert sich automatisch bei Gitea und ist bereit, Jobs auszufuehren.

Bestehender Runner

Wenn bereits ein act_runner als separater Docker-Container laeuft (z.B. gitea-runner), kann docker-compose.ci.yml als Dokumentation und Vorlage fuer eine Neueinrichtung verwendet werden. Der bestehende Runner muss nicht ersetzt werden.

Bestehenden Runner pruefen:

docker ps --filter name=gitea-runner

3. Erforderliche Konfiguration

Umgebungsvariablen

Variable Beschreibung Wo setzen
GITEA_INSTANCE_URL URL der Gitea-Instanz .env oder Systemumgebung
GITEA_RUNNER_REGISTRATION_TOKEN Runner-Registrierungstoken .env oder Systemumgebung

Gitea Secrets (fuer die CI-Pipeline)

In Gitea unter Repository > Settings > Actions > Secrets werden die Secrets fuer die Pipeline konfiguriert. Benoetigt wird genau eines:

Secret Beschreibung
REGISTRY_TOKEN Gitea-Zugangstoken (Access Token) mit Schreibrecht auf Pakete (package: write) und zusaetzlich auf das Repository (repository: write, fuer Releases). Wird im Job publish fuer docker login localhost:3002 --password-stdin verwendet und im Release-Schritt ueber env als GITEA_TOKEN an .gitea/scripts/publish-release.sh gereicht -- nie als Argument.

Das Token erscheint nie im Log: es wird per --password-stdin uebergeben und Gitea maskiert Secret-Werte in der Job-Ausgabe. Das Veroeffentlichungs-Skript .gitea/scripts/publish-images.sh kennt das Token nicht; der Login bleibt im Workflow.

Der Push geht ueber localhost:3002 (Gitea laeuft auf demselben Rechner wie der Runner), weil der Nginx Proxy Manager vor git.vicolab.de grosse Image-Blobs blockt. Das Pullen auf den Servern laeuft ueber git.vicolab.de (docker-compose.prod.yml).

Weitere Secrets bei Bedarf:

  1. In Gitea Settings > Actions > Secrets den Secret anlegen
  2. In .gitea/workflows/ci.yml ueber ${{ secrets.SECRET_NAME }} referenzieren

Wichtig: Secrets niemals in Logs ausgeben oder in Workflow-Dateien hardcoden.

4. Pipeline-Ueberblick

Die CI/CD-Pipeline (.gitea/workflows/ci.yml) laeuft bei jedem Push auf die Zweige main und live sowie bei jedem Tag v* (z. B. v1.0.0) und besteht aus drei aufeinander aufbauenden Jobs:

  1. quality -- Lint und TypeScript Type-Check (Lint ist derzeit ein Leerlauf, siehe WINDOWS #35; der Type-Check ist echt)
  2. test -- Vitest Unit- und Integrationstests
  3. publish -- Docker Images mit Versionsstempel bauen und in die Gitea-Registry veroeffentlichen

Ablauf: quality -> test -> publish (jeder Job nur bei Erfolg des vorherigen). Der Job publish besteht aus vier Schritten: actions/checkout@v4 mit fetch-depth: 0 (volle Historie samt Tags, sonst liefert git describe nichts), Login in die Registry (siehe Abschnitt 3), der Aufruf von .gitea/scripts/publish-images.sh und der Aufruf von .gitea/scripts/publish-release.sh (legt bei Tags v* den Gitea-Release aus dem CHANGELOG-Abschnitt an; auf main endet er mit "nichts zu tun").

Das Release-Skript spricht die Gitea-API ueber GITHUB_API_URL bzw. GITHUB_SERVER_URL/api/v1 an -- im Job-Container ist das https://git.vicolab.de; localhost:3002 ist von dort NICHT erreichbar (nur der Docker-Daemon des Hosts erreicht die Registry so). Lokal laesst sich das Skript mit --dry-run --tag vX.Y.Z pruefen, ohne Netzaufruf und ohne Token.

Zwei Kanaele: Etiketten je Anlass

Das Skript .gitea/scripts/publish-images.sh entscheidet allein anhand GITHUB_REF, ob und unter welchen Etiketten veroeffentlicht wird:

Anlass Kanal (APP_CHANNEL) Etiketten in der Registry
Push auf main beta beta und latest (latest ist nur ein Alias fuer beta und entfaellt spaeter)
Tag vX.Y.Z live live und vX.Y.Z + Gitea-Release Tessera X.Y.Z mit dem CHANGELOG-Abschnitt
Push auf live ohne Tag -- keine; der Lauf prueft nur (quality, test), das Skript endet mit "nichts zu tun"

Das Kanalmodell fuer den Betrieb (welcher Server welches Etikett zieht, Freigabe, Hotfix) steht in docs/anleitung-betrieb.md, Kapitel 9.

Versionsstempel

Das Skript berechnet vier Werte und gibt sie als --build-arg an beide Dockerfiles (apps/web/Dockerfile, apps/api/Dockerfile):

Build-Arg Quelle
APP_VERSION git describe --tags --always (ohne Tag: kurzer Commit-SHA)
APP_CHANNEL beta oder live, siehe Tabelle oben
APP_COMMIT git rev-parse --short HEAD
APP_BUILD_TIME date -u, ISO-Format

Die API liest die Werte zur Laufzeit (GET /health/version, Startzeile im Log). Das Web-Image bettet NEXT_PUBLIC_APP_* beim Build in das Browser-Bundle ein -- deshalb laeuft die builder-Stufe des Web-Images jetzt bei jedem Pipeline-Lauf neu, die Laufzeit liegt eher bei 4-6 statt 2 Minuten.

Lokale Probe ohne Docker-Aufruf:

GITHUB_REF=refs/tags/v1.2.3 sh .gitea/scripts/publish-images.sh --print-plan

Hinweis: Die fruehere Entscheidung D-13 (Images lokal bauen, keine Registry) ist ueberholt -- seit der Einfuehrung der Gitea-Registry werden Images gepusht und von den Servern per docker compose pull geholt.

5. Sicherheitshinweise

Docker Socket

Der act_runner mountet den Host-Docker-Socket (/var/run/docker.sock), um Jobs in Docker-Containern ausfuehren zu koennen. Das bedeutet:

  • Der Runner hat Zugriff auf den Docker-Daemon des Hosts
  • Er kann Container starten, stoppen und inspizieren

Risikobewertung: Akzeptabel fuer interne CI, da:

  • Nur Claude pusht manuell bei Meilensteinen (D-11)
  • Kein externer Code wird ausgefuehrt
  • Ephemeral Mode ist aktiviert (siehe unten)

Ephemeral Runner

GITEA_RUNNER_EPHEMERAL=1 sorgt dafuer, dass der Runner seine Credentials nach jedem Job widerruft und sich neu registriert. Das verhindert:

  • Persistente Zugriffstokens im Runner-Container
  • Kompromittierung ueber alte Job-Artefakte
  • Seitliche Bewegung zwischen Jobs

Workflow-Dateien

Workflow-Dateien in .gitea/workflows/ werden direkt aus dem Repository geladen. Da nur vertrauenswuerdiger Code gepusht wird (D-11, Claude als einziger Committer), ist das Risiko einer manipulierten Pipeline minimal.

Ein Tag-Push v* ist der Freigabe-Hebel fuer Live: wer ihn setzen darf, kann das live-Etikett neu belegen. Heute hat nur das Konto schalli Schreibrecht (0 Kollaborateure, keine Branch-Regeln). Kommen weitere Konten dazu, in Gitea unter Repository > Settings > Branches / Tags eine Tag-Schutzregel fuer v* und einen Branch-Schutz fuer live anlegen (T-KU1-04).

6. Fehlerbehebung

Runner registriert sich nicht

  1. Pruefen ob GITEA_INSTANCE_URL korrekt und erreichbar ist
  2. Token ggf. neu generieren (Token sind einmalig verwendbar bei Ephemeral Mode)
  3. Runner-Logs pruefen: docker compose -f docker-compose.ci.yml logs act_runner

Pipeline startet nicht

  1. In Gitea pruefen ob Actions fuer das Repository aktiviert ist
  2. Pruefen ob der Runner als "online" angezeigt wird (Gitea > Actions > Runners)
  3. Sicherstellen dass die Workflow-Datei unter .gitea/workflows/ liegt
  4. YAML-Syntax validieren

Docker-Build schlaegt fehl

  1. Sicherstellen dass docker-compose.yml im Arbeitsverzeichnis des Runners erreichbar ist
  2. Docker Daemon Status pruefen: docker info
  3. Disk Space pruefen: df -h

Stempel zeigt dev oder nur eine Kurzkennung statt des Tags

  1. Im Job publish pruefen, dass actions/checkout@v4 mit fetch-depth: 0 auscheckt -- ohne Tags liefert git describe --tags --always nur den SHA
  2. Pruefen, ob der Tag wirklich gepusht wurde: git ls-remote --tags origin
  3. dev bedeutet: das Image wurde ohne Build-Args gebaut (lokal statt ueber das Skript) -- das ist fuer lokale Builds normal