Files
Elternbeirat/docs/deployment.md
T
tleiningerandClaude Opus 4.8 94d9cac77b Add release/dev build scripts and branch-based deploy workflow
- scripts/release.sh: build from main, tag :latest + short SHA, push
  both. Aborts if not on main or working tree is dirty; warns on
  unpushed commits. Prevents dev states from becoming :latest.
- scripts/dev-build.sh: local dev image (tagged per branch), never
  pushed -- keeps development builds off the registry.
- docs/deployment.md: document branch/PR workflow, replace manual
  build/push steps with the scripts, SHA-based rollback now standard.
- .gitattributes: force LF on *.sh so the shebang works on Windows.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-21 07:58:46 +02:00

186 lines
6.5 KiB
Markdown

# Deployment
Wie ein neuer Stand der Website auf Unraid landet. Aktueller Stand:
**Handbetrieb** — Image lokal bauen, in die Gitea-Registry pushen, auf Unraid
ziehen. Die Automatisierung per Gitea Actions (plan.md Schritt 10) kommt später.
> **Test- vs. Produktivbetrieb.** Solange die Domain noch nicht umgezogen ist und
> NPM nicht davorsteht, läuft der Container mit einem Port-Mapping und ist im
> lokalen Netz direkt erreichbar (`http://<unraid>:5000`). Im Produktivbetrieb
> entfällt das Mapping — dann ist nur NPM der Weg zum Container (plan.md AE-4,
> Abschnitt 8). Die beiden compose-Varianten sind unten getrennt beschrieben.
---
## Voraussetzungen (einmalig)
### Gitea-Registry-Token
Der Push in die Registry braucht ein Gitea-Zugriffstoken mit **Paket-Schreibrecht**
(`package: Read and Write`) — **nicht** das Kontopasswort.
1. Gitea → oben rechts Profilbild → **Settings** → **Applications**.
2. Abschnitt **Manage Access Tokens**: Name vergeben (z.B. `registry-push`).
3. Unter **Select scopes**: `package` auf **Read and Write** stellen.
4. **Generate Token** klicken, Zeichenkette **sofort kopieren** (nur einmal
sichtbar).
> **Token ist ein Geheimnis.** Niemals in Git, Chats, Screenshots oder Tickets
> im Klartext ablegen. Wird eins doch einmal sichtbar: in Gitea **löschen** und
> neu erzeugen. Ein `package`-Token erlaubt das Hochladen beliebiger Images in
> die Registry.
Der Docker-Login speichert das Token danach lokal, sodass es nur einmal
eingegeben werden muss.
---
## Arbeitsweise: Branches, nie direkt auf main
`main` ist der **veröffentlichte** Stand — nur daraus wird `:latest` gebaut, und
nur `:latest` zieht Unraid. Deshalb wird nie direkt auf `main` committet:
1. Feature-Branch anlegen: `git switch -c <bereich>/<kurz>` (z.B.
`deployment/sha-tagging`, `content/pipeline`).
2. Dort committen, Branch pushen: `git push -u origin <branch>`.
3. In Gitea einen **Pull Request** gegen `main` öffnen und dort mergen.
4. Erst danach aus `main` das Release-Image bauen (unten).
**Dev-Images bleiben lokal.** Zum Ausprobieren auf dem eigenen Rechner:
```bash
scripts/dev-build.sh # baut elternbeirat-web:dev-<branch>, pusht NICHTS
```
So kann ein Entwicklungsstand nie versehentlich als `:latest` in der Registry
landen.
## Neuen Stand ausrollen (Handbetrieb)
Voraussetzung: einmalig an der Registry angemeldet (siehe unten). Dann, **auf
main** und mit sauberem Arbeitsverzeichnis:
```bash
scripts/release.sh
```
Das Skript baut das Image, taggt es mit `:latest` **und** dem Commit-Kurz-SHA
(für Rollback) und pusht beide. Es **bricht ab**, wenn du nicht auf `main` bist
oder uncommittete Änderungen hast, und warnt bei ungepushten Commits.
> Der Image-Pfad `gitea.anticarnist.de/tom/elternbeirat` ist **kleingeschrieben**
> — Container-Registries verlangen das im Pfad, obwohl Benutzer (`Tom`) und Repo
> (`Elternbeirat`) großgeschrieben sind.
### Unraid einmalig an der Registry anmelden
Das Image ist **privat**, deshalb muss Unraid sich einmal anmelden, bevor es
ziehen kann. Das **Compose Manager Plus**-Plugin hat dafür kein UI-Feld — der
Login läuft über das Unraid-Terminal (`>_`-Symbol oben rechts in der
Weboberfläche, Prompt `root@Cube:~#`):
```bash
docker login gitea.anticarnist.de
# Username: Tom
# Password: <package-Token>
```
Der Login bleibt gespeichert; er muss nur wiederholt werden, wenn das Token
wechselt. Zwei erfahrungsgemäße Stolpersteine:
- **Nicht mit PowerShell/Laptop verwechseln.** Der Login muss im *Unraid*-Terminal
passieren (`root@Cube`), nicht in der Windows-PowerShell (`PS C:\`). Der Laptop
braucht den Login nur zum *Pushen*, Unraid zum *Ziehen*.
- **Falscher Username bleibt hängen.** Meldet der Login „Stored credentials
invalid or expired" und fragt *nicht* nach dem Namen, erst `docker logout
gitea.anticarnist.de`, dann neu einloggen — sonst wird versehentlich ein
Nonsens-Username gespeichert.
- **Klartext-Warnung.** Docker speichert das Token unverschlüsselt in
`/root/.docker/config.json`. Auf dem eigenen Server für den Anfang okay.
*Später sauberer:* einen Credential-Helper einrichten (→ offener Punkt unten).
### Auf Unraid neu ziehen und starten
Im **Compose Manager Plus**-Plugin (Unraid-Weboberfläche):
- Reiter **Docker** → Abschnitt **Compose** → Stack **elternbeirat** →
**Compose Down**, dann **Compose Up** (oder „Pull" + „Up", je nach
Plugin-Version), damit die neue `:latest` gezogen wird.
> `docker compose up` zieht ein `:latest` **nicht** automatisch neu, wenn schon
> ein gleichnamiges Image lokal liegt. Im Zweifel vorher explizit „Pull".
---
## compose-Varianten
### Test (jetzt: ohne NPM, direkt im LAN erreichbar)
Liegt im Repo als `compose.yaml`. Zieht das Registry-Image und mappt Port
**5000 → 8080**:
```yaml
services:
eb-web:
image: gitea.anticarnist.de/tom/elternbeirat:latest
container_name: eb-web
restart: unless-stopped
environment:
ASPNETCORE_URLS: http://+:8080
TZ: Europe/Berlin
ports:
- "5000:8080" # TEST-Zugang, im Produktivbetrieb entfernen
```
Erreichbar unter `http://<unraid>:5000`.
### Produktiv (später: nur über NPM)
Kein `ports:`-Block, stattdessen das externe NPM-Docker-Netz (plan.md
Abschnitt 8):
```yaml
services:
eb-web:
image: gitea.anticarnist.de/tom/elternbeirat:latest
container_name: eb-web
restart: unless-stopped
environment:
ASPNETCORE_URLS: http://+:8080
TZ: Europe/Berlin
networks: [npm]
networks:
npm:
external: true
```
---
## Rollback
`scripts/release.sh` taggt jeden Release zusätzlich mit dem Commit-Kurz-SHA, der
sich — anders als das wandernde `:latest` — nie verschiebt. Zum Zurückrollen in
der Unraid-`compose.yaml` `:latest` durch `:<sha>` des letzten funktionierenden
Stands ersetzen und neu hochfahren:
```yaml
image: gitea.anticarnist.de/tom/elternbeirat:99dd052 # statt :latest
```
Welche SHA-Tags in der Registry liegen, zeigt Gitea unter
`Tom/-/packages` → `elternbeirat`.
---
## Offene Automatisierung (später, plan.md Schritt 10)
- `.gitea/workflows/deploy.yml`: auf Push nach `main` bauen und pushen.
- Zwei bekannte Stolpersteine: der `act_runner` braucht Docker-Socket-Zugriff;
die Registry braucht das `write:package`-Token, nicht das Login-Passwort.
- Redeploy per Watchtower **label-scoped**, sonst aktualisiert er den ganzen
Home-Lab-Bestand.
- **Registry-Token auf Unraid verschlüsseln:** aktuell liegt es im Klartext in
`/root/.docker/config.json`. Später einen Credential-Helper einrichten, damit
die Klartext-Warnung von `docker login` verschwindet.