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

240 lines
9.0 KiB
Markdown

# 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:
```bash
git remote add origin https://<gitea-url>/<user>/tessera-ctl.git
```
3. 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):
```bash
export GITEA_INSTANCE_URL=https://git.vicolab.de
export GITEA_RUNNER_REGISTRATION_TOKEN=<token-aus-schritt-oben>
```
Runner starten:
```bash
docker compose -f docker-compose.ci.yml up -d
```
Runner-Status pruefen:
```bash
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:
```bash
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:
```bash
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