From d9f3477780d1ca3f025e1f786a69ddd872b87e57 Mon Sep 17 00:00:00 2001 From: Pouzor Date: Sat, 28 Mar 2026 13:03:28 +0100 Subject: [PATCH] fix(ci): inject bcrypt hash via compose environment with $$ escaping, remove dynamic hash generation --- .github/workflows/docker-ci.yml | 33 +++++++++++---------------------- docker-compose.ci.yml | 10 +++++++++- 2 files changed, 20 insertions(+), 23 deletions(-) diff --git a/.github/workflows/docker-ci.yml b/.github/workflows/docker-ci.yml index 5c0c95b..227be16 100644 --- a/.github/workflows/docker-ci.yml +++ b/.github/workflows/docker-ci.yml @@ -12,19 +12,10 @@ jobs: steps: - uses: actions/checkout@v4 - # ── Generate a bcrypt hash for the CI test password ──────────────────── - - name: Generate CI password hash - id: pwhash - run: | - pip install --quiet passlib bcrypt - HASH=$(python3 -c "from passlib.context import CryptContext; print(CryptContext(schemes=['bcrypt']).hash('ci-password-123'))") - echo "hash=$HASH" >> "$GITHUB_OUTPUT" - - # ── Write a minimal .env consumed by docker-compose.yml ─────────────── + # ── Write a minimal .env required by env_file: .env in docker-compose.yml ─ + # AUTH_PASSWORD_HASH is NOT set here — it's injected via docker-compose.ci.yml + # environment section using $$ escaping to avoid docker-compose $VAR expansion. - name: Write .env - # PW_HASH is set as an env var so bash does not expand the '$' chars in the bcrypt hash - env: - PW_HASH: ${{ steps.pwhash.outputs.hash }} run: | { echo "SECRET_KEY=ci-only-secret-key-not-for-production" @@ -34,19 +25,17 @@ jobs: echo "STATUS_CHECKER_INTERVAL=300" echo "MCP_API_KEY=ci-mcp-key" echo "MCP_SERVICE_KEY=ci-svc-key" - echo "AUTH_USERNAME=admin" - printf 'AUTH_PASSWORD_HASH=%s\n' "$PW_HASH" } > .env - # ── Build + start backend and frontend (skip mcp) ───────────────────── - # docker-compose.ci.yml adds ports: 8000:8000 so the runner can reach the backend + # ── Build + start backend and frontend (skip mcp) ───────────────────────── + # docker-compose.ci.yml: exposes port 8000 + injects AUTH_* env vars - name: Build images run: docker compose -f docker-compose.yml -f docker-compose.ci.yml build backend frontend - name: Start stack run: docker compose -f docker-compose.yml -f docker-compose.ci.yml up -d backend frontend - # ── Wait for backend to be healthy (max 60 s) ───────────────────────── + # ── Wait for backend to be healthy (max 60 s) ───────────────────────────── - name: Wait for backend health run: | echo "Waiting for backend..." @@ -58,16 +47,16 @@ jobs: sleep 2 done echo "Backend did not become healthy in time" >&2 - docker compose logs backend + docker compose -f docker-compose.yml -f docker-compose.ci.yml logs backend exit 1 - # ── Smoke: frontend serves HTML ──────────────────────────────────────── + # ── Smoke: frontend serves HTML ─────────────────────────────────────────── - name: Smoke — frontend returns 200 run: | STATUS=$(curl -so /dev/null -w "%{http_code}" http://localhost:3000/) [ "$STATUS" = "200" ] || { echo "Frontend returned $STATUS"; exit 1; } - # ── Tier 3: integration tests against the live stack ────────────────── + # ── Tier 3: integration tests against the live stack ────────────────────── - uses: actions/setup-python@v5 with: python-version: '3.11' @@ -79,12 +68,12 @@ jobs: env: INTEGRATION_BASE_URL: http://localhost:8000 INTEGRATION_USERNAME: admin - INTEGRATION_PASSWORD: ci-password-123 + INTEGRATION_PASSWORD: admin run: | cd backend pytest tests/test_integration.py -v - # ── Teardown ─────────────────────────────────────────────────────────── + # ── Teardown ────────────────────────────────────────────────────────────── - name: Dump logs on failure if: failure() run: docker compose -f docker-compose.yml -f docker-compose.ci.yml logs diff --git a/docker-compose.ci.yml b/docker-compose.ci.yml index dd464c4..4c995dd 100644 --- a/docker-compose.ci.yml +++ b/docker-compose.ci.yml @@ -1,6 +1,14 @@ -# CI override — exposes backend port 8000 so smoke/integration tests can reach it. +# CI override — exposes backend port 8000 and injects credentials. # Usage: docker compose -f docker-compose.yml -f docker-compose.ci.yml up -d backend frontend +# +# AUTH_PASSWORD_HASH uses $$ escaping: docker-compose converts $$ → $ before +# passing to the container, so the backend receives a valid bcrypt hash. +# This avoids the project .env file being subject to docker-compose $VAR expansion. services: backend: ports: - "8000:8000" + environment: + AUTH_USERNAME: admin + # bcrypt hash of "admin" — $$ is docker-compose escape for literal $ + AUTH_PASSWORD_HASH: $$2b$$12$$RtMbyw17l4N5UGzeXMNAWuzCaVV.XFBY7ZetWheQhxcBDcxahapkG