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 now test → build → sign → attest-sbom → attest-slsa → verify → release
  • standards/pipeline/templates/.gitlab-ci.yml — stages are now lint → 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:if evaluation — and records "Both paths verified on the consumer before release."
  • Reading the v4.1.2 template source directly: branch now 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_BRANCH and - 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: registry

Its 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.0v4.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.0v4.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) — unchanged
  • reference-check@v3.1.0 (four commented includes) — unchanged
  • paper@~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

  1. The vale exemption rationale is now inverted. It is exempt because it pinned ahead of CATALOG-VERSION (v4.0.1 vs v3.3.0). With the catalog at v4.1.2, vale@v4.0.1 is now behind, and the comments in both scripts still explain it as a forward pin. Functionally fine (v4.1.x changed nothing in vale), but the stated reason no longer matches reality. Left untouched deliberately.
  2. container-attest's manual needs: override is redundant and its comment is false. The comment claims "the catalog's default needs: lists only the build job" — but container-attest has declared an sbom_job input with an optional: true need on container-sbom since 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 drops container-sbom gets an invalid pipeline where the component's optional: true would have survived. Safe to delete in a follow-up; left in place to keep this diff to the version move.
  3. 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 *.md only).
  4. Upstream, low priority: container-sbom hard-codes an optional: true need on <container_job>-attest while container-attest needs sbom_job — if a consumer ever names their attest job container-build-attest, those two form a cycle. No consumer does today, and this repo's rendered graph is acyclic.
  5. Deprecation warnings from /ci/lint: retry: when: stuck_or_timeout_failure is deprecated, in this repo's default: 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.

🤖 Generated with Claude Code

Merge request reports

Loading