diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 87e03c9..8d979bb 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -29,14 +29,16 @@ jobs: - name: Strict staging release safety check run: >- RELEASE_ENV=staging APP_MODE=live - APP_RELEASE_ID=ci-$(git rev-parse --short HEAD) + APP_RELEASE_ID=ci-${{ gitea.sha }} CONVEX_IMAGE=ghcr.io/get-convex/convex-backend@sha256:3aa2ccd6697a75de53948b1ee531d7c9688fe09ef360748d75df46dfc888f69a CONVEX_DASHBOARD_IMAGE=ghcr.io/get-convex/convex-dashboard@sha256:63e94c62940a09acd36464e02e1b0a7589f7ef48227003de78510f9523bb4e6b bun run release:check container: - # Requires a runner with Docker/Compose. This job is prepared but remains - # unverified until Gitea reports an actual compatible runner. + # Docker builds run only for trusted pushes. Do not run this job for PR + # code on a shared/privileged runner; use a dedicated rootless runner once + # one is registered and verified in Gitea. + if: ${{ gitea.event_name == 'push' }} runs-on: ubuntu-latest steps: - name: Checkout @@ -44,4 +46,25 @@ jobs: - name: Validate Compose expansion run: docker compose config --quiet - name: Build immutable web artifact - run: docker build --build-arg APP_RELEASE_ID=fixture --tag eve-intelligence:fixture . + run: docker build --build-arg APP_RELEASE_ID=fixture-ci --tag eve-intelligence:fixture . + - name: Run isolated container smoke + shell: bash + run: | + set -euo pipefail + volume="eden-ci-reference-${GITHUB_RUN_ID:-trusted-push}" + container="eden-ci-web-${GITHUB_RUN_ID:-trusted-push}" + cleanup() { docker rm --force "$container" >/dev/null 2>&1 || true; docker volume rm "$volume" >/dev/null 2>&1 || true; } + trap cleanup EXIT + docker volume create "$volume" >/dev/null + docker run --rm --network none --user root -v "$volume:/app/data/reference" eve-intelligence:fixture \ + bun workers/reference.ts --fixture --directory /app/data/reference + docker run --detach --name "$container" --network none \ + -e APP_MODE=demo -e APP_ORIGIN=http://127.0.0.1:3000 -e ORIGIN=http://127.0.0.1:3000 \ + -e HOST=127.0.0.1 -e PORT=3000 -e REFERENCE_DIR=/app/data/reference -e APP_RELEASE_ID=fixture-ci \ + -v "$volume:/app/data/reference:ro" eve-intelligence:fixture >/dev/null + for attempt in $(seq 1 60); do + if docker exec "$container" bun -e "const r = await fetch('http://127.0.0.1:3000'); if (r.status !== 200 || r.headers.get('x-application-release') !== 'fixture-ci') process.exit(1);"; then break; fi + if [ "$attempt" = 60 ]; then echo 'container smoke did not become ready' >&2; exit 1; fi + sleep 0.5 + done + docker exec "$container" bun -e "const r = await fetch('http://127.0.0.1:3000/assets'); if (r.status !== 200) process.exit(1);" diff --git a/docs/release-safety.md b/docs/release-safety.md index 3f7a8aa..d371701 100644 --- a/docs/release-safety.md +++ b/docs/release-safety.md @@ -83,8 +83,10 @@ Read-only `tea` inspection on 2026-09-19 reported no Gitea workflows or runs for `jb/eden`; runner labels, Docker availability, and action availability are therefore not validated. The workflow is prepared locally but has not run remotely. The source job runs a strict staging release check after building -the client, and the separate container job validates Compose expansion and -builds the digest-pinned web image. +the client. The separate container job is restricted to trusted pushes, +validates Compose expansion, builds the digest-pinned web image, compiles a +synthetic reference into a private volume, and smoke-tests the runtime with +`--network none`; these steps still need an actual compatible Gitea runner. No browser automation, OAuth callback failure injection, Convex migration rollback, instance-credential backup/restore, key rotation, or live revocation/transfer evidence is claimed by this lane. Those remain explicit