fix(harness): bake cf-proxy nginx.conf at build time, not via configs:
All checks were successful
CodeQL / Analyze (${{ matrix.language }}) (go) (pull_request) Successful in 0s
CodeQL / Analyze (${{ matrix.language }}) (javascript-typescript) (pull_request) Successful in 0s
CodeQL / Analyze (${{ matrix.language }}) (python) (pull_request) Successful in 1s
pr-guards / disable-auto-merge-on-push (pull_request) Successful in 3s
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 5s
Check merge_group trigger on required workflows / Required workflows have merge_group trigger (pull_request) Successful in 5s
branch-protection drift check / Branch protection drift (pull_request) Successful in 8s
CI / Detect changes (pull_request) Successful in 8s
Lint curl status-code capture / Scan workflows for curl status-capture pollution (pull_request) Successful in 7s
E2E API Smoke Test / detect-changes (pull_request) Successful in 7s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 8s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 8s
Harness Replays / detect-changes (pull_request) Successful in 8s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 7s
Runtime PR-Built Compatibility / detect-changes (pull_request) Successful in 9s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 2s
CI / Platform (Go) (pull_request) Successful in 4s
CI / Python Lint & Test (pull_request) Successful in 3s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 4s
CI / Canvas (Next.js) (pull_request) Successful in 5s
CI / Canvas Deploy Reminder (pull_request) Has been skipped
Runtime PR-Built Compatibility / PR-built wheel + import smoke (pull_request) Successful in 4s
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 5s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 49s
Harness Replays / Harness Replays (pull_request) Successful in 50s

The previous configs:-based fix (87b971a2) didn't actually fix the DinD
issue — Compose v2 falls back to bind mounts for `configs:` when swarm
mode is not active, so the resulting runc invocation still tries to
mount /workspace/.../cf-proxy/nginx.conf from the OUTER host filesystem
that the act_runner-vs-host-docker socket-mount can't see. Same
"not a directory" error returned.

Switch to a thin Dockerfile (cf-proxy/Dockerfile) that COPYs nginx.conf
into nginx:1.27-alpine. The build context is uploaded to the daemon as
a tarball, not bind-mounted from the host filesystem, so the path
translation gap doesn't apply. Verified locally: `docker build` +
`docker run cf-proxy nginx -T` reproduces the baked config end-to-end.

Trade-off: ~2-3s build cost on every harness up. Acceptable for the
Gitea CI gate; local-dev re-builds the image only when nginx.conf
changes (Docker layer cache).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent 87b971a292
commit 7eb348536b
2 changed files with 27 additions and 23 deletions

View File

@ -0,0 +1,14 @@
# cf-proxy harness image — nginx + the harness's tenant-routing config baked
# in at build time.
#
# Why bake (not bind-mount): on Gitea Actions / act_runner, the runner is a
# container talking to the OUTER docker daemon over the host socket; runc
# resolves bind-mount source paths on the outer host filesystem, where the
# repo at `/workspace/.../tests/harness/cf-proxy/nginx.conf` is invisible.
# Compose `configs:` (with `file:`) falls back to bind mounts when swarm is
# not active, so it hits the same gap. A build-time COPY uploads the file
# as part of the docker build context — the daemon receives the tarball
# directly and never bind-mounts. See issue #88 item 2.
FROM nginx:1.27-alpine
COPY nginx.conf /etc/nginx/nginx.conf

View File

@ -168,41 +168,31 @@ services:
# subdomain — the Host header carries the tenant identity, not the # subdomain — the Host header carries the tenant identity, not the
# routing destination. Local cf-proxy mirrors this exactly. # routing destination. Local cf-proxy mirrors this exactly.
# #
# nginx.conf delivery: docker compose `configs:` block (not a bind # nginx.conf delivery: built into a custom image via cf-proxy/Dockerfile
# mount) so the file ships as content packaged by compose, not a # (a thin nginx:1.27-alpine + COPY). NOT a bind mount and NOT a
# host-path bind that has to be visible to the docker daemon's runc. # compose `configs:` block, both of which break under Gitea's
# Bind mounts break under Gitea's act_runner DinD because runc # act_runner: the runner talks to the OUTER docker daemon over the
# resolves the source path on the OUTER docker host (the runner's # host socket, and runc resolves bind sources on the outer host
# host filesystem), not inside the runner container — the path # filesystem, where `/workspace/.../tests/harness/cf-proxy/nginx.conf`
# `/workspace/.../tests/harness/cf-proxy/nginx.conf` is only visible # is invisible. Compose `configs:` falls back to bind mounts without
# to the runner, not to the daemon below it. The `configs:` form # swarm, so it hits the same gap. A build context, by contrast, is
# uploads the file to the daemon as part of the service definition # uploaded to the daemon as a tarball at build time — no bind. See
# and is bind-mount-equivalent at the container level. See issue #88 # issue #88 item 2.
# item 2.
cf-proxy: cf-proxy:
image: nginx:1.27-alpine build:
context: ./cf-proxy
dockerfile: Dockerfile
depends_on: depends_on:
tenant-alpha: tenant-alpha:
condition: service_healthy condition: service_healthy
tenant-beta: tenant-beta:
condition: service_healthy condition: service_healthy
configs:
- source: cf-proxy-nginx-conf
target: /etc/nginx/nginx.conf
mode: 0444
# Bind to 127.0.0.1 only — hardcoded ADMIN_TOKENs make 0.0.0.0 # Bind to 127.0.0.1 only — hardcoded ADMIN_TOKENs make 0.0.0.0
# exposure unsafe even on a local network. # exposure unsafe even on a local network.
ports: ports:
- "127.0.0.1:8080:8080" - "127.0.0.1:8080:8080"
networks: [harness-net] networks: [harness-net]
configs:
# Defined once at compose level so any future service (e.g. a second
# nginx variant for an external-connect smoke test) can reuse the
# same source file.
cf-proxy-nginx-conf:
file: ./cf-proxy/nginx.conf
networks: networks:
harness-net: harness-net:
name: molecule-harness-net name: molecule-harness-net