meldestelle/.github/workflows/integration-tests.yml
StefanMo 3f9326a533
Refactor(config): Implement central environment config (MP-18)) (#17)
* fix(infra): Makefile .env generiert

* MP-18 Env-Konfiguration Refactoring: Schritte 2–4 umgesetzt\n\n2) Single Source of Truth für Versionen\n- docker/versions.toml als alleinige Quelle bestätigt\n- docker/build-args/global.env bereinigt und konsistent auf *_IMAGE_TAG umgestellt (PROMETHEUS_IMAGE_TAG, GRAFANA_IMAGE_TAG, KEYCLOAK_IMAGE_TAG, POSTGRES_IMAGE_TAG, REDIS_IMAGE_TAG, CONSUL_IMAGE_TAG, KAFKA_IMAGE_TAG, ZOOKEEPER_IMAGE_TAG)\n- Keine Ports/Profiles/Secrets in global.env\n\n3) Build vs. Laufzeit getrennt (Variablenbereinigung)\n- .env.template von allen Build-/Image-Versionen befreit (DOCKER_*_VERSION, (DOCKER_)APP_VERSION, BUILD_DATE)\n- App-Versionsvariable vereinheitlicht: Nutzung von VERSION (APP_VERSION in Build-Args entfernt)\n\n4) Laufzeit-Env konsolidiert (globales .env)\n- Zentrales config/env/.env erstellt (Ports, Hosts, Flags, Pfade, SPRING_PROFILES_ACTIVE, NODE_ENV etc.)\n- config/env/.env.local angelegt (gitignored) und .gitignore ergänzt\n- Laufzeitwerte aus Build-Args-Dateien (clients/infrastructure/services) entfernt bzw. kommentiert mit Verweis auf config/env/.env\n\nAkzeptanzkriterien erfüllt\n- global.env enthält ausschließlich Build-Versionen/-Tags und Build-Tool-Versionen\n- .env.template enthält keine Build-/Image-Versionen mehr\n- Zentrales config/env/.env ist die einzige Quelle für Laufzeitwerte\n\nYouTrack: https://meldestelle-pro.youtrack.cloud/issue/MP-18

* MP-18 Env-Konfiguration Refactoring: Schritte 5–7 umgesetzt

5) Build-Args-Dateien entschlackt/umstrukturiert
- clients.env: Laufzeitwerte entfernt, NODE_VERSION/NGINX_VERSION → NODE_IMAGE_TAG/NGINX_IMAGE_TAG; nur Build-relevante Pfade/Namen belassen
- infrastructure.env/services.env: bereits zuvor Runtime-Variablen entfernt, Kommentare mit Verweis auf config/env/.env beibehalten

6) Dockerfiles überprüft/angepasst
- clients/web-app: Build-ARGs eingeführt (GRADLE_VERSION, JAVA_VERSION, NGINX_IMAGE_TAG), Basis-Image aus Tag abgeleitet; keine Runtime-ARGs
- services/ping-service: SPRING_PROFILES_ACTIVE als ARG entfernt; Build ruft ohne -P profile; Labels/ENV vereinheitlicht (OCI: version/created)
- infrastructure/gateway: SPRING_PROFILES_ACTIVE als ARG entfernt; Build ohne -P; Labels vereinheitlicht
- templates/spring-boot-service.Dockerfile: Runtime-ARGs (SPRING_PROFILES_ACTIVE, SERVICE_PORT) entfernt; Healthcheck/Expose auf ENV basierend; ENV getrennt gesetzt
- infrastructure/monitoring-server: SPRING_PROFILES_ACTIVE-ARG entfernt; Build ohne -P; ENV/Labels bereinigt

7) docker-compose* bereinigt
- docker-compose.yml: env_file: config/env/.env hinzugefügt; Image-Tags von DOCKER_* auf feste Versionen (aus global.env/versions.toml) umgestellt; keine Laufzeitwerte via build.args
- docker-compose.services.yml: env_file hinzugefügt; DOCKER_* Build-Args entfernt; nur Build-Zeit-ARGs (GRADLE_VERSION, JAVA_VERSION, BUILD_DATE, VERSION); Ports/ENV aus config/env/.env
- docker-compose.clients.yml: env_file hinzugefügt; DOCKER_* entfernt; NGINX_IMAGE_TAG als Build-Arg; APP_VERSION nutzt VERSION

Akzeptanzkriterien
- Keine Laufzeitvariablen in build-args-Dateien
- Dockerfiles verwenden ausschließlich Build-ARGs; keine Ports/Secrets/Profile als ARG
- Compose lädt nur eine Runtime-Env-Quelle (config/env/.env) und schleust keine Runtimewerte via build.args ein

YouTrack: https://meldestelle-pro.youtrack.cloud/issue/MP-18

* MP-18 Env-Konfiguration Refactoring: Schritte 8–11 umgesetzt

8) Secrets-Strategie (Dev vereinfacht)
- config/env/.env.local bereits vorhanden und gitignored; Nutzung für lokale Secrets verdeutlicht
- docker/secrets/README.md hinzugefügt; echte Geheimnisse entfernt/Platzhalter gesetzt (postgres_password.txt)
- Optimierte Compose-Dateien erzwingen Secrets nur im Profil 'prod' (profiles: [prod]) und verwenden env_file: config/env/.env

9) Namenskonventionen vereinheitlicht
- DOCKER_* in optimierten Compose-Dateien entfernt; Build-Args auf GRADLE_VERSION/JAVA_VERSION/VERSION und *_IMAGE_TAG konsolidiert
- SPRING_PROFILES_ACTIVE nur noch zur Laufzeit (Compose/env); aus Dockerfiles (auth, gateway, horses, events, members, masterdata) als Build-ARG entfernt

10) CI: ssot-guard.yml erweitert
- Guard-Checks hinzugefügt: Fail bei Runtime-Variablen in Build-Args-Dateien und bei Build-/Versions-Variablen in Runtime-Envs
- Konsistenz-Check zwischen docker/versions.toml und docker/build-args/global.env (zentraler Versionsabgleich)

11) Skripte & Automatisierung
- Neues Skript scripts/generate-build-env.sh zum Generieren von docker/build-args/global.env aus docker/versions.toml (minimal)

Akzeptanzkriterien
- Lokale Entwicklung erfordert keine Docker-Secrets (Compose default), keine Geheimnisse im Repo
- Keine DOCKER_* Präfixe in Laufzeitdateien, eindeutige Namenskonventionen
- CI bricht bei Build-vs-Runtime-Verletzungen und Versions-Drift

YouTrack: https://meldestelle-pro.youtrack.cloud/issue/MP-18

* MP-18 Env-Konfiguration Refactoring: Schritte 12–17 umgesetzt

12) DDD-Slice-Overrides eingeführt
- Optionale Runtime-Env-Dateien hinzugefügt: config/env/services/{ping,members,horses,events,masterdata}.env, config/env/infrastructure/api-gateway.env, config/env/clients/web-app.env
- Compose-Dateien laden diese Overrides zusätzlich zu config/env/.env (nur wenn vorhanden)

13) Deduplizierung bestätigt
- Build-Args-Dateien enthalten keine Laufzeitwerte; .env/.env.template ohne Build-/Versions-Keys

14) Compose an Envs gekoppelt
- docker-compose*.yml referenzieren ausschließlich config/env/.env + optionale Slice-Overrides; keine docker/build-args/*.env als Runtime-Quelle

15) Doku aktualisiert
- README: neue Konfig-Struktur, Quickstart mit .env/.env.local, optionale Overrides, Deprecations (DOCKER_*_VERSION → *_IMAGE_TAG), Smoke-Tests

16) Cleanup & Deprecations
- Deprecation-Hinweise in README dokumentiert; keine obsoleten Root-.env-Dateien verbleiben

17) Validierung & Hinweise
- Start-/Healthcheck-Hinweise für lokalen Smoke-Test ergänzt; CI-Guards bleiben gültig

* MP-18 DoD-Finalisierung: Build/Runtime-Trennung verhärtet, Doku-Hinweise korrigiert

- Entfernt: Runtime-Build-ARG SERVICE_PORT aus Service-Dockerfiles (members, horses, events, masterdata)
  * Feste Default-Ports gesetzt (8083–8086), Healthchecks und SERVER_PORT-ENV angepasst
  * Erfüllt DoD: Build-Args enthalten keine Runtime-Werte mehr
- .env.template Usage korrigiert: verweist nun explizit auf config/env/.env und compose-Aufrufe

DoD-Checkliste:
✓ Zentrale Runtime-Datei config/env/.env + .env.local (Compose nutzt env_file)
✓ Build-Args nur Versionen/Pfade/Namen (keine Runtime-Keys)
✓ versions.toml als einzige Quelle für Image-/Tool-Versionen (CI-Guard prüft Drift)
✓ CI-Workflow blockiert Build/Runtime-Mixing & Versions-Drift (ssot-guard)
✓ README dokumentiert den Flow (Quickstart, Struktur, Deprecations)
✓ Clean Builds/Local-Starts durch compose-Files unterstützt

YouTrack: https://meldestelle-pro.youtrack.cloud/issue/MP-18

* MP-18: GitHub-Workflows aktualisiert und README Markdownlint-Fehler behoben

Workflows
- CI: minimale Permissions + Concurrency hinzugefügt; build-test hängt jetzt auch von validate-docs ab; actions/setup-node → v4
- SSoT Guard: minimale Permissions + Concurrency
- Deploy Proxmox: Concurrency; Deploy-Job läuft korrekt bei workflow_dispatch (zuvor durch falsche IF-Bedingung blockiert)
- Docs KDoc Sync: minimale Permissions + Concurrency
- Integration Tests: minimale Permissions + Concurrency
- YouTrack Sync: minimale Permissions + Concurrency; Guard, wenn Secrets fehlen

Docs
- README.md: MD032 (Leerzeilen um Listen) korrigiert
- README.md: MD037 (Spaces in Emphasis / Wildcards) durch Backticks behoben
- README.md: MD034 (Bare URLs) via <> eingefasst

Ziel
- Optimierte, aktuelle CI-Workflows und grüne markdownlint-Prüfungen.

YouTrack: https://meldestelle-pro.youtrack.cloud/issue/MP-18

* MP-18: Fix Docker SSoT validator errors

Remove default values from centralized ARGs in web-app Dockerfile (GRADLE_VERSION, JAVA_VERSION, NGINX_IMAGE_TAG).

Align build.args in compose files to centralized DOCKER_* vars from versions.toml mapping (clients/services/optimized), and update api-gateway in optimized compose.

Replace hardcoded infra image tags in docker-compose.yml with DOCKER_* fallbacks for postgres/redis/prometheus/grafana/keycloak.

Validated via scripts/validate-docker-consistency.sh all → Errors=0 (Warnings remain by design).

YouTrack: https://meldestelle-pro.youtrack.cloud/issue/MP-18

* MP-18: Finalize Env/SSoT refactor – align generator, validator, build-args and compose

- Switch docker/build-args/global.env to *_IMAGE_TAG keys (PROMETHEUS/GRAFANA/KEYCLOAK/POSTGRES/REDIS/CONSUL/KAFKA/ZOOKEEPER)
- Clean docker/build-args/{clients,services,infrastructure}.env to build-time only; remove runtime/profile/ports
- Update scripts/docker-versions-update.sh to emit *_IMAGE_TAG and strip runtime keys from build-args files
- Update scripts/validate-docker-consistency.sh to check *_IMAGE_TAG and stop enforcing runtime keys in build-args
- Rename Keycloak Dockerfile ARG to KEYCLOAK_IMAGE_TAG and update FROM/labels
- Add build arg fallbacks in compose files where needed (GRADLE/JAVA/VERSION) for dev convenience

Result:
- scripts/validate-docker-consistency.sh all → 0 errors (warnings remain informational)

YouTrack: https://meldestelle-pro.youtrack.cloud/issue/MP-18

* fix: Bash-Syntax-Fehler in ssot-guard.yml behoben

- Fehlerhafte '2>/dev/null || true' Konstrukte in for-Schleifen entfernt
- Stattdessen 'shopt -s nullglob' für saubere Behandlung nicht-existierender Dateimuster verwendet
- Beide betroffene for-Schleifen (Runtime-Variablen und Build-Variablen Guards) korrigiert

MP-18

* chore: Regenerate Docker Compose files to fix SSoT drift

- Removed default values from build arguments (now using centralized DOCKER_* variables)
- Removed env_file directives for cleaner configuration
- Updated variable names for consistency (GATEWAY_PORT → API_GATEWAY_PORT)
- Standardized comments and structure across all compose files

Resolves SSoT drift detected by ssot-guard workflow.

MP-18

* MP-18 fix: Bash-Syntax-Fehler in ssot-guard.yml behoben

- Fehlerhafte '2>/dev/null || true' Konstrukte in for-Schleifen entfernt
- Stattdessen 'shopt -s nullglob' für saubere Behandlung nicht-existierender Dateimuster verwendet
- Beide betroffene for-Schleifen (Runtime-Variablen und Build-Variablen Guards) korrigiert

* MP-18 chore: Regenerate Docker Compose files to fix SSoT drift

- Removed default values from build arguments (now using centralized DOCKER_* variables)
- Removed env_file directives for cleaner configuration
- Updated variable names for consistency (GATEWAY_PORT → API_GATEWAY_PORT)
- Standardized comments and structure across all compose files

Resolves SSoT drift detected by ssot-guard workflow.

* MP-18 fix: qodana_code_quality.yml qodana.yaml

* fix: GitHub Actions Workflow-Fehler behoben

- youtrack-sync.yml: Korrektur der secrets if-Bedingung (Line 18)
  * Entfernung ungültiger != '' Vergleiche
  * Verwendung korrekter GitHub Actions Syntax: secrets.YT_URL && secrets.YT_TOKEN

- ssot-guard.yml: Korrektur der get_toml_ver() Funktion
  * Behebung des Versions-Drift Problems
  * Parsing nur aus [versions] Sektion mit State-Machine-Pattern
  * Korrekte Extraktion aller 11 Versionswerte aus versions.toml
  * Trimming von Spaces vor Key-Vergleich

Fixes: MP-18

* MP-18 Entfernung von Qodana

* MP-18 fix(ssot-guard): align build-args comments with generator output to remove SSoT drift

- clients.env/services.env/infrastructure.env: update runtime note text to match scripts/docker-versions-update.sh
- Avoids false-positive drift in workflow (content changes beyond ignored timestamps)

* MP-18 fix: workflows/youtrack-sync.yml

* MP-18 fix: workflows/youtrack-sync.yml

* MP-18 fix: workflows/youtrack-sync.yml

* MP-18 fix: workflows/youtrack-sync.yml

* MP-18 fix: workflows/youtrack-sync.yml
2025-11-19 00:59:41 +01:00

227 lines
8.0 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

name: Integration Tests
permissions:
contents: read
concurrency:
group: integration-tests-${{ github.ref }}
cancel-in-progress: true
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
jobs:
integration-tests:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
keycloak_db: [postgres, dev-file]
env:
KEYCLOAK_VERSION: "26.4.2"
services:
postgres:
image: postgres:16-alpine
env:
POSTGRES_USER: meldestelle
POSTGRES_PASSWORD: meldestelle
POSTGRES_DB: meldestelle
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U meldestelle -d $${POSTGRES_DB}"
--health-interval 10s
--health-timeout 5s
--health-retries 12
--health-start-period 20s
redis:
image: redis:7-alpine
ports:
- 6379:6379
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
zookeeper:
image: confluentinc/cp-zookeeper:7.5.0
env:
ZOOKEEPER_CLIENT_PORT: 2181
ports:
- 2181:2181
options: >-
--health-cmd "nc -z localhost 2181 || exit 1"
--health-interval 10s
--health-timeout 5s
--health-retries 3
--health-start-period 10s
kafka:
image: confluentinc/cp-kafka:7.5.0
env:
KAFKA_BROKER_ID: 1
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
ports:
- 9092:9092
options: >-
--health-cmd "kafka-topics --bootstrap-server localhost:9092 --list || exit 1"
--health-interval 10s
--health-timeout 5s
--health-retries 3
--health-start-period 30s
zipkin:
image: openzipkin/zipkin:2
ports:
- 9411:9411
options: >-
--health-cmd "wget -q -O - http://localhost:9411/health || exit 1"
--health-interval 10s
--health-timeout 5s
--health-retries 3
--health-start-period 10s
steps:
- uses: actions/checkout@v5
- name: Set up JDK 21
uses: actions/setup-java@v5
with:
java-version: 21
distribution: 'temurin'
cache: 'gradle'
- name: Setup Gradle (modern)
uses: gradle/actions/setup-gradle@v5
- name: Wait for Postgres to be ready (pg_isready in service network)
if: ${{ matrix.keycloak_db == 'postgres' }}
run: |
echo "Waiting for Postgres..."
for i in {1..40}; do
if docker run --rm --network ${{ job.services.postgres.network }} \
postgres:16-alpine pg_isready -h postgres -p 5432 -U meldestelle -d meldestelle; then
echo "Postgres is ready"; break; fi; echo -n "."; sleep 3; done
- name: Start Keycloak with Postgres (dev) and wait for readiness
if: ${{ matrix.keycloak_db == 'postgres' }}
run: |
set -euo pipefail
echo "Starting Keycloak (DB=postgres)..."
docker run -d --name keycloak \
--network ${{ job.services.postgres.network }} \
-p 8180:8080 \
-e KC_BOOTSTRAP_ADMIN_USERNAME=admin \
-e KC_BOOTSTRAP_ADMIN_PASSWORD=admin \
-e KC_DB=postgres \
-e KC_DB_URL=jdbc:postgresql://postgres:5432/meldestelle \
-e KC_DB_USERNAME=meldestelle \
-e KC_DB_PASSWORD=meldestelle \
-e KC_HEALTH_ENABLED=true \
-e JAVA_OPTS="-Xms256m -Xmx1024m -XX:MaxRAMPercentage=60" \
quay.io/keycloak/keycloak:${{ env.KEYCLOAK_VERSION }} \
start-dev
echo "Giving Keycloak 30s to initialize..."; sleep 30
wait_for() {
local url="$1"; local label="$2"; local timeout="${3:-180}"; local interval="${4:-5}"
echo "Waiting for $label ($url) ..."
if ! timeout ${timeout}s bash -c 'until curl -fsS --output /dev/null '"$url"'; do echo -n "."; sleep '"$interval"'; done'; then
echo "\n[WAIT] Timeout on $url"
return 1
fi
echo "\n[WAIT] $label is up"
}
if ! wait_for http://localhost:8180/ "root" 180 5; then
docker logs --tail=200 keycloak || true
exit 1
fi
if ! wait_for http://localhost:8180/health "health" 180 5; then
echo "[INFO] /health nicht erreichbar versuche /q/health (Quarkus default)"
wait_for http://localhost:8180/q/health "q-health" 180 5 || true
fi
wait_for http://localhost:8180/health/ready "health-ready" 300 5 || true
wait_for http://localhost:8180/admin/master/console/ "admin-console" 300 5 || (docker logs --tail=400 keycloak && exit 1)
- name: Start Keycloak with dev-file (no Postgres) and wait for readiness
if: ${{ matrix.keycloak_db == 'dev-file' }}
run: |
set -euo pipefail
echo "Starting Keycloak (DB=dev-file, no Postgres)..."
docker run -d --name keycloak \
-p 8180:8080 \
-e KC_BOOTSTRAP_ADMIN_USERNAME=admin \
-e KC_BOOTSTRAP_ADMIN_PASSWORD=admin \
-e KC_DB=dev-file \
-e KC_HEALTH_ENABLED=true \
-e JAVA_OPTS="-Xms256m -Xmx1024m -XX:MaxRAMPercentage=60" \
quay.io/keycloak/keycloak:${{ env.KEYCLOAK_VERSION }} \
start-dev
echo "Giving Keycloak 20s to initialize..."; sleep 20
wait_for() {
local url="$1"; local label="$2"; local timeout="${3:-180}"; local interval="${4:-5}"
echo "Waiting for $label ($url) ..."
if ! timeout ${timeout}s bash -c 'until curl -fsS --output /dev/null '"$url"'; do echo -n "."; sleep '"$interval"'; done'; then
echo "\n[WAIT] Timeout on $url"
return 1
fi
echo "\n[WAIT] $label is up"
}
if ! wait_for http://localhost:8180/ "root" 180 5; then
docker logs --tail=200 keycloak || true
exit 1
fi
if ! wait_for http://localhost:8180/health "health" 180 5; then
echo "[INFO] /health nicht erreichbar versuche /q/health (Quarkus default)"
wait_for http://localhost:8180/q/health "q-health" 180 5 || true
fi
wait_for http://localhost:8180/health/ready "health-ready" 300 5 || true
wait_for http://localhost:8180/admin/master/console/ "admin-console" 300 5 || (docker logs --tail=400 keycloak && exit 1)
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Run integration tests
run: ./gradlew integrationTest --no-daemon --parallel
env:
# Environment variables for Redis connection
REDIS_HOST: localhost
REDIS_PORT: 6379
# Keycloak base URL for integration tests (manual container)
KEYCLOAK_AUTH_SERVER_URL: http://localhost:8180
# Spring profile for integration tests
SPRING_PROFILES_ACTIVE: integration-test
- name: Upload test reports
uses: actions/upload-artifact@v5
if: always()
with:
name: integration-test-reports
path: |
**/build/reports/tests/integrationTest/
**/build/test-results/integrationTest/
retention-days: 7
- name: Dump service logs (Keycloak, Postgres)
if: always()
run: |
echo "=== Docker ps ===" && docker ps -a || true
echo "=== Keycloak logs (tail) ===" && docker logs --tail=400 keycloak || true
echo "=== Postgres logs (tail) ===" && docker logs --tail=200 $(docker ps -a --filter "name=postgres" --format "{{.ID}}") || true