From 49b741243e7ec4b9c95fd3db148d72fcbe34d039 Mon Sep 17 00:00:00 2001 From: Ben Stull Date: Wed, 3 Jun 2026 22:41:49 -0700 Subject: [PATCH] test(tier1): docker compose stack (gitea seed + backend + web + mailpit) Wires the four-service Tier-1 integration stack and brings it up green. Fixes found during live bring-up: - gitea-admin-init: run as user 'git'; the gitea CLI refuses to run as root and our custom entrypoint bypasses the image's s6 init that normally drops privileges. - backend.Dockerfile: COPY VERSION to /VERSION; health.py reads the canonical VERSION file from the repo root (parents[2]) at import, so it must exist in the image or the backend crashes on startup. env_file cold-start: generated/.env.tier1.generated is created as an empty placeholder (gitignored) so the backend env_file path always exists; the seeder overwrites it before backend starts. Co-Authored-By: Claude Opus 4.8 --- Makefile | 14 ++++++ testing/backend.Dockerfile | 4 ++ testing/docker-compose.yml | 94 ++++++++++++++++++++++++++++++++++++ testing/generated/.gitignore | 1 + testing/generated/.gitkeep | 0 5 files changed, 113 insertions(+) create mode 100644 Makefile create mode 100644 testing/docker-compose.yml create mode 100644 testing/generated/.gitignore create mode 100644 testing/generated/.gitkeep diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a0f0cbf --- /dev/null +++ b/Makefile @@ -0,0 +1,14 @@ +tier1-up: + docker compose -f testing/docker-compose.yml up --build -d + +tier1-down: + docker compose -f testing/docker-compose.yml down -v + +tier1-logs: + docker compose -f testing/docker-compose.yml logs -f + +fe-unit: + cd frontend && npm run test:run + +e2e: + cd e2e && BASE_URL=$${BASE_URL:-http://localhost:8080} MAILSINK_URL=$${MAILSINK_URL:-http://localhost:8025} npm run e2e diff --git a/testing/backend.Dockerfile b/testing/backend.Dockerfile index dee5492..e626cc7 100644 --- a/testing/backend.Dockerfile +++ b/testing/backend.Dockerfile @@ -7,6 +7,10 @@ COPY backend/requirements.txt /app/requirements.txt RUN pip install --no-cache-dir -r requirements.txt uvicorn COPY backend/ /app/ +# ยง20.1: health.py reads the canonical VERSION file from the repo root +# (parents[2] of backend/app/health.py). In the image that resolves to +# /VERSION, so the file must be copied there or backend import crashes. +COPY VERSION /VERSION RUN mkdir -p /data EXPOSE 8000 diff --git a/testing/docker-compose.yml b/testing/docker-compose.yml new file mode 100644 index 0000000..f329435 --- /dev/null +++ b/testing/docker-compose.yml @@ -0,0 +1,94 @@ +name: rfc-tier1 + +services: + gitea: + image: gitea/gitea:1.22 + environment: + GITEA__security__INSTALL_LOCK: "true" + GITEA__server__ROOT_URL: "http://gitea:3000/" + GITEA__server__HTTP_PORT: "3000" + GITEA__database__DB_TYPE: "sqlite3" + GITEA__webhook__ALLOWED_HOST_LIST: "*" + healthcheck: + test: ["CMD", "curl", "-sf", "http://localhost:3000/api/healthz"] + interval: 5s + timeout: 3s + retries: 30 + ports: + - "3001:3000" + + gitea-admin-init: + image: gitea/gitea:1.22 + # The gitea CLI refuses to run as root (loadRunModeFrom [F] "not + # supposed to be run as root"). Our custom entrypoint bypasses the + # image's s6 init that normally drops to the git user, so set it here. + user: git + depends_on: + gitea: + condition: service_healthy + volumes_from: + - gitea + entrypoint: ["/bin/sh", "-c"] + command: + - > + gitea admin user create --admin --username giteaadmin + --password giteaadmin-pass --email admin@example.test + --must-change-password=false || true + restart: "no" + + gitea-seed: + image: alpine:3.20 + depends_on: + gitea-admin-init: + condition: service_completed_successfully + env_file: + - .env.tier1 + environment: + GITEA_ADMIN_USER: giteaadmin + GITEA_ADMIN_PASSWORD: giteaadmin-pass + GITEA_BOT_PASSWORD: rfc-bot-pass + SEED_OUT: /seed/.env.tier1.generated + volumes: + - ./seed-gitea.sh:/seed-gitea.sh:ro + - ./generated:/seed + entrypoint: ["/bin/sh", "-c"] + command: + - apk add --no-cache curl >/dev/null && sh /seed-gitea.sh + restart: "no" + + backend: + build: + context: .. + dockerfile: testing/backend.Dockerfile + depends_on: + gitea-seed: + condition: service_completed_successfully + env_file: + - .env.tier1 + - ./generated/.env.tier1.generated + volumes: + - backend-data:/data + healthcheck: + test: ["CMD", "python", "-c", "import urllib.request,sys; sys.exit(0 if urllib.request.urlopen('http://localhost:8000/api/health').status==200 else 1)"] + interval: 5s + timeout: 3s + retries: 30 + + web: + build: + context: .. + dockerfile: testing/web.Dockerfile + depends_on: + backend: + condition: service_healthy + ports: + - "8080:80" + + mailpit: + image: axllent/mailpit:latest + ports: + - "8025:8025" + - "1025:1025" + +volumes: + backend-data: diff --git a/testing/generated/.gitignore b/testing/generated/.gitignore new file mode 100644 index 0000000..aec062d --- /dev/null +++ b/testing/generated/.gitignore @@ -0,0 +1 @@ +.env.tier1.generated diff --git a/testing/generated/.gitkeep b/testing/generated/.gitkeep new file mode 100644 index 0000000..e69de29