chore(deps): move catalog pin to v4.1.2, split container signing
Moves the catalog pin from v3.3.0 to v4.1.2 and makes the two changes a version-string bump alone would have gotten wrong.
CATALOG-VERSION is the single source of truth; just sync propagated it to every managed pin. just validate is green.
Two items that want reviewer attention
1. container-sign added to both consumer-facing templates
Catalog v4.0.0 is breaking: container builds and pushes but no longer signs, signing moved to a separate container-sign component, and :latest is gated to the default branch and tags.
templates/ and standards/*/templates/ are lift-and-replace scaffolds that consumers copy into their own repositories. Re-pinning them to v4 without adding container-sign would mean every consumer who scaffolds from them builds an image and never signs it — and standards/provenance/ is the standard whose entire purpose is signed, attested releases. That silent loss of guarantee is the failure mode this MR exists to avoid.
Both templates that wire container gain a container-sign include and a dedicated sign stage between build and the attest stages:
standards/provenance/templates/release-pipeline-include.yml— stages are nowtest → build → sign → attest-sbom → attest-slsa → verify → releasestandards/pipeline/templates/.gitlab-ci.yml— stages are nowlint → check → build → sign → deploy
The provenance template's verify include also drops the needs_job input. Catalog v4.0.2 made verify stage-gated (it declares no needs:) precisely so it stops racing the separate sign job to "no signatures found"; the input is still declared upstream but no longer read, and passing it implied a DAG edge that no longer exists.
Verified from the merged YAML (project /ci/lint API, include_merged_yaml):
| job | stage | needs |
|---|---|---|
container-build |
build |
— |
container-sign |
sign |
container-build (artifacts: true) |
container-sbom |
attest-sbom |
container-build (artifacts: true) |
container-attest |
attest-slsa |
container-build (artifacts: true), container-sbom |
container-verify |
verify |
— (stage-gated, inherits IMAGE_DIGEST_* from dotenv) |
2. The pages branch: main workaround is dropped
.gitlab-ci.yml pinned branch: main explicitly, with a comment noting $CI_DEFAULT_BRANCH did not expand inside the component's rules:if so the pages job was never created, plus an "Upstream bug to file against pipeline catalog v3.0.0" note.
Decision: drop it. Evidence:
- Catalog v4.1.1 changelog states outright that consumers who worked around the bug by pinning a literal
branch: "main"can drop the workaround. - Catalog v4.1.2 (!82) fixed the remaining half — nested variables inside a variable value do not expand during
rules:ifevaluation — and records "Both paths verified on the consumer before release." - Reading the v4.1.2 template source directly:
branchnow defaults to the empty string, and the rule splits in two, comparing variable to variable with no nesting and no quoted literal. - The rendered rules for this repo confirm it:
PAGES_DEPLOY_BRANCH: ''with- if: $PAGES_DEPLOY_BRANCH == "" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCHand- if: $CI_COMMIT_BRANCH == $PAGES_DEPLOY_BRANCH.
Dropping it also removes a hardcoded branch name that would drift if the default branch were ever renamed. The stale upstream-bug note is removed with it.
Residual risk, stated plainly: rules:if is only evaluated in a real pipeline, and the pages job by design does not appear on merge-request pipelines. So this MR's own pipeline cannot prove the deploy fires — the first proof is the post-merge main pipeline. Please confirm the pages job appears there.
The HUMAN-CONFIRM: marker in the same block (verify just resolves in the pages image's apk repos) is left in place, unchanged.
Third item, not requested but load-bearing
standards/pipeline/templates/.gitlab-ci.yml passed a registry: input to container. That input has never existed — checked at v3.0.0, v3.1.0, v3.2.0, v3.3.0, v3.3.2, v4.0.0 and v4.1.2. The starter therefore could not resolve for any consumer who copied it as-is:
$ glab ci lint <old shape>
invalid. `container@v4.1.2`: unknown input arguments: registryIts image_name was also $CI_PROJECT_PATH, which buildah build -t / buildah push resolve against Docker Hub rather than the GitLab registry. Now image_name: $CI_REGISTRY_IMAGE, no registry input. This predates the catalog move; fixed here because a template has to be valid at the version it pins.
Target version: why v4.1.2
v4.1.2 is the newest catalog release (2026-07-22). Renovate's !38 (closed) offered only v4.0.2 because the shared preset's minimumReleaseAge: 7 days cooldown hid v4.1.0–v4.1.2. That cooldown protects against a compromised third-party release; this is our own catalog, and v4.1.1/v4.1.2 are the exact pages fixes this repo needs. !38 (closed) was also red because it edited the pin in .gitlab-ci.yml without touching CATALOG-VERSION.
Every pin that changed
Six managed pins, all v3.3.0 → v4.1.2:
| file | component |
|---|---|
.gitlab-ci.yml |
pages |
standards/pipeline/templates/.gitlab-ci.yml |
container |
standards/pipeline/templates/.gitlab-ci.yml |
pages (commented example) |
standards/provenance/templates/release-pipeline-include.yml |
container |
standards/provenance/templates/release-pipeline-include.yml |
container-sbom |
standards/provenance/templates/release-pipeline-include.yml |
container-attest |
standards/provenance/templates/release-pipeline-include.yml |
verify |
Plus two new pins added at v4.1.2: container-sign in each of the two templates. These are managed (not exempt), so future just sync runs keep them in step — confirmed idempotent.
Exemptions left alone, as sync-versions.sh and validate-versions.sh both intend:
vale@v4.0.1(.gitlab-ci.yml) — unchangedreference-check@v3.1.0(four commented includes) — unchangedpaper@~latest— not a semver pin, out of scope for sync
Validation
| command | result |
|---|---|
just sync |
rewrote 6 pins; re-run produced no further change (idempotent) |
just validate |
PASS — registry, matrix, versions (reference v1.2.0 + catalog v4.1.2 consistent), templates |
just guard |
checked 18 file(s), 0 violation(s) |
glab ci lint .gitlab-ci.yml |
valid |
glab ci lint standards/pipeline/templates/.gitlab-ci.yml |
valid |
glab ci lint standards/provenance/templates/release-pipeline-include.yml |
valid (also valid with $CI_SERVER_FQDN expanded) |
project /ci/lint merged YAML |
job graph and pages rules as tabled above |
Flagged for a human decision, not changed here
- The
valeexemption rationale is now inverted. It is exempt because it pinned ahead ofCATALOG-VERSION(v4.0.1 vs v3.3.0). With the catalog at v4.1.2,vale@v4.0.1is now behind, and the comments in both scripts still explain it as a forward pin. Functionally fine (v4.1.x changed nothing invale), but the stated reason no longer matches reality. Left untouched deliberately. container-attest's manualneeds:override is redundant and its comment is false. The comment claims "the catalog's defaultneeds:lists only the build job" — butcontainer-attesthas declared ansbom_jobinput with anoptional: trueneed oncontainer-sbomsince v3.3.0, and the two attest jobs are already in separate stages. The override also hard-codes a non-optional- job: container-sbom, so a consumer who lifts this template and dropscontainer-sbomgets an invalid pipeline where the component'soptional: truewould have survived. Safe to delete in a follow-up; left in place to keep this diff to the version move.- Garbled sentence in the provenance template header, lines 21-24: "consult the catalog for current consumers as a worked / the canonical multi-variant version" is missing words. Pre-existing, not vale-linted (vale covers
*.mdonly). - Upstream, low priority:
container-sbomhard-codes anoptional: trueneed on<container_job>-attestwhilecontainer-attestneedssbom_job— if a consumer ever names their attest jobcontainer-build-attest, those two form a cycle. No consumer does today, and this repo's rendered graph is acyclic. - Deprecation warnings from
/ci/lint:retry: when: stuck_or_timeout_failureis deprecated, in this repo'sdefault:block and in the catalog components. Pre-existing, unrelated to this move.
Do not merge yet
Superseded Renovate MRs !38 (closed) (v4.0.2) and !34 (closed) (v3.3.2) are closed in favour of this one. Holding for review as requested.