diff --git a/.gitea/workflows/ci.yaml b/.gitea/workflows/ci.yaml index ab9c9b6f..a4c39796 100644 --- a/.gitea/workflows/ci.yaml +++ b/.gitea/workflows/ci.yaml @@ -411,6 +411,50 @@ jobs: pip install --quiet --no-cache-dir pytest pytest-asyncio python -m pytest test_main.py -v --tb=short + # ── P83: BUILD_SHA integrity (always) ──────────────────────────────────── + # Every Dockerfile must declare ARG BUILD_SHA + ENV BUILD_SHA so the + # check-rebuild-needed.sh script can detect "old code in container" drift. + # Every docker-compose build: block must pass BUILD_SHA through as a build + # arg — otherwise the ARG defaults to "unknown" and the check is toothless. + build-sha-integrity: + runs-on: docker + container: alpine:3.20 + steps: + - name: Checkout + run: | + apk add --no-cache git python3 + git clone --depth 1 --branch ${GITHUB_REF_NAME} ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git . + - name: Validate every Dockerfile + compose block declares BUILD_SHA + run: | + python3 - <<'PY' + import re, sys, glob + fails = [] + # 1. Each Dockerfile must have ARG BUILD_SHA + ENV BUILD_SHA=${BUILD_SHA} + for df in sorted(glob.glob("*/Dockerfile")): + # Skip nested non-canonical Dockerfiles (e.g. admin-compliance/ai-compliance-sdk/Dockerfile) + if df.count("/") > 1: continue + src = open(df).read() + if "ARG BUILD_SHA" not in src: + fails.append(f"{df}: missing ARG BUILD_SHA") + if "ENV BUILD_SHA" not in src: + fails.append(f"{df}: missing ENV BUILD_SHA") + # 2. Every build: block in docker-compose.yml must pass BUILD_SHA + import yaml + compose = yaml.safe_load(open("docker-compose.yml")) + for name, svc in (compose.get("services") or {}).items(): + build = svc.get("build") + if not isinstance(build, dict): + continue # skipping pre-built image refs + args = (build.get("args") or {}) + if "BUILD_SHA" not in args: + fails.append(f"docker-compose.yml: service '{name}' build.args missing BUILD_SHA") + if fails: + print("::error::BUILD_SHA integrity check failed:") + for f in fails: print(f" - {f}") + sys.exit(1) + print(f"OK: BUILD_SHA wired in all Dockerfiles + compose build blocks.") + PY + # ── OpenAPI contract validation (always) ───────────────────────────────── validate-canonical-controls: runs-on: docker diff --git a/ai-compliance-sdk/Dockerfile b/ai-compliance-sdk/Dockerfile index 4e27e624..97a10bce 100644 --- a/ai-compliance-sdk/Dockerfile +++ b/ai-compliance-sdk/Dockerfile @@ -38,6 +38,9 @@ RUN adduser -D -u 1000 appuser USER appuser # Expose port +ARG BUILD_SHA="unknown" +ENV BUILD_SHA=${BUILD_SHA} + EXPOSE 8090 # Health check diff --git a/compliance-tts-service/Dockerfile b/compliance-tts-service/Dockerfile index 76428078..3c566b66 100644 --- a/compliance-tts-service/Dockerfile +++ b/compliance-tts-service/Dockerfile @@ -35,6 +35,9 @@ RUN if [ -f /etc/ImageMagick-6/policy.xml ]; then sed -i "s/rights=\"none\" RUN chown -R ttsuser:ttsuser /app USER ttsuser +ARG BUILD_SHA="unknown" +ENV BUILD_SHA=${BUILD_SHA} + EXPOSE 8095 HEALTHCHECK --interval=30s --timeout=10s --start-period=120s --retries=3 CMD curl -sf http://127.0.0.1:8095/health || exit 1 diff --git a/developer-portal/Dockerfile b/developer-portal/Dockerfile index 2dae055a..0d1c4bc9 100644 --- a/developer-portal/Dockerfile +++ b/developer-portal/Dockerfile @@ -39,6 +39,9 @@ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static USER nextjs # Expose port +ARG BUILD_SHA="unknown" +ENV BUILD_SHA=${BUILD_SHA} + EXPOSE 3000 # Set hostname diff --git a/docker-compose.yml b/docker-compose.yml index 15a0087f..d2c767c6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -46,6 +46,7 @@ services: args: NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:-https://macmini:8002} NEXT_PUBLIC_SDK_URL: ${NEXT_PUBLIC_SDK_URL:-https://macmini:8093} + BUILD_SHA: ${BUILD_SHA:-unknown} container_name: bp-compliance-admin platform: linux/arm64 expose: @@ -71,6 +72,8 @@ services: developer-portal: build: + args: + BUILD_SHA: ${BUILD_SHA:-unknown} context: ./developer-portal dockerfile: Dockerfile container_name: bp-compliance-developer-portal @@ -88,6 +91,8 @@ services: # ========================================================= backend-compliance: build: + args: + BUILD_SHA: ${BUILD_SHA:-unknown} context: ./backend-compliance dockerfile: Dockerfile container_name: bp-compliance-backend @@ -142,6 +147,8 @@ services: # ========================================================= ai-compliance-sdk: build: + args: + BUILD_SHA: ${BUILD_SHA:-unknown} context: ./ai-compliance-sdk dockerfile: Dockerfile container_name: bp-compliance-ai-sdk @@ -186,6 +193,8 @@ services: # ========================================================= compliance-tts-service: build: + args: + BUILD_SHA: ${BUILD_SHA:-unknown} context: ./compliance-tts-service dockerfile: Dockerfile container_name: bp-compliance-tts @@ -215,6 +224,8 @@ services: # ========================================================= dsms-node: build: + args: + BUILD_SHA: ${BUILD_SHA:-unknown} context: ./dsms-node dockerfile: Dockerfile container_name: bp-compliance-dsms-node @@ -238,6 +249,8 @@ services: dsms-gateway: build: + args: + BUILD_SHA: ${BUILD_SHA:-unknown} context: ./dsms-gateway dockerfile: Dockerfile container_name: bp-compliance-dsms-gateway @@ -259,6 +272,8 @@ services: # ========================================================= consent-tester: build: + args: + BUILD_SHA: ${BUILD_SHA:-unknown} context: ./consent-tester dockerfile: Dockerfile container_name: bp-compliance-consent-tester @@ -293,6 +308,8 @@ services: document-crawler: build: + args: + BUILD_SHA: ${BUILD_SHA:-unknown} context: ./document-crawler dockerfile: Dockerfile container_name: bp-compliance-document-crawler @@ -326,6 +343,8 @@ services: # ========================================================= docs: build: + args: + BUILD_SHA: ${BUILD_SHA:-unknown} context: . dockerfile: docs-src/Dockerfile container_name: bp-compliance-docs diff --git a/docs-site/Dockerfile b/docs-site/Dockerfile new file mode 100644 index 00000000..9e62e271 --- /dev/null +++ b/docs-site/Dockerfile @@ -0,0 +1,51 @@ +# ============================================ +# BreakPilot Compliance Dokumentation - MkDocs Build +# Multi-stage build fuer minimale Image-Groesse +# ============================================ + +# Stage 1: Build MkDocs Site +FROM python:3.11-slim AS builder + +WORKDIR /docs + +RUN pip install --no-cache-dir \ + mkdocs==1.6.1 \ + mkdocs-material==9.5.47 \ + pymdown-extensions==10.12 + +COPY mkdocs.yml /docs/ +COPY docs-src/ /docs/docs-src/ + +RUN mkdocs build + +# Stage 2: Serve with Nginx +FROM nginx:alpine + +COPY --from=builder /docs/docs-site /usr/share/nginx/html + +RUN echo 'server { \ + listen 80; \ + server_name localhost; \ + root /usr/share/nginx/html; \ + index index.html; \ + location / { \ + try_files $uri $uri/ /index.html; \ + } \ + gzip on; \ + gzip_types text/plain text/css application/json application/javascript text/xml application/xml; \ + gzip_min_length 1000; \ + location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ { \ + expires 1y; \ + add_header Cache-Control "public, immutable"; \ + } \ +}' > /etc/nginx/conf.d/default.conf + +ARG BUILD_SHA="unknown" +ENV BUILD_SHA=${BUILD_SHA} + +EXPOSE 80 + +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD wget --no-verbose --tries=1 --spider http://localhost/ || exit 1 + +CMD ["nginx", "-g", "daemon off;"] diff --git a/docs-src/Dockerfile b/docs-src/Dockerfile index 89d44ec0..45ef3f8f 100644 --- a/docs-src/Dockerfile +++ b/docs-src/Dockerfile @@ -40,6 +40,9 @@ RUN echo 'server { \ } \ }' > /etc/nginx/conf.d/default.conf +ARG BUILD_SHA="unknown" +ENV BUILD_SHA=${BUILD_SHA} + EXPOSE 80 HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ diff --git a/document-crawler/Dockerfile b/document-crawler/Dockerfile index 7a25e578..8d0ef763 100644 --- a/document-crawler/Dockerfile +++ b/document-crawler/Dockerfile @@ -27,6 +27,9 @@ ENV CRAWL_BASE_PATH=/data/crawl ENV MAX_FILE_SIZE_MB=50 # Expose port +ARG BUILD_SHA="unknown" +ENV BUILD_SHA=${BUILD_SHA} + EXPOSE 8098 # Health check diff --git a/dsms-gateway/Dockerfile b/dsms-gateway/Dockerfile index b04598dd..f0f656ce 100644 --- a/dsms-gateway/Dockerfile +++ b/dsms-gateway/Dockerfile @@ -22,6 +22,9 @@ ENV IPFS_GATEWAY_URL=http://dsms-node:8080 ENV PORT=8082 # Expose port +ARG BUILD_SHA="unknown" +ENV BUILD_SHA=${BUILD_SHA} + EXPOSE 8082 # Health check diff --git a/dsms-node/Dockerfile b/dsms-node/Dockerfile index 310076d8..ab946f00 100644 --- a/dsms-node/Dockerfile +++ b/dsms-node/Dockerfile @@ -14,6 +14,9 @@ ENV IPFS_PROFILE=server # 4001 - Swarm (P2P) # 5001 - API # 8080 - Gateway +ARG BUILD_SHA="unknown" +ENV BUILD_SHA=${BUILD_SHA} + EXPOSE 4001 EXPOSE 5001 EXPOSE 8080