ci(acl): keep the integration lane local-only; document the runner gap (refs #46)
All checks were successful
CI / lint (pull_request) Successful in 49s
CI / build (pull_request) Successful in 42s
CI / unit (pull_request) Successful in 50s
CI / mutation (pull_request) Successful in 1m31s
CI / compose-smoke (pull_request) Successful in 3m54s

The hosted Gitea runner starts the OpenZaak stack as sibling containers via the
host daemon, so a process on the runner can't reach the published ports — the seed
and dotnet test get Connection refused on localhost:8000. Drop the (non-working)
integration CI job; make integration stays the local / host-runner gate. Document
the limitation in gitea-actions-gotchas.md §5 and the CI runbook, and track running
it inside the compose network in #55. ADR-0006 updated accordingly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-29 12:04:44 +02:00
parent 855a5565fe
commit 3829cb0b68
4 changed files with 51 additions and 23 deletions

View File

@@ -63,24 +63,11 @@ jobs:
path: services/acl/StrykerOutput/**/reports/mutation-report.html
if-no-files-found: warn
integration:
runs-on: ubuntu-latest
steps:
- uses: https://github.com/actions/checkout@v4
- uses: https://github.com/actions/setup-dotnet@v4
with:
dotnet-version: '10.0.x'
# `make integration` brings the OpenZaak stack up, seeds a PUBLISHED BIG
# zaaktype (OZ_PUBLISH=1) and runs the Integration-category tests, then tears
# down. Needs Docker + python3 (both on ubuntu-latest) and outbound access to
# selectielijst.openzaak.nl from the OpenZaak container (see ADR-0006).
- run: make integration
- name: dump OpenZaak logs on failure
if: failure()
run: docker compose -f infra/openzaak/docker-compose.yml logs --no-color --tail=80 oz-init openzaak 2>&1 || true
- name: tear down on failure
if: failure()
run: docker compose -f infra/openzaak/docker-compose.yml down --volumes 2>&1 || true
# NOTE: there is deliberately no `integration` job here yet. `make integration`
# runs the ACL ↔ real-OpenZaak test locally / on a host-executing runner, but on
# the hosted runner a process on the runner cannot reach the stack's published
# ports (sibling containers — see gitea-actions-gotchas.md §5). Running it inside
# the compose network is tracked in #55.
compose-smoke:
runs-on: ubuntu-latest

View File

@@ -47,8 +47,8 @@ itself. No new test dependency is added.**
- **The lane is kept out of the fast checks.** `make unit` runs with
`--filter "Category!=Integration"`; Stryker is pinned to `Acl.Tests` (`test-projects`), so
neither the unit nor the mutation lane needs a live stack. A new `make integration` target
brings the stack up, seeds, runs the lane, and always tears down — mirrored by a Gitea
Actions `integration` job. This matches `make` being the single source of truth (ADR-0005).
brings the stack up, seeds, runs the lane, and always tears down. This matches `make` being
the single source of truth (ADR-0005).
- **Publishing is opt-in in the seed.** `infra/openzaak/seed_catalogus.py` gains an
`OZ_PUBLISH=1` path that adds the relations OpenZaak's publish requires — two statustypen
(begin/eind), a roltype, and a resultaattype whose Selectielijst procestype is matched onto
@@ -69,8 +69,13 @@ itself. No new test dependency is added.**
`selectielijst.openzaak.nl`. It is a stable public reference API (the same one OpenZaak uses
in production) but it is a network touchpoint, and a CI environment without egress would need
a local Selectielijst service or a recorded fixture. `OZ_SELECTIELIJST` overrides the base URL.
- **Cost:** the lane needs the stack up first; CI runs it as a dedicated job (Docker + dotnet +
python3), separate from the fast lanes.
- **Cost:** the lane needs the stack up first, so it is separate from the fast lanes.
- **Not yet a hosted-runner job.** `make integration` passes locally and on a host-executing
runner, but on Gitea's hosted runner a process *on the runner* cannot reach the stack's
published ports — Compose starts sibling containers via the host daemon, so the ports bind to
the host, not the runner (gitea-actions-gotchas.md §5, the same split as §1). Running the test
and seed *inside* the compose network (via a built image, not bind mounts) to gate it in CI is
tracked in **#55**. Until then the lane runs locally and is not a merge gate.
## Alternatives considered

View File

@@ -17,9 +17,14 @@ and CI cannot drift:
| `build` | `make build``dotnet build … -c Release` | .NET 10 SDK |
| `unit` | `make unit``dotnet test … -c Release --filter "Category!=Integration"` | .NET 10 SDK |
| `mutation` | `make mutation``dotnet tool restore``dotnet stryker` (ACL); uploads the HTML report as an artifact | .NET 10 SDK |
| `integration` | `make integration``openzaak-up` → seed a **published** BIG zaaktype (`OZ_PUBLISH=1`) → `dotnet test … --filter "Category=Integration"` → tear down | .NET 10 SDK + container engine + python3 + egress to `selectielijst.openzaak.nl` |
| `compose-smoke` | `make smoke` → seed config volumes → `up -d` (full stack) → `up --wait` durable services → `down` | container engine + compose v2 |
> **`make integration` is not a hosted-runner job yet.** The ACL ↔ real-OpenZaak
> test (`make integration`, ADR-0006) passes locally and on a host-executing runner,
> but a process on the hosted runner can't reach the stack's published ports
> (sibling containers — see [gitea-actions-gotchas.md §5](gitea-actions-gotchas.md)).
> Run it inside the compose network to gate it in CI — tracked in **#55**.
All `uses:` references are absolute, tag-pinned URLs (`https://github.com/actions/checkout@v4`,
`https://github.com/actions/setup-dotnet@v4`) per CLAUDE.md §8.7 and §15 — Gitea
Actions resolves them from GitHub.

View File

@@ -124,3 +124,34 @@ needed). v3 uses the older artifact protocol that Gitea implements, and has no G
guard. Inputs are the same (`name`, `path`, `if-no-files-found`), so it is a drop-in
swap. Do **not** bump to `@v4` until act_runner advertises github.com-compatible
artifact support.
---
## 5. A runner process can't reach a service container's published port
**Symptom** — green locally, but a CI step that runs *on the runner* and talks to a
compose service over `localhost` fails. The ACL integration test's seed died with:
```
OpenZaak ready (000)
urllib.error.URLError: <urlopen error [Errno 111] Connection refused>
make: *** [Makefile:114: integration] Error 1
```
OpenZaak was demonstrably up — uwsgi had been serving for ~2 minutes — yet
`curl`/`urllib` to `localhost:8000` from the runner were refused the whole time.
**Why** — the same sibling-container split as §1. Compose starts the stack via the
host daemon, so `ports: ["8000:8000"]` publishes to the *daemon host*, not to the job
container. From the runner, `localhost:8000` has nothing listening. (`make smoke`
sidesteps this by polling readiness via `docker inspect` (§2), never a service port.)
**Fix** — don't talk to service ports from the runner. Either check state via `docker
inspect` (health), or run the client **inside the compose network** so it reaches the
service by name (`http://openzaak:8000`). For a test/seed that needs the repo's own
code, deliver it via a **built image** (not a bind mount — §1), then
`docker run --network <stack>_cg …`.
**Status**`make integration` (the ACL ↔ real-OpenZaak test, ADR-0006) therefore
runs locally / on a host-executing runner only. Gating it on the hosted runner via
the compose network is tracked in **#55**.