CI/CD-Pipeline¶
Die Kamerplanter-CI/CD-Pipeline läuft vollständig auf GitHub Actions. Sie umfasst automatische Qualitätsprüfungen für Backend und Frontend, das Bauen und Veröffentlichen von Container-Images sowie die automatisierte Helm-Chart-Publikation. Releases werden durch Git-Tags ausgelöst und führen alle Schritte in der richtigen Reihenfolge aus.
Voraussetzungen¶
- Schreibzugriff auf das GitHub-Repository (
nolte/kamerplanter) - Kein manuelles Einrichten von Secrets notwendig — alle Workflows nutzen das automatisch verfügbare
GITHUB_TOKEN - Container-Images werden in die GitHub Container Registry (GHCR) unter
ghcr.io/nolte/gepusht
Branch-Strategie¶
| Branch | Zweck |
|---|---|
feature/* | Entwicklungsarbeit; CI läuft bei Pull Requests auf develop |
develop | Integrationsbranch; löst CI und Image-Build aus |
main | Repräsentiert den aktuell stabilen Release-Stand; wird nach jedem Release automatisch aktualisiert |
Hinweis
main wird nicht direkt für die Entwicklung verwendet. Commits landen über develop und Git-Tags auf main. Der Workflow release-cd-refresh-master.yml übernimmt diesen Schritt automatisch nach einem veröffentlichten Release.
Übersicht der Workflows¶
| Datei | Auslöser | Zweck |
|---|---|---|
backend.yml | Push/PR auf develop, Pfad src/backend/** | Lint + Tests Backend |
frontend.yml | Push/PR auf develop, Pfad src/frontend/** | Lint + Tests + Build Frontend |
docker-publish.yml | Push auf develop oder v*-Tag | Container-Images + Helm-Chart bauen und publizieren |
skaffold-verify.yml | PR auf develop, Pfad skaffold.yaml, helm/**, Dockerfiles | Helm-Lint + Skaffold-Diagnose |
release-drafter.yml | Push auf develop | Release-Notes-Entwurf automatisch aktualisieren |
release-cd-deliver-docs.yml | Veröffentlichtes Release | MkDocs-Dokumentation auf GitHub Pages deployen |
release-cd-refresh-master.yml | Veröffentlichtes Release | main-Branch auf den Release-Stand aktualisieren |
Backend-CI (backend.yml)¶
Der Backend-CI-Workflow läuft bei jedem Push auf develop und bei Pull Requests, sofern Dateien unter src/backend/ geändert wurden.
Was geprüft wird¶
- Ruff Lint — prüft den Python-Code auf Stil- und Qualitätsprobleme (
ruff check .) - Ruff Format — stellt sicher, dass der Code korrekt formatiert ist (
ruff format --check .) - Unit-Tests — führt alle Tests unter
tests/unit/mit pytest aus
jobs:
lint-test:
runs-on: ubuntu-latest
steps:
- uses: actions/setup-python@v5
with:
python-version: '3.14'
allow-prereleases: true
- name: Install dependencies
run: pip install -e ".[dev]"
- name: Ruff lint
run: ruff check .
- name: Ruff format check
run: ruff format --check .
- name: Unit tests
run: pytest tests/unit/ -v --tb=short
Lokale Prüfung vor dem Push
Abhängigkeiten installieren¶
Die Backend-Abhängigkeiten werden aus pyproject.toml installiert. Der [dev]-Extra enthält pytest, ruff und weitere Entwicklungswerkzeuge:
Frontend-CI (frontend.yml)¶
Der Frontend-CI-Workflow läuft bei jedem Push auf develop und bei Pull Requests, sofern Dateien unter src/frontend/ geändert wurden.
Was geprüft wird¶
- TypeScript-Prüfung — strikter Typen-Check ohne Ausgabe (
tsc --noEmit) - ESLint — Qualitätsprüfung des TypeScript/React-Codes
- Vitest — alle Unit- und Komponenten-Tests
- Vite-Build — stellt sicher, dass der Produktions-Build fehlerfrei kompiliert
jobs:
lint-test-build:
runs-on: ubuntu-latest
steps:
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
cache-dependency-path: src/frontend/package-lock.json
- run: npm ci
- run: npx tsc --noEmit
- run: npm run lint
- run: npm run test
- run: npm run build
Lokale Prüfung vor dem Push
Build-Artefakt¶
Bei einem Push auf develop (nicht bei PRs) wird das fertige dist/-Verzeichnis als GitHub Actions Artefakt hochgeladen und für 7 Tage aufbewahrt. Das ermöglicht eine schnelle Inspektion des Build-Ergebnisses ohne lokales Kompilieren.
Container-Build und -Publikation (docker-publish.yml)¶
Dieser Workflow baut und publiziert alle Container-Images sowie den Helm-Chart. Er wird ausgelöst durch:
- Push auf
develop(wenn Backend-, Frontend- oder Helm-Dateien geändert wurden) - Push eines
v*-Tags (Release) — dann werden alle Komponenten gebaut, unabhängig von Pfadänderungen - Manuell über
workflow_dispatch
Pfad-basiertes Filtern¶
Damit nicht bei jeder Änderung alle Images neu gebaut werden, ermittelt ein changes-Job zuerst, welche Komponenten betroffen sind:
Bei einem v*-Tag oder manuellem Auslösen wird das Filtern übersprungen — es werden immer alle Komponenten gebaut.
Backend-Image¶
Das Backend-Image basiert auf python:3.14-slim und wird als Single-Stage-Build erstellt:
FROM python:3.14-slim AS base
WORKDIR /app
COPY pyproject.toml .
RUN pip install --no-cache-dir .
COPY . .
EXPOSE 8000
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
Das Image wird nach ghcr.io/nolte/kamerplanter-backend gepusht.
Frontend-Image¶
Das Frontend-Image verwendet einen zweistufigen Multi-Stage-Build: Zuerst wird die React-App mit Node.js 22 gebaut, dann werden die statischen Dateien in ein schlankes nginx-Image kopiert:
FROM node:22-alpine AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM nginx:1.27-alpine
COPY --from=build /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
Das Image wird nach ghcr.io/nolte/kamerplanter-frontend gepusht.
Image-Tags¶
Docker-Metadata wird automatisch per docker/metadata-action erzeugt:
| Tag-Schema | Beispiel | Wann |
|---|---|---|
latest | kamerplanter-backend:latest | Push auf develop |
| Commit-SHA | kamerplanter-backend:a3f7c2b | Immer |
| Branch-Name | kamerplanter-backend:develop | Push auf Branch |
| Semantic Version | kamerplanter-backend:1.2.0 | v1.2.0-Tag |
| Major.Minor | kamerplanter-backend:1.2 | v1.2.0-Tag |
Helm-Chart¶
Das Helm-Chart für Kamerplanter liegt unter helm/kamerplanter/ und wird als OCI-Artefakt gepusht:
Bei einem Release-Tag wird die version und appVersion in Chart.yaml automatisch auf die Release-Version gesetzt, bevor das Chart gepackt wird. Gleichzeitig werden die Image-Tags in values.yaml von latest auf die konkrete Version umgestellt.
Layer-Caching¶
Alle Image-Builds nutzen den GitHub Actions Cache (type=gha) für Docker-Layer. Das beschleunigt Folge-Builds erheblich, wenn sich nur wenige Schichten ändern.
Skaffold-Verify (skaffold-verify.yml)¶
Dieser Workflow läuft bei Pull Requests auf develop, wenn skaffold.yaml, Helm-Dateien oder Dockerfiles geändert wurden. Er stellt sicher, dass die lokale Entwicklungsumgebung weiterhin funktionsfähig ist.
Was geprüft wird¶
helm dependency build— lädt die Chart-Abhängigkeiten herunter (bjw-s common chart, valkey)helm lint— prüft das Helm-Chart auf Syntaxfehler mit den Dev-Valueshelm template— rendert alle Kubernetes-Manifeste und prüft die Templating-Logikskaffold diagnose— validiert die Skaffold-Konfigurationskaffold render— erzeugt gerenderte Manifeste und lädt sie als Artefakt hoch
Skaffold ist nur für die lokale Entwicklung
Skaffold wird ausschließlich für die lokale Entwicklungsumgebung (Kind-Cluster) verwendet. Produktions-Deployments laufen nicht über Skaffold, sondern über den docker-publish-Workflow in Kombination mit dem Helm-Chart.
Release-Prozess¶
Ein vollständiger Release besteht aus mehreren automatischen Schritten, die durch das Veröffentlichen eines GitHub-Releases ausgelöst werden.
Schritt 1: Release-Entwurf vorbereiten (automatisch)¶
Der release-drafter-Workflow aktualisiert bei jedem Push auf develop automatisch einen Release-Entwurf mit den Änderungen seit dem letzten Tag.
Schritt 2: Release-Tag setzen¶
Das Tag löst docker-publish.yml aus und baut alle Images und den Helm-Chart mit der korrekten Versionsnummer.
Schritt 3: Release veröffentlichen¶
Wenn das GitHub-Release als "published" markiert wird, laufen zwei weitere Workflows an:
release-cd-deliver-docs.yml — deployt die MkDocs-Dokumentation auf GitHub Pages über einen wiederverwendbaren Workflow aus nolte/gh-plumbing.
release-cd-refresh-master.yml — aktualisiert den main-Branch auf den Stand des neuen Release-Tags. main zeigt damit immer auf den zuletzt veröffentlichten stabilen Stand.
Schritt 4: Release-Assets (automatisch)¶
Der update-release-assets-Job im docker-publish-Workflow hängt am Release folgende Dateien an:
docker-compose-1.2.0.yml— versionierte Docker-Compose-Datei für Self-Hosting.env.example-1.2.0— Vorlage für Umgebungsvariablen- Container-Image-Referenzen und Helm-Pull-Befehl im Release-Text
Zusammenfassung Release-Ablauf¶
sequenceDiagram
participant Dev as Entwickler
participant GH as GitHub
participant GHCR as ghcr.io
Dev->>GH: git push origin v1.2.0
GH->>GH: docker-publish.yml startet
GH->>GHCR: backend-Image :1.2.0 pushen
GH->>GHCR: frontend-Image :1.2.0 pushen
GH->>GHCR: Helm-Chart :1.2.0 pushen
GH->>GH: Release-Assets anhängen
Dev->>GH: Release als "published" markieren
GH->>GH: MkDocs-Docs deployen
GH->>GH: main-Branch aktualisieren GHCR-Pakete abrufen¶
Alle Images sind öffentlich lesbar. Für lokale Tests:
Häufige Fragen¶
Warum schlägt der Backend-CI fehl, obwohl die Tests lokal laufen?
Stelle sicher, dass du Python 3.14 verwendest (python --version). Die CI verwendet explizit python-version: '3.14' mit allow-prereleases: true. Abweichende Python-Versionen können zu unterschiedlichem Verhalten führen. Prüfe auch, ob alle Abhängigkeiten mit pip install -e ".[dev]" installiert wurden.
Warum wird kein neues Image gebaut, obwohl ich auf develop gepusht habe?
Das Pfad-Filtern in docker-publish.yml stellt sicher, dass nur tatsächlich betroffene Komponenten gebaut werden. Wenn du z. B. nur eine Spec-Datei geändert hast, wird kein Image gebaut. Bei v*-Tags wird das Filtern umgangen.
Wie aktualisiere ich das Helm-Chart manuell?
Du kannst docker-publish.yml über workflow_dispatch manuell auslösen. Navigiere dazu in GitHub zu Actions → Build & Publish Container Images → Run workflow.
Wann wird main aktualisiert?
main wird ausschließlich automatisch durch release-cd-refresh-master.yml nach einem veröffentlichten Release aktualisiert. Direkte Pushes auf main sind nicht vorgesehen.
Wie sehe ich, welche Image-Version gerade läuft?
Die Image-Digest-Labels (org.opencontainers.image.*) sind in jedem Image eingebettet. Du kannst sie mit docker inspect <image> auslesen oder im GHCR-Package-Tab auf GitHub nachsehen.