ci: add a Caproni end-to-end job and environment
Summary
Adds an end-to-end job that brings up a Caproni cluster, deploys this branch's build of Artifact Registry into it, and drives the service through its HTTP APIs.
| Path | What |
|---|---|
test/caproni/ |
The environment: AR plus PostgreSQL, Valkey and Garage. Four deployers, no GitLab monolith, no TLS. |
scripts/e2e/ |
install-tools.sh, caproni-up.sh, run-e2e-tests.sh, collect-diagnostics.sh. |
test:e2e:caproni |
The job wiring them together. |
The suite provisions a namespace through the GitLab API (POST /api/gitlab/v1/namespaces), then creates a repository in it through the management API (POST /api/v1/:slug/repositories), asserting an exact status per call plus the response fields and defaults both contracts document — including the idempotent replay (200, same UUID) and the name-conflict 409.
It gates the diff, not a release. scripts/chart-gen.sh stamps the chart's appVersion with the image tag, and the image is loaded into the k3d nodes with k3d image import. Nothing is pushed, so there is no pull secret to provision, no per-merge-request image to garbage-collect, and it behaves identically on the security mirror and on forks, where $CI_REGISTRY_IMAGE would not be readable from the cluster.
This is not the development rig — that is gitlab-org/gitlab-caproni, and caproni/README.md is retargeted to it here (it still pointed at the twice-superseded caproni-demo). Full rationale, local usage, and the deviations from the upstream Caproni snippet: docs/dev/caproni-e2e.md.
Governing ADRs
scripts/adr-freshness.sh reports the mirror stale for 007, 020, 021 and 022. I diffed ADR-020 against upstream: the drift is link rewrites plus an ADR-021 boundary clarification, neither bearing on this change.
- ADR-024 (Infrastructure delivery) — conforms, and exercises it. The environment deploys the chart
scripts/chart-gen.shgenerates from.runway/fairway.yaml— the same descriptor Runway deploys from — rather than a bespoke test chart. - ADR-005 (Artifact delivery mode) — conforms. The environment sets the instance default to
DELIVERY_MODE_PROXY, which is the ADR's documented case for clients that cannot reach the storage backend directly: a pre-signed URL would point at thedeps-garageClusterIP, unreachable from the job container. - ADR-020 (Authentication flow) — conforms, with a coverage gap worth stating. The environment selects the existing
auth.bootstrap_tokendevelopment stub. Token exchange would need the monolith's JWKS and therefore a monolith in the cluster, which is what this environment deliberately omits. The suite does not exercise ADR-020's token-exchange path. - ADR-001 / ADR-007 (Organization anchor, schema) — conforms. The suite provisions with the
(gitlab, organization, <numeric id>)anchor tuple; a non-organization or non-numeric anchor makes usage-data attribution drop the event. - ADR-023 (Code structure) — no conflict. Its locked paths are
cmd/,internal/,internal/format/<name>/and the depguard packages;test/andscripts/are unlocked, and the tree is explicitly "not a mandate".test/caproniis the layout the Caproni testing docs assume.
Testing
The job is the test. Evidence from the branch:
| Pipeline | Commit | Result |
|---|---|---|
| 2767155675 | 9ee22e9a (post-rebase) |
57/57 green, test:e2e:caproni 267s |
| 2763259172 | d55a4b21 |
12/12 green, test:e2e:caproni 249s |
The tip commit df96431e changes comments only — no code or configuration — verified by diffing every touched file with comments stripped. Its pipeline runs with this MR.
Also verified locally: shellcheck, shfmt, yamllint, glab ci lint, markdownlint, Vale, lychee.
An earlier iteration regressed the job from 231s to 456s. That was diagnosed from per-line trace timestamps to an unscoped mise x -- installing the whole repository toolchain (+65s, fixed in cc6a22d1) and a one-off 137s stall in the caproni plugin's release lookup (did not reproduce). The Postgres gate added in 27a6e5ae is net negative cost: it drops the readiness wait from 102s to 17s by keeping AR from crash-looping on a missing database.
Reviewable LOC
1,235 added lines, so per development-model.md here is the split and why it is not split further:
| Group | Code | Comments | Docs |
|---|---|---|---|
scripts/e2e/ (4 scripts) |
367 | 219 | — |
test/caproni/ (3 YAML) |
134 | 60 | — |
| CI and repo config | 69 | 47 | — |
Documentation (3 .md) |
— | — | 210 |
| Total | 570 | 326 | 210 |
Code is 570 lines; the rest is comments (326), documentation (210) and blanks (129). Splitting would not help: the environment, the scripts that drive it, and the job that runs them are only verifiable together — a test/caproni that nothing runs, or a suite with no environment, is unreviewable and untestable in isolation. The intermediate commits are individually reviewable and tell the story in order.
Scenario catalogs
No catalog change. Per guardrail 12 this is ci/docs rather than feat/fix, and the suite is a smoke test rather than a named-scenario harness, so it flips no Status in docs/testing/. It overlaps e2e.oci.setup.create-repository, which additionally requires the new repository to work as a push target.
Note for the reviewer
No plan MR precedes this. Every commit is ci: or docs:, which conventions.md classifies as toil, but the change is large enough that the toil/feature boundary is a fair question to raise — flagging it rather than leaving it implicit.
Related to #318