# CI/CD

## Wo liegt der eigentliche Workflow?

Der lauffähige GitHub-Actions-Workflow liegt unter [`.github/workflows/ci.yml`](../../.github/workflows/ci.yml)
im Repository-Root - **nicht** hier. Das ist keine Abweichung vom Plan, sondern eine technische
Notwendigkeit: GitHub führt ausschließlich Workflows aus, die exakt unter `.github/workflows/`
liegen, ein anderer Pfad wird von GitHub schlicht nicht erkannt/getriggert. Dieser Ordner
(`infra/ci/`) enthält stattdessen die dazugehörige **Dokumentation** - passend zur Struktur des
restigen `infra/`-Verzeichnisses (Podman Compose, Helm, ...).

## Was die Pipeline heute macht

Vier parallele Jobs bei jedem Push/PR auf `main` (siehe [`ci.yml`](../../.github/workflows/ci.yml)
für Details):

| Job | Prüft |
|---|---|
| `backend` | `mvn clean verify` über den kompletten Maven-Reactor (Unit-, Architektur- und Testcontainers-Integrationstests, gegen den rootless Podman-Socket des Runners) |
| `frontend` | `ng build --configuration production` (AOT-Template-Typprüfung) + `ng test` |
| `container-build` | Baut alle 9 Container-Images per `podman build` (Matrix-Job), **ohne** sie zu pushen |
| `helm-lint` | `helm lint` + `helm template` für jede der 8 service-spezifischen values-Dateien |

`container-build` läuft erst nach einem grünen `backend`-Job (`needs: backend`) - ein Image aus
ungetestetem Code zu bauen wäre Zeitverschwendung.

## Warum hier "CI" endet und noch kein "CD"

Es fehlt bewusst ein Deployment-Schritt (Image-Push in eine Registry, `helm upgrade --install`
gegen einen echten Cluster). Beides würde Secrets voraussetzen, die dieses Lernprojekt nicht
hat und auch nicht vortäuschen soll (ein Registry-Token oder eine `KUBECONFIG` als GitHub-Secret
in einem öffentlichen Lernrepository wäre ohnehin ein Sicherheitsrisiko). Das ist eine bewusste
Scope-Entscheidung, kein Versehen - siehe auch die ähnlich begründeten Auslassungen in
[docker-compose-guide.md](../../documentation/docs/05-devops/docker-compose-guide.md) und
[kubernetes-guide.md](../../documentation/docs/05-devops/kubernetes-guide.md).

## Update: die letzte Meile zu echtem CD - über Jenkins statt GitHub Actions

Die unten skizzierte Vertiefungsaufgabe wurde umgesetzt, aber bewusst NICHT durch Ausbau von
`ci.yml` - stattdessen zusätzlich mit Jenkins (siehe
[ADR-0009](../../documentation/docs/01-architektur/adr/0009-jenkins-fuer-mehrstufige-pipeline.md)
und [jenkins-pipeline.md](../../documentation/docs/05-devops/jenkins-pipeline.md)): eine
vierstufige `Local → Test → QS → Prod`-Pipeline (`Jenkinsfile` im Repo-Root) mit demselben
Approval-Gate-Prinzip wie Punkt 3 unten, aber vier statt zwei Stufen und nativ in Jenkins statt
über GitHub Environments. GitHub Actions bleibt unverändert für schnelles PR-Feedback zuständig -
beide Systeme bewusst nebeneinander, nicht als Ersatz füreinander (gleiches Muster wie
Eureka/Kubernetes-native Discovery in ADR-0007).

## Ursprüngliche Vertiefungsaufgabe: die letzte Meile zu echtem CD

Wer STATTDESSEN `ci.yml` selbst zu echtem Continuous Deployment ausbauen möchte (die Alternative,
die NICHT gewählt wurde - siehe Update oben):

1. **Registry**: Ein `redhat-actions/podman-login`-Schritt vor dem `podman build`, plus
   `podman push` mit einem echten Tag (z. B. `ghcr.io/<user>/catalog-service:${{ github.sha }}`)
   statt nur `podman build` lokal im Runner.
2. **Deployment**: Ein zusätzlicher Job `deploy`, der nach `container-build` UND `helm-lint` läuft
   (`needs: [container-build, helm-lint]`), sich per `azure/k8s-set-context` (oder äquivalent) gegen
   einen Cluster authentifiziert (`KUBECONFIG` als GitHub-Secret) und für jeden Service
   `helm upgrade --install <service> infra/helm/library-service -f infra/helm/values/<service>-values.yaml --set image.tag=${{ github.sha }}` ausführt.
3. **Umgebungen**: GitHub Environments (`staging`, `production`) mit manuellem Approval-Gate vor
   dem Produktions-Deploy - verhindert, dass jeder Merge nach `main` automatisch live geht.
4. **Image-Tag-Strategie**: aktuell würde `${{ github.sha }}` verwendet (nachvollziehbar,
   unveränderlich) statt `latest` (siehe `image.tag` in den values-Dateien, dort noch `"local"`
   für den lokalen Compose-Betrieb).
