refactor(ci): one verify-stack stage for all live-stack checks (closes #58) (refs #46 #56)
All checks were successful
CI / lint (pull_request) Successful in 51s
CI / build (pull_request) Successful in 40s
CI / unit (pull_request) Successful in 48s
CI / mutation (pull_request) Successful in 1m36s
CI / verify-stack (pull_request) Successful in 4m37s

On the single self-hosted runner CI jobs run sequentially, so booting OpenZaak once
beats once-per-job. Replace the integration + notifications + compose-smoke jobs with
one verify-stack job that brings the full stack up once and runs, as clearly-named
steps: health (make verify-up, the DoD smoke) → ACL ↔ OpenZaak (verify-acl) →
OpenZaak → NRC delivery (verify-nrc) → teardown (always) + log dump on failure.

The check logic moves into stack-agnostic runners (run-acl-integration.sh,
run-notification-check.sh) that operate on whatever stack is already up, reaching
services by container IP. The local single-concern wrappers (make integration oz-only,
make verify-notifications oz+nrc) keep working by delegating to the same runners, so
nothing is duplicated. make ci now runs the consolidated 'verify' stage.

Verified locally: make verify boots the full stack once, ACL integration passes and
the NRC notification is delivered, then tears down.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-29 16:48:38 +02:00
parent a256db1a23
commit d49443353e
10 changed files with 228 additions and 186 deletions

View File

@@ -17,20 +17,21 @@ 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``infra/run-integration.sh`: OpenZaak up → seed a **published** BIG zaaktype + run `Acl.IntegrationTests` **as containers inside the compose network** → tear down | container engine + egress (base images, nuget, `selectielijst.openzaak.nl`) |
| `notifications` | `make verify-notifications` → bring up OpenZaak + NRC → seed → assert a zaak-create notification reaches a subscriber (containers on the network) → tear down | container engine + egress (base images, `selectielijst.openzaak.nl`) |
| `compose-smoke` | `make smoke` → seed config volumes → `up -d` (full stack) → `up --wait` durable services → `down` | container engine + compose v2 |
| `verify-stack` | the single live-stack stage — steps: `make verify-up` (full stack up + health, the DoD smoke) → `make verify-acl` (ACL ↔ OpenZaak) → `make verify-nrc` (OpenZaak → NRC delivery) → `make down` | container engine + egress (base images, nuget, `selectielijst.openzaak.nl`) |
> **The `integration` job needs no `setup-dotnet`.** dotnet runs inside the test
> image, and both the seed and the test join the OpenZaak network and reach it by
> container IP — so the runner never has to reach a published port
> (see [gitea-actions-gotchas.md §5](gitea-actions-gotchas.md)).
> **Why one `verify-stack` job, not three.** The single self-hosted runner runs jobs
> **sequentially**, so booting OpenZaak once (instead of once per check) is the
> cheapest layout (issue #58). It subsumes the old `integration`, `notifications`, and
> `compose-smoke` jobs — the bring-up step *is* the "compose up reaches green health"
> gate. No `setup-dotnet`: the ACL test runs in a built image and every check reaches
> services by **container IP** (the runner can't reach published ports — see
> [gitea-actions-gotchas.md §5/§6](gitea-actions-gotchas.md)).
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.
> **`compose-smoke` runs on a containerized runner.** Workspace bind mounts do
> **`verify-stack` runs on a containerized runner.** Workspace bind mounts do
> **not** reach the sibling containers Compose starts, so config/assets are
> streamed into external named volumes via `docker cp` (`infra/seed-config.sh`),
> and the upstream images are used verbatim (no build). If you add a service that
@@ -92,19 +93,23 @@ the containerized CI runner). Keep the two files in sync.
## Running CI locally (`make ci`)
Until the runner exists, run the full pipeline yourself before pushing:
`make ci` runs the exact same checks as the pipeline — handy to run before pushing:
```bash
make ci # lint + build + unit + mutation + smoke — the fast pipeline lanes
make ci # lint + build + unit + mutation + verify — mirrors the pipeline
make lint # or a single stage
make mutation # Stryker.NET ratchet on the ACL
make smoke # compose up --wait, curl /health, tear down
make integration # ACL ↔ real OpenZaak (its own CI job; not part of `make ci`)
make verify # the live-stack stage: full stack up once → ACL + NRC checks → down
```
> `make integration` is a separate, heavier lane (it stands the OpenZaak stack up and
> seeds a published zaaktype), so it is **not** folded into `make ci`. Run it before
> pushing changes that touch the ACL gateway or the OpenZaak seed. See ADR-0006.
> **`make verify`** mirrors the CI `verify-stack` job: it boots the full stack once and
> runs both the ACL ↔ OpenZaak and OpenZaak → NRC checks against it. For fast,
> single-concern local iteration use a lighter throwaway stack instead:
>
> ```bash
> make integration # ACL ↔ OpenZaak only (no NRC)
> make verify-notifications # OpenZaak → NRC delivery only
> ```
**Prerequisites:** .NET 10 SDK, a container engine with Compose v2, and `curl`.

View File

@@ -173,7 +173,7 @@ reality is a service name or an IPv4 literal — and only the IP passes.
**Fix** — in-network tooling reaches OpenZaak/NRC by **container IP**
(`docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'`), not
service name; the notif verify harness also registers the sink callback by IP.
(`infra/run-integration.sh`, `infra/verify-notifications.sh`.)
(`infra/run-acl-integration.sh`, `infra/run-notification-check.sh`.)
**Related — abonnement callbacks must enforce auth.** NRC probes a callback when an
abonnement is registered and refuses it (`no-auth-on-callback-url`) unless it returns