ops: add CI/CD pipeline, a/b rolling deploy, Gitea Actions workflow
Deploy to Production / deploy (push) Failing after 10s

- .gitea/workflows/deploy.yml — push-to-main triggers rolling deploy
- scripts/deploy-bluegreen.sh — a-stack then b-stack restart; Maven runs
  in Docker (no JDK needed on runner host); Caddy reload at end
- scripts/deploy-all.ps1 — emergency manual deploy from dev machine
- infra/docker-compose.yml — a/b pairs per service; wget health checks;
  Gitea service; Prometheus/Grafana/DB ports restricted to localhost
- infra/Caddyfile — dual upstreams with health-based routing
- infra/Dockerfile.* — one per service
- infra/prometheus.yml + grafana provisioning

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Carsten Rehfeld
2026-05-14 14:01:12 +02:00
parent 5156089152
commit 82f0ac6007
72 changed files with 4715 additions and 27 deletions
@@ -23,8 +23,22 @@ quarkus.http.port=8180
apix.api-key=${APIX_API_KEY:dev-insecure-key-change-in-prod}
# ── Verification ──────────────────────────────────────────────────────────────
apix.dns.doh-url=${APIX_DOH_URL:https://dns.google/resolve}
apix.gleif.api-url=${GLEIF_API_URL:https://api.gleif.org/api/v1}
apix.opencorporates.api-key=${OPENCORPORATES_API_KEY:}
apix.opencorporates.api-url=${OPENCORPORATES_API_URL:https://api.opencorporates.com/v0.4}
apix.hygiene.security-txt-url-template=${APIX_SECURITY_TXT_TEMPLATE:https://{domain}/.well-known/security.txt}
apix.verification.http-timeout-ms=${APIX_VERIFICATION_TIMEOUT_MS:5000}
apix.org.tan.expose-in-response=false
# ── Mail signing (Ed25519) ────────────────────────────────────────────────────
# PKCS#8 private key and SubjectPublicKeyInfo public key, Base64 standard encoding.
# If blank, an ephemeral key pair is generated at startup (dev/test only).
apix.mail.signing.private-key-base64=${APIX_MAIL_SIGNING_PRIVATE_KEY:}
apix.mail.signing.public-key-base64=${APIX_MAIL_SIGNING_PUBLIC_KEY:}
# Key ID published in signed payloads and the /mail-signing-keys endpoint.
# Convention: YYYY-MM, rotated every 6 months.
apix.mail.signing.kid=${APIX_MAIL_SIGNING_KID:dev}
apix.sanctions.cache-path=${SANCTIONS_CACHE_PATH:./sanctions-cache}
# ── Logging ───────────────────────────────────────────────────────────────────
@@ -33,3 +47,30 @@ quarkus.log.console.json=false
# ── Health ────────────────────────────────────────────────────────────────────
quarkus.smallrye-health.root-path=/q/health
# ── Observability ─────────────────────────────────────────────────────────────
# HTTP server request metrics (http_server_requests_seconds histogram) are
# auto-instrumented by quarkus-micrometer. Prometheus scrapes /q/metrics.
quarkus.micrometer.enabled=true
quarkus.micrometer.export.prometheus.enabled=true
# Without match-patterns, the uri label is the raw request path and UUIDs create
# unbounded cardinality. Order matters: specific sub-paths before the catch-all.
quarkus.micrometer.binder.http-server.match-patterns=\
/services/[0-9a-f-]+/replacements=/services/{id}/replacements,\
/services/[0-9a-f-]+/history=/services/{id}/history,\
/services/[0-9a-f-]+/olevel=/services/{id}/olevel,\
/services/[0-9a-f-]+=/services/{id},\
/organizations/[0-9a-f-]+/verify=/organizations/{id}/verify,\
/organizations/[0-9a-f-]+/request-upgrade=/organizations/{id}/request-upgrade,\
/organizations/[0-9a-f-]+/earned-level=/organizations/{id}/earned-level,\
/organizations/[0-9a-f-]+/temp-grant=/organizations/{id}/temp-grant,\
/organizations/[0-9a-f-]+/rotate-key=/organizations/{id}/rotate-key,\
/organizations/[0-9a-f-]+/request-tan=/organizations/{id}/request-tan,\
/organizations/[0-9a-f-]+/rotate-key-with-tan=/organizations/{id}/rotate-key-with-tan,\
/organizations/[0-9a-f-]+/rotate-key-dns=/organizations/{id}/rotate-key-dns,\
/organizations/[0-9a-f-]+/confirm-key-rotation-dns=/organizations/{id}/confirm-key-rotation-dns,\
/organizations/[0-9a-f-]+/notify-key-rotation-fraud=/organizations/{id}/notify-key-rotation-fraud,\
/organizations/[0-9a-f-]+/fraud-lock=/organizations/{id}/fraud-lock,\
/organizations/[0-9a-f-]+/audit-log=/organizations/{id}/audit-log,\
/organizations/[0-9a-f-]+/event-feed=/organizations/{id}/event-feed,\
/organizations/[0-9a-f-]+=/organizations/{id}