fix(vale): Bake vale and pinned styles into ci-tools
-
valehad stopped linting anything, and the job's own red square hid it. On 2026-08-04 the job failed three pipelines in a row onreferencewith HTTP 429, exhausting its retries while asking gitlab.com to archive thegitlab-org/gitlabmonorepo atmasterfordoc/.vale. It examined zero files each time. Because the component is normally included withallow_failure: trueso prose findings advise rather than block, "the styles download was rate-limited so nothing ran" was indistinguishable from "the prose is clean" and from "there are 313 findings" -- one ignored red job standing for three different states. That is why finding counts on this estate kept moving between sessions.valeand a pinned snapshot of the GitLab docs styles now ship insideci-tools, at/opt/vale-styleswithVALE_STYLES_PATHset. The default job path makes no network call at all: the binary is no longer pulled from a GitHub release per run, and the styles are no longer archived out of a 3 GB monorepo per run per consumer. A styles failure is now a loudci-toolsbuild failure that blocks, instead of a job nobody reads. This also makes the component offline-capable, which the airgapped consumers this catalog exists for actually need. -
The vale ruleset drifted under consumers with no diff in their repository.
styles_archivedefaulted tomaster, andmasteris not a version. Diffing the pinned tag againstmasteron the same day showed a new rule (BoldEmphasis), two new style packages, and four changed rule files. A consumer's green pipeline could go red because someone else merged documentation upstream.VALE_STYLES_REFpins it (v18.11.8-ee) and carries a Renovate annotation, so the ruleset now moves when a maintainer chooses and arrives as a reviewable MR.vale_versionandstyles_archivekeep working as runtime overrides, and both now default to empty (use the image). A failed override fetch, or an image with no styles, exits 2 -- check could not run -- and never falls through to a vale invocation that would report zero findings against zero rules and read as a pass. Verified against all three branches: baked styles present, baked styles missing, and an override URL that 404s.Not fixed here, and it needs a major.
allow_failure: truestill swallows exit 2 along with exit 1, so a consumer who wants prose findings advisory cannot yet make could-not-run blocking. The mechanism isallow_failure: { exit_codes: [1] }, which means changing theallow_failureinput's shape: a breaking contract change for the six current consumers, and one whose empty-array semantics should be proven on a lab before shipping. The measured defect above is fixed regardless, because the runtime failure mode is now gone rather than relabelled. -
The shared Renovate preset stripped the leading
vfrom every version it wrote.extractVersionTemplate: "^v?(?<version>.+)$"on the Containerfile ARG manager rewrotev2.6.3as2.6.4, but the ARG is used as the release TAG in the download URL (the filename gets a separately-stripped copy). Six merge requests acrossci-tools,ci-buildahandci-buildkitproposed URLs that 404: verified against the live API,gitlab-org/clirelease1.109.0returns 404 andv1.109.0returns 200. Replaced withversioningTemplate: "semver-coerced". The same line sat on the catalog component-pin manager, where it is worse: that manager'smatchStringsrequires thev, so the stripped value failsconfirmIfDepUpdated, the file reverts, and Renovate throwsupdate-failure. That is the errored branch updates, and why no consumer was on the current catalog version. Same root cause, two failure shapes: one writes silently wrong, the other cannot write at all. -
validate:containersbuilt nothing and passed. Itswhile readloop had no input redirect, so it read stdin, which is empty in a CI job. It printed a correct build plan and thenbuilt 0 image(s); Job succeeded. No pipeline had ever run it, because its rules require a merge request that changes a Containerfile. It now redirects from the plan and asserts built == planned. -
osv-verdictwas fail-open in merge-request diff mode. Consumers may see MRs newly go red, correctly. The head scan wrote its findings inside the working tree andgit stash --include-untrackeddeleted them; the stash is never popped.sorton the missing file exited 2 to stderr,commread an empty stream and exited 0, and the verdict waspassno matter what the head introduced. There is noset -ein that script, so nothing surfaced. Scan output now lives outside the working tree, the declaredosv-head.jsonartifact is restored through anEXITtrap covering all five exit paths, and a guard fail-closes when either side of the diff is absent. -
osv-verdicttreated an unparseable scan as a clean one. The jq parse ended in|| : > "$ids_file", so any schema change, truncated write or missing file became an empty findings list and a pass. Sinceosv_scanner_versionis Renovate-managed, a major bump reshaping the JSON would have silently disarmed the check estate-wide. The parse now fails closed, and a scanner exit of 1 (findings present) that parses to zero findings is treated as schema drift rather than as clean. -
auditsetLANG, the POSIX locale variable, to the language name. On the vanilla bases every current consumer overrides to,LANGis already exported, so the assignment propagatedLANG=goto every child process (locale: Cannot set LC_CTYPE to default locale, collation flipping to C ordering). Renamed toAUDIT_LANG.
Changed
-
audit'simageinput is now optional, derived fromlanguage. It had no default, which made it required: the component could not be included without naming an image, and its own error text coached a vanilla base ("add 'image: rust:1.88.0'"). Across all 57 group projects every one of audit's six consumers passesimage:and five pass a vanilla base, four of them naming a languageci-toolsalready bakes. Conformity was zero by construction. Existing overrides keep working; migration is deleting the line.rustresolves toci-rust,gotoci-go,nodeandpythontoci-tools. Removing the input entirely remains the eventual major. -
auditno longer installs audit tooling at job runtime. The rust arm rancargo install --locked cargo-deny --version 0.19.2 2>/dev/null || true, shadowing the image's Renovate-tracked pin with an unpinned second source of truth and swallowing its own failure. The go arm rango install golang.org/x/vuln/cmd/govulncheck@v1.1.4, which was OLDER than the baked version, so the audit ran an older scanner than the image shipped. The python arm ran an unpinnedpip install pip-audit, which on this image's pip backtracks to an older release rather than failing. All three now check the baked tool is present and fail pointing at a stale image. -
ci-toolsbakespip-auditat a Renovate-trackedPIP_AUDIT_VERSION. -
.image-buildruns on the catalog's ownci-buildahand no longer curl-installs cosign at runtime, which happened on every image build, against this repo's own no-runtime-curl-installs rule, without checksum verification, in the job that signs every catalog image. -
Digest pinning is off.
pinDigests: falsefor the docker datasource, and invariant 1 inADOPTION.mdrewritten to match: the semver tag is the pin. Digests rot faster than a review-gated MR can land. -
Containerfile changes are built on the merge request. No
build:ci-*job ran onmerge_request_event, so a Containerfile change was never exercised until after merge, which is why six MRs proposing 404 URLs were all green.validate:containersbuilds the changed images, and their dependents, without pushing or signing. -
BREAKING:
automerge-gateno longer fails the pipeline for a customer-shipping class whose checks are green. It reportsGATE_DECISION=human-reviewand exits zero. Consumers on the default (customer_shipping: true) will see this job go from permanently red to green. That default previously exited nonzero unconditionally, which put "not automerge-eligible" on the same pipeline red/green channelonly_allow_merge_if_pipeline_succeedsreads to gate human merge. The documented adoption shape therefore blocked the human lane rather than routing to it, and the catalog's own sim had to wrap that shape inallow_failure: trueto stay green. A failing or absent check still exits nonzero on the shipping path. The automerge block itself was never carried by this exit code; it lives inrenovate-config.json(platformAutomerge: falseplusautomerge: falseon every rule). -
automerge-gateemits aGATE_DECISIONdotenv (automerge-eligible,human-review,blocked,tampered) withwhen: always, so the decision is auditable and assertable rather than only a log line. -
automerge-gaterefuses to decide against a tampered floor.CUSTOMER_SHIPPINGis a job-level variable and GitLab ranks project and group CI/CD variables above job-level ones, so a Maintainer on a consuming project could set it in Settings and silently send the job down the non-shipping path with no diff in any file. The gate now compares the runtime value against the literal it was compiled with and emitsGATE_DECISION=tamperedwith a nonzero exit if they differ. Documentation claiming the floor was "enforced at two layers" has been corrected inrenovate-config.json,README.md,templates/automerge-gate/README.mdandADOPTION.md; it was one layer plus a report.
[4.2.0] - 2026-07-27
Added
-
site-checkcomponent. Runs a project's own check command -- type checking, framework diagnostics -- on merge requests. It exists because a build is not a check:astro buildand its equivalents transform sources without type-checking them, so a TypeScript or framework major goes green in CI while breaking types. Four projects in this group each defined"check": "astro check"inpackage.jsonand no pipeline ran it; a TypeScript v7 merge request passed CI in one of them purely because the build never invoked the checker, and running that checker by hand against the current pinned TypeScript surfaced two pre-existing type errors nobody had seen.allow_failuredefaults tofalse, because a check that cannot fail the pipeline is not a gate. The component stays deliberately thin -- install, then run the project's own command -- so the check keeps living inpackage.jsonwhere maintainers can run it locally. -
ADOPTION.md— the adoption profile every consumer of this catalog conforms to: three invariants (everything Renovatable, the gate runs on the path that merges, CI references nothing a refactor can break), the standard consumer shape, adoption tiers, and enforcement. It exists so that "this project is deliberately exempt" and "nobody got round to this project yet" stop being indistinguishable — which is how dependency traffic went unmanaged here for months without anything looking wrong. -
Inherited enrollment for the Renovate runner. The runner now injects this catalog's own preset as base config for every autodiscovered project (
RENOVATE_CONFIG+RENOVATE_REQUIRE_CONFIG: optional). Under the previousrequiredmodel, 37 of 57 projects in the group carried norenovate.jsonand were therefore silently unmanaged. A project now opts out explicitly with{"enabled": false}, and a project needing overrides still ships its ownrenovate.json, which merges on top of the injected base. Enrollment is opt-out instead of opt-in, which is the only version that stays complete as the group grows. -
RENOVATE_FORCEcadence normalizer in the runner preset —{"schedule":null,"automergeSchedule":null,"prHourlyLimit":0}. The CI cron becomes the single source of cadence, so a configscheduleinherited from anywhere can no longer silently park updates (see Fixed, below).prConcurrentLimitis deliberately left alone: it stays the useful throttle, a per-repo ceiling on open MRs, whereprHourlyLimitinteracts badly with an infrequent cron — at 4/hour on a weekly run a project drains four updates per week. -
validate:renovate-configjob.renovate.json,renovate-config.jsonandpresets/renovate.jsonare now schema-validated on every change, pinned to the Renovate major the runner executes. Every project in the group resolves the shared preset at scan time, so a schema error here is a group-wide outage rather than a local one. The pin matters: an unpinnednpx renovate-config-validatorresolves an older major that reports false errors on current keys such asmanagerFilePatterns. -
needs: []on therenovatejob. The job sits in the last stage, so an earlier-stage failure skipped it — and the likely failure on this catalog is a container build on a rotated base-image digest, which is precisely what Renovate would have bumped. That is a deadlock. Scheduled runs already exclude the build jobs, but aweb-triggered run executes the full pipeline, so this is load-bearing there.
Changed
-
Evidence is frozen, by path: reproduction material stops being renovated unattended. A file under
bench/,archive/,evidence/,experiments/,rig/,receipts/orresults/is not a dependency choice tracking the moving world — it is the experimental condition some captured result describes. Bumping it does not maintain the experiment, it voids the receipt. Measured: three of the six benchmark subjects inlab/rootlesswere bumped before this rule existed, and because a receipt names its subject only as"dockerfile": "go-scratch"and records no versions, nothing in the receipt could reveal the drift.Keyed on path, not on repository. Only 7 of 18 lab projects hold receipts at all — the rest are tools, docs and landing pages that must stay current — so a repository-wide freeze both over-freezes those and would rot the receipt-bearing labs' own site builds and CI. Per-path also generalises: an evidence directory appearing in a non-lab project is covered with no config change, and no list of projects has to be maintained as labs are added.
Two supporting choices:
dependencyDashboardApprovalrather thanenabled: false, so the dashboard still lists every available update and a thaw is one checkbox (re-run, re-capture, tick) instead of a config change; and avulnerabilityAlertsoverride, so a genuine CVE still opens an MR against frozen evidence. Globs verified against minimatch at both root and nested depth, including thatbenchmarks/andsite/src/rigging.tsdo not match.Separately, the 13 unmaintained
lab/project-*hackathon repositories now carry{"enabled": false}of their own: they are kept as reference for new work rather than maintained software, so updates there generate review nobody will read. Left writable rather than archived so code can still be lifted out. -
TypeScript ceilinged below v7 in the shared preset, by upstream limitation rather than preference. v7 is the native-compiler port and its package no longer exports the in-process compiler API, so every tool that drives TypeScript programmatically breaks.
@astrojs/check(latest 0.9.9) peers^5.0.0 || ^6.0.0, install failsERESOLVE, and forcing it through crashes@astrojs/language-serverat runtime on the removed API. Upstream tracks this at withastro/astro#17268, labelledtriage: unable to fix; Microsoft's own 7.0 announcement tells Astro/Vue/Svelte/MDX projects to stay on 6.x. The ceiling still modernizes -- 6.0.3 is stable and compatible, so Renovate now proposes the v6 major instead of a v7 that cannot merge, and four projects stop regenerating a red MR every day. Removable in one line when@astrojs/checkships v7 support. Two related traps worth knowing:typescript-eslintcarries a hard peer ceiling (<6.1.0) and fails loudly at install, whilevue-tscandsvelte-checkdeclare loose>=5.0.0peers, so they install cleanly against v7 and fail at RUNTIME instead of in CI. -
Renovate scan timeout raised to 3h, sized for a cold run. The first scan after enrolling a whole group must open every branch it finds and regenerate a lockfile per JS project. Measured on this estate: 41 of 56 projects in 72 minutes, so the previous
1h 30mcould not complete a single cold pass. Steady-state daily runs are a fraction of that, since most projects have nothing to do. A timeout is deliberately still not retried --job_execution_timeoutis a distinct failure reason from the infrastructure classes, and retrying a scan that legitimately needs more wall-clock than it was given cannot succeed, it only spends the budget twice. -
Renovate autodiscovery skips purge-pending projects. The filter now excludes
*-deletion_scheduled-*. Those projects linger for the retention window, autodiscovery still finds them, and the bot's Dependency Dashboard write fails — a guaranteed warning every scan for a project that is already gone. -
renovatejob robustness. Explicittimeout: 1h 30mso a growing autodiscover scan is not killed mid-run into a partial green, andretrybroadened to the three infrastructure classes (runner_system_failure,stuck_or_timeout_failure,scheduler_failure) rather than retrying a real Renovate error into a different failure. -
Runner preset documents the cadence and enrollment doctrines in its header, including the schedule-window failure mode in full, so an operator standing one up from this asset does not rediscover it. The shipped default stays
RENOVATE_REQUIRE_CONFIG: required— correct for a fresh operator with no shared preset — with the inversion documented for operators who have one. The recommended cron is now daily rather than weekly.
Fixed
-
ci-tools:glabis looked up on the right forge. TheGLAB_VERSIONpin carrieddatasource=github-releasesforgitlab-org/cli, a GitLab-hosted project. The lookup returnedno-resultsilently every scan, so the pin sat at v1.92.1 while v1.108.0 shipped — sixteen minor versions of drift, reported only as a warning on a dashboard. Nowgitlab-releases. -
Renovate preset: scheduled runs actually open merge requests. The preset scheduled updates for
before 6am on Monday(Mon 00:00-05:59 UTC) while the runner's cron fires at 06:00 and the job starts 06:07-06:09 — every run landed outside its own window. Renovate'supdateNotScheduleddefault still refreshed existing branches, so the runner looked healthy (56 repos autodiscovered, job green, dashboards current) while never creating a new branch. Every update parked in Awaiting Schedule indefinitely: 12 items on this project, 11 ontach, 24 onreference. Measured effect estate-wide — Renovate MRs created fell from 22 in May to 12 in June to 2 in July. The window is nowbefore 9am on Monday, giving the 06:00 cron roughly three hours of slack for queue delay and the job's own ~12 minute runtime. -
UBI grouping covers UBI10. The group rule matched
registry.access.redhat.com/ubi9{/,}**only, so consumers onubi10received ungrouped erratum bumps. Now matchesubi{9,10}{/,}**. -
Catalog component pins no longer emit a spurious lookup-failure warning. Consumers write
component: $CI_SERVER_FQDN/gitlab-com/public-sector/pipeline/<name>@vX.Y.Z. Renovate's nativegitlabcimanager treats the unresolved CI variable as the registry host, so itsgitlab-tagslookup always returnedno-resultand eight consumer dashboards carried aFailed to look up gitlab-tags package gitlab-com/public-sector/pipelinewarning. The pins themselves were never actually unmanaged — thecustomManagersregex entry tracks them correctly with a hardcodedpackageNameand thegitlab-releasesdatasource — so the native extraction was pure duplicate noise. It is now disabled for this package. Closes #2 (closed).
[4.1.2] - 2026-07-22
Fixed
pagestemplate: default-branch deploys actually fire. The 4.1.1 fix repaired explicit literalbranchinputs but not the default: nested variables inside a variable value do not expand duringrules:ifevaluation, so the job variable holding$CI_DEFAULT_BRANCHstill compared as literal text and the pages job stayed absent under default inputs -- verified against a live consumer pipeline. Thebranchinput default is now the empty string and the rule splits in two: empty input deploys when$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH(direct variable-to-variable, no nesting), an explicit input deploys from exactly that branch. Both paths verified on the consumer before release. (!82 (merged))
[4.1.1] - 2026-07-22
Fixed
pagestemplate: the deploy rule now fires with the defaultbranchinput. The rule interpolated the input inside quotes (== "$[[ inputs.branch ]]"), and GitLabrules:iftreats a quoted right side as a literal string -- with the default ($CI_DEFAULT_BRANCH) the rendered rule compared the branch name against that literal text, never matched, and the pages job silently dropped from every consumer pipeline using defaults. The input now lands in a job variable (PAGES_DEPLOY_BRANCH) and the rule compares variable to variable, which works for both the variable default and an explicit literal branch. Consumers that worked around the bug by pinning a literal (branch: "main") can drop the workaround. Swept the other component templates for the same quoted-input-in-rules shape:pageswas the only one. (!81 (merged))
[4.1.0] - 2026-07-19
Added
automerge-gatetemplate -- the terminal, fail-closed decision job for Renovate automerge on Public Sector projects, with a hard customer-shipping human-signoff floor. It consumes per-check dotenv verdicts (e.g.osv-verdict) overneeds:withoptional: true, and exits nonzero unless every check named inrequired-checksis both present and== pass-- an absent or non-pass signal, or zero checks evaluated (empty/typo'drequired-checks), blocks automerge, so a weakly-instrumented project cannot clear its own gate.osv-verdicttemplate -- emits an OSV-scan verdict forautomerge-gate(a signal of the L0 CI gate). Installsosv-scanneras a checksum-verified binary (the same idiomci-toolsuses for cosign/syft/glab/yq); on a merge-request pipeline it scans the merge-base and head and fails only on findings the head newly introduces (pre-existing findings do not block an unrelated bump), fail-closing if the merge-base is unreachable. WritesCHECK_OSV=pass|failfor the gate.ci-toolsnow shipsdiffutils(diff+cmp). ubi9-minimal ships neither. A consumer's shell no-op/idempotency check built ondiff/cmpfailed command-not-found and, under a2>/dev/nullredirect, degraded silently to the "changed" path -- a real bug this hid downstream. The Containerfile carries a keep-this note.
Changed
- Dependency bumps: UBI9 base image, golang 1.26.4, gitlab-org/cli (glab) 1.102.0, and convergence of the shared renovate preset onto the root config.
Fixed
releasereflows the annotated tag body before publishing. Tag bodies are hard-wrapped at ~72 columns (git convention), but GitLab renders single newlines as line breaks, so the release page showed mid-sentence breaks. The notes generation now joins wrapped prose into single-line paragraphs while preserving blank-line paragraph breaks and list items (-,*,1.). The PGP signature is still stripped and stays verifiable viagit verify-tag. Consumers pin@v4.1.0to pick this up; existing release pages can be re-rendered by re-running the tag'screate-releasejob.
[4.0.2] - 2026-06-05
Fixed
verifyruns after the separatecontainer-signjob. v4.0.0 split signing into its own job, butverifyonlyneeds:the build job, so on a DAG it raced ahead of signing -> "no signatures found".verifyis now stage-gated (noneeds:) -- it runs after the build+sign stage(s) and inherits the digests from the container job's dotenv. Keep the verify stage after build/sign.verifyguards:latest.:latestis pushed only on the default branch / tags (the v4 :latest gate), soverifychecksIMAGE_DIGEST_LATESTonly when present -- no spurious failure on MR pipelines. Consumers on v4.0.x should pin@v4.0.2.
[4.0.1] - 2026-06-05
Fixed
- Catalog images now resolve for consumers that don't override them.
v4.0.0 gave
container,container-sign,vale, andregistry-cleanupanimageinput defaulting toci-*:$[[ component.version ]]. GitLab does not interpolate$[[ component.version ]]inside a resolved input default, so any consumer using the default pulled the literalci-*:$[[ component.version ]]and failed with "invalid reference format". Reverted to a body-hardcodedimage: .../ci-*:$[[ component.version ]](which interpolates) and dropped theimageinput. The in-repo container sim (which used the input to override at a SHA include) is removed;sim-go-binaryremains. Consumers on v4.0.0 should pin@v4.0.1.
[4.0.0] - 2026-06-04
This is a breaking release (catalog v4.0.0): container build and
container signing are now separate components, and :latest is gated to
the default branch.
Added
container-signcomponent. Cosign-signs a container image's pushed digests (keyless via GitLab OIDC), readingcontainer.envfrom thecontainercomponent (IMAGE_DIGEST_SHA/_LATEST/_TAG). This is the signing half of the build/sign split; it mirrors the post-build shape ofcontainer-sbomandcontainer-attest.imageinput oncontainerandcontainer-sign. Defaults to the catalog image at your catalog tag (ci-buildah/ci-toolsat$[[ component.version ]]); override for a private fork or a:latestimage when including at a SHA (e.g. the in-repo sim harness, wherecomponent.versionis empty).registry-cleanupcomponent. The container registry cleanup policy (container_expiration_policy) is disabled estate-wide, so SHA build tags and cosign.sigsignature tags accumulate without bound. This component applies a verified baseline (enabled: true,cadence: 7d,keep_n: 10,older_than: 30d,name_regex_delete: .*) via an idempotentPUT projects/:idon the default branch; it needs a token withapiscope (CI_JOB_TOKENcannot edit project settings). Thename_regex_keepcarve-out protects cosign signatures --^(latest.*|v\d+\.\d+\.\d+(-.*)?|sha256-[a-f0-9]{64}\.sig)$-- because GitLab cleanup treatssha256-<digest>.sigtags as ordinary tags; without the.sigclause it deletes signatures of images you keep and breakscosign verify. The clause also protectslatestand suffix variants (latest-fips) and all semver tags. The component is the single source of truth; its README documents a manual one-shot fallback for projects not yet wired to CI.
Changed
-
BREAKING:
containerno longer pushes or signs:lateston merge request pipelines.:latestnow tracks the default branch only (default branch + tag pipelines);:SHAis still built on every pipeline (downstream smoke/scan need it) and:TAGon tag pipelines. Previously every MR pipeline moved:latestto unreviewed MR code and signed it. Fixes the transient-clobber + spurious-signature gap. -
BREAKING:
containerno longer signs. Thecontainercomponent now only builds, pushes, and writescontainer.env. Cosign signing moved to the newcontainer-signcomponent (theid_tokens/COSIGN_YESwiring moved with it). Consumers that relied oncontainerto sign must add acontainer-signjob per build job to preserve signatures. Rationale: signing is a distinct concern with distinct identity/perms; decoupling lets a consumer build without signing or re-sign without rebuilding. -
container-sbomno longer declaresartifacts:reports:cyclonedx. GitLab-side SBOM ingestion (Dependency List, Security, Licensing) is owned by Ultimate Container Scanning, which emits a GitLab-conformantgl-sbom-report.cdx.jsonwith the requiredgitlab:*CycloneDX properties. Submitting this component's syft SBOM in parallel produced the "Required GitLab CycloneDX properties are missing" parse error on the Security and Licensing tabs. The component keeps its real purpose -- the cosign attestation bound to the image digest (external / Iron Bank channel, verifiable viacosign verify-attestation). Consumers that relied on this component to populate the Dependency List should ensureJobs/Container-Scanning.gitlab-ci.ymlis included (Ultimate). -
valegained animageinput. Defaults to the catalogci-toolsat your catalog tag, overridable for a fork or a:latestat SHA includes. Aligns withcontainer/container-sign/registry-cleanupand fixes the empty-tag (ci-tools:) "invalid reference format" failure when included at a SHA, wherecomponent.versionis empty.
Removed
-
Phantom
*-versionrows in component READMEs. Thecosign-version/syft-versionrows incontainer-attest,container-sbom, andcontainer-manifestREADMEs documented inputs that are not declared in those components'spec.inputs(the tool versions are pinned in-component, not consumer-overridable). Removed the misleading rows. -
templates/referencerelocated toexamples/reference. The reference Go/Rust verify + lockfile implementations are examples, not a CI component (notemplate.yml); moving them out oftemplates/keeps that tree component-only. Links in the top-level README andtemplates/attest/README.mdupdated. -
build-container-buildahcomponent. A near-duplicate ofcontainer(buildah build + push + the now-extracted sign) with no external consumers.container+container-signsupersede it; theimageinput added to both gives the sim harness the override it previously relied onbuild-container-buildahfor.sim-buildahnow exercisescontainer+container-signend-to-end.
Fixed
-
releasecomponent preserves backtick-delimited content in release descriptions. The previous implementation passedCI_COMMIT_TAG_MESSAGEthrough therelease:keyword'sdescription:field, where release-cli / glab shell-eval'd the value. Backtick-delimited code spans, fenced code blocks, version regexes, refs, and any other backticked prose were interpreted as command substitution and silently dropped from the rendered release page (/bin/sh: eval: line 198: <token>: not foundshows up in the job log; everything inside backticks vanishes from the page). The component now writes the annotated tag body to a file viagit tag -l --format='%(contents)'(with PGP signature stripping for signed tags) and publishes it throughglab release create --notes-file. The body reaches the release page byte-for-byte, with no shell parsing. -
releasecomponent re-runs are idempotent.glab release createupdates an existing release by default and the component does not pass--no-update. Re-runs on the same tag (manual replay, Renovate retry, transient CI flake) now refresh the release page with the current tag annotation body instead of failing withrelease for tag "<tag>" already exists and --no-update flag was specified. The upstreamrelease:keyword wrapper, which always invoked glab with--no-update, no longer mediates the call.
[3.4.0]
ci-tools grows to cover reference-style projects too -- yq + node
- npm + python3 + make are now baked in alongside cosign / syft /
glab / jq. Downstream projects that were patching alpine packages
in inline
before_scriptblocks can drop the patches and pull this image.
Note: the
reference-checkcomponent originally drafted for this release ships from thegitlab-com/public-sector/referencecatalog instead, not from thispipelinecatalog. Theci-toolsexpansion below stays here becausereference-check(and other reference-style projects) pull this image at runtime.
Added
-
ci-toolsimage expanded. Addsmake,nodejs+npm(Fedora's node 22 / UBI9's available),python3+python3-pip,findutils, andyq(mikefarah v4.52.5, checksum-verified upstream binary). The reference's own CI was patching these in via alpineapk addchains; consumers pullingci-tools:v3.4.0get the full set with one image reference and zero patching. -
Renovate
customManagersrule for catalog component pins in consumer.gitlab-ci.ymlfiles. Picks uppipeline/<component>@vX.Y.Zand proposes bumps against the catalog's GitLab releases.
Changed
valecomponent gains adisable_rulesinput. Space-separated rule names get appended to the generated.vale.inias<rule> = NOlines. Intended for voice-specific opt-outs -- projects that legitimately choose against a particular gitlab-docs convention (e.g. em-dash in long prose, "via" in technical writing) without needing to fork the styles archive. Defaults to empty (no rules disabled).
[3.3.0]
Two new catalog primitives — the paper component and the ci-latex
image — plus a clutch of consumer-friendly runner_tag and OCI
metadata improvements, Renovate hardening, and the dependency
freshness pass.
Added
-
New
ci-latexcatalog image (Fedora 44 base): TeX Live full + latexmk + tectonic + typst + poppler-utils + Node.js + npm + Chromium runtime closure (Playwright-ready). Built and cosign- signed by the catalog's own pipeline like every other ci-* image. Pinned by FROM + dnf install, no runtime curl-fetches. Replaces the upstreamtexlive/texlivedefault in thepapercomponent. Node + Chromium added in the same release so consumers that pair PDF rendering with HTML preview capture (the reference'scapture-template-previewsjob) don't have to layer them in. -
New
papercomponent: build an academic or technical paper PDF from LaTeX or Typst source. Engine-agnostic (latexmk,tectonic, ortypstselectable). Default image is the new catalog-managedci-latex, which carries all three engines. Optionalstyle_url/style_nameinputs fetch a shared style package (e.g.,gitlab.styfrom the public-sector reference project) before build. Emits the PDF as a 30-day artifact at a configurable path; suitable for download, mirror to Pages, or attachment to a release. -
compliance,container-attest,container-sbom, andvalecomponents now accept arunner_taginput (defaultsaas-linux-large-amd64). Previously these jobs ran on GitLab's defaultsaas-linux-small-amd64, which is undersized for cosign attest, syft SBOM generation, and concurrent vale runs across larger docs trees. Thecontainerandverifycomponents already exposedrunner_tag; this brings the rest of the catalog in line. Consumers that don't set the input get the new default; consumers that need a different tier pass it explicitly. -
containercomponent now injects three OCI image-metadata build-args automatically:OCI_VERSION($CI_COMMIT_TAGfalling back to$CI_COMMIT_SHORT_SHA),OCI_REVISION($CI_COMMIT_SHA), andOCI_CREATED($CI_JOB_STARTED_AT, ISO 8601). Consumer Dockerfiles that declare matchingARGlines and reference them inLABEL org.opencontainers.image.version|.revision|.createdwill have the values populated. Consumers that don't declare the ARGs see no effect (Docker logs an unused-build-arg warning, no failure).Use case: chain of custody from a running image back to a specific git SHA, release tag, and build moment without any consumer-side
--build-argplumbing. Pairs with the forthcomingcontainer-imagestandard inreference/which codifies the full OCI label set Public Sector projects ship.
Changed
- Fedora base for
ci-latexbumped from 42 to 44 (current stable). Two release cycles of TeX Live + toolchain freshness. Renovate is pinned to<=44so future MRs stay on shipped releases (skips Rawhide). - Renovate
fileMatchmade explicit for both gitlabci and dockerfile managers —presets/*.ymlandcontainers/*.Containerfileare now unambiguously in scope. Defensive; no behavior change for already-tracked files. - Dependency freshness pass via the now-self-hosted runner:
cargo-audit0.21.2 → 0.22.0 (ci-rust image)golang1.25.7 → 1.26.3 (ci-go image; cascade rebuilds ci-go-fips + posture-verify)govulncheckv1.1.4 → v1.3.0 (ci-go image)gitlab-org/cli(glab) v1.92.1 → v1.98.1 (ci-tools image)quay.io/buildah/stablev1.43.0 → v1.43.1 (bootstrap image in catalog.gitlab-ci.yml)- UBI9 base images patched to current minor
[3.0.0]
Namespace migration. The group gitlab-com/public-sector-tools is now
gitlab-com/public-sector. Every artifact under the catalog's identity
moves with it: registry image paths, cosign OIDC identity in
signatures, SLSA provenance subjects, catalog component include URLs.
The -tools suffix dropped to reflect the seriousness of the work and
to keep URLs and provenance simpler.
This is a hard break. v2.x catalog components reference a registry path
that no longer exists. Consumers must update their .gitlab-ci.yml
include lines from gitlab-com/public-sector-tools/pipeline/... to
gitlab-com/public-sector/pipeline/... and bump the version pin to
@v3.0.0. There is no compatibility shim.
Removed
posture-verifycomponent. The audit/dashboard layer (consumer- declared claims with arbitrary verify shell, fleet-audit ledger refresh) is being divested. GitLab's compliance product is the right home for that work. Reference'sreference-checkcomponent (lands in v3.1.0) is the standards-conformance replacement at a different layer.ci-kanikoimage is not published in v3.0.0. The image depends ongitlab-com/public-sector/kaniko:latestwhich is not yet republished at the new path. The build job remains in.gitlab-ci.ymlwithallow_failure: true. Returns whenever the kaniko fork ships at the new path.
Changed
- Every image path under the catalog moves to
registry.gitlab.com/gitlab-com/public-sector/pipeline/.... v-stripped semver tag scheme unchanged. - All
include: component:examples updated to the new path. - Renovate preset and runner config target
gitlab-com/public-sector/**. templates/reference/go/verify.goexample uses<NAMESPACE>/<TOOL>placeholders instead of the previoustachhardcoded reference.
[2.7.0]
Validation cleanup. The catalog's self-validation surface was
scattered across job names that didn't read as validation
(syntax, lint-templates, sim-*). v2.7.0 renames the
template-validation jobs into a validate: namespace and adds a
single roll-up artifact that says "is this MR safe to merge?" in
one place.
Added
pipeline-self-validatesjob — runs at the end of the sim stage. Inspects everyvalidate:*,sim-*,build:*,secret_detection, andcontainer_scanningjob in the same pipeline via the GitLab Jobs API; emitspipeline-self-validation.mdwith:- Pipeline URL, ref, SHA
- Diff class detection (containerfile / template / script / ci-shape / sim / preset / renovate-config / misc) from the files changed since the MR base
- Per-job status + duration table
- Single-line verdict: SAFE iff no required validation job failed
- The artifact is paste-friendly for both human review and LLM triage — the "is this Renovate MR safe to merge" answer in one place rather than a separate confidence job per concern.
Changed
- Validation job names — the legibility move:
syntax→validate:templates:syntaxlint-templates→validate:templates:shape- sim jobs (sim-go-binary, sim-buildah-build) retain their names
(component
job_nameinput regex disallows colons); the verdict job reports them under the same table.
Why
Renovate is actuating bumps weekly. Each bump's "is this safe?" question deserves a structured answer, not lore-based pipeline reading. The verdict artifact compresses six prior validation layers into one paste-into-review unit.
[2.6.1]
Patch release. Scheduled pipelines on the catalog were firing every
job (image builds, sims, lint) because rules:changes evaluates as
"all changed" on schedule, defeating the change-based gates. v2.6.0's
catalog-as-runner intent was: scheduled pipelines run Renovate only.
Fixed
if: $CI_PIPELINE_SOURCE == "schedule" when: neverprepended to every non-renovate job's rules:.gitlab-ci.yml: 8 image build jobs + create-release + syntax + lint-templates..gitlab/ci/sim/sim-go-binary.yml+sim-buildah.yml: thejob_rulespassed to the binary / build-container-buildah components.
- Result: scheduled pipelines now produce a single
renovatejob; commit/tag pipelines unchanged.
[2.6.0]
presets/renovate-runner.gitlab-ci.yml — a Renovate runner shape
shipped as a catalog asset (companion to the existing
presets/renovate.json consumer config). The catalog project itself
becomes the Renovate runner for the PST estate by self-including
the preset; no sister renovate-runner project is required.
Added
presets/renovate-runner.gitlab-ci.yml— self-contained scheduled-pipeline definition for a Renovate bot. Ships therenovate(schedule + web) andrenovate-dry-run(MR + feature- branch) jobs, plus defaults for endpoint, platform, ignore- onboarding, require-config, and cache. Operator overridesRENOVATE_AUTODISCOVER_FILTERfor the group glob andRENOVATE_REPOSITORIESfor the dry-run target.
Changed
.gitlab-ci.yml— self-includespresets/renovate-runner.gitlab-ci.ymland overrides the two PST-specific variables. Newrenovatestage gated bysource=schedule/web, so commit/tag pipelines never trigger it. The catalog repo is now the operational center for Renovate as well as the source-of-truth for the components Renovate keeps fresh.
Migration (one-time)
The legacy gitlab-com/public-sector/renovate-runner project
is superseded by this self-included setup. Operationally:
- Disable the schedule on the legacy runner project.
- Move the
RENOVATE_TOKENmasked CI variable from the legacy runner project to this catalog project. - Create the pipeline schedule on this catalog project
(
0 6 * * 1UTC, refmain). - Archive or delete the legacy runner project.
Why catalog-as-runner
Conceptual sprawl reduction: the catalog is the source of truth for the components Renovate keeps fresh; it's also the right operational center for the bot that does the keeping. One project, one set of practices, one place to look.
[2.5.0]
Ultimate-features alignment release. CycloneDX SBOMs that the catalog
already produces (and cosign-attests) now also feed GitLab's
Dependency List via artifacts.reports.cyclonedx. The cosign
attestation channel was the external-auditor surface; this release
adds the GitLab-side ingestion that populates the Dependency
Management UI and unlocks Vulnerability Report correlation against
SBOM data.
Changed
binarytemplate -- declareartifacts.reports.cyclonedx: [dist/*.sbom.cdx.json, dist/*-sbom.cdx.json]so any CycloneDX SBOM the consumer'sbuild_scriptemits is ingested by GitLab Ultimate. Both naming patterns are matched so cargo-cyclonedx default (<name>.sbom.cdx.json) and consumer variations work without changes.container-sbomtemplate -- declareartifacts.reports.cyclonedx: [dist/sbom/sbom.cdx.json]. The syft- generated CycloneDX SBOM now feeds Dependency List in addition to being cosign-attested against the image digest. Dual channels for dual audiences (external auditor + GitLab maintainer).
[2.4.0]
Catalog dogfooding. The catalog's own image-build pipeline now uses
its own ci-buildah image as the build host, replacing the
quay.io/buildah/stable bootstrap. Pinned one release behind
(ci-buildah:v2.3.0 for v2.4.0 builds) so the chain is non-circular.
Changed
- Catalog build host image --
.image-build:template under.gitlab-ci.ymlnow usesregistry.gitlab.com/gitlab-com/public-sector/pipeline/ci-buildah:v2.3.0instead ofquay.io/buildah/stable:v1.43.0. Cosign is now baked in, so the runtime cosign install step is removed (saves ~5 sec per image build + drops the GitHub CDN dependency). - Bootstrap note in
.gitlab-ci.ymlupdated to reflect dogfooding has landed.
[2.3.0]
Maintenance-signal release. Two enhancements aimed at one job: a single maintainer should be able to grow the estate without surprises -- finding signal should surface automatically in a shape a triage LLM can act on immediately.
Added
container-scan-summarycomponent. Renders an LLM-pasteable markdown summary of GitLab Container Scanning findings. Readsgl-container-scanning-report.json, tallies by severity, lists the top Critical + High findings in a table (CVE, package, version, fix-in, severity). Image, pipeline URL, ref are surfaced at the top of the summary. Pair withJobs/Container-Scanning.gitlab-ci.yml-- container_scanning stays advisory on default-branch and MR (so pipelines don't go red on every CVE), while this component produces a single-page artifact + log block that a maintainer can paste straight into a triage conversation. Default rules: MR + default-branch + tag. The summary is empty-state safe -- if no report is found (rule-excluded upstream), the artifact says so honestly rather than failing the job.
Changed
valetemplate -- emit "vale: no findings on " when vale produces zero output. Eliminates the 0-byte artifact ambiguity (no-op vs clean pass). Artifact + log now record the positive result.
[2.2.0]
Minor release. vale component grows a mode input that splits the
ruleset into two presets: project (default) for READMEs and
project-level markdown, and docs for content shipping to
docs.gitlab.com. Existing consumers that want the v2.1.x behavior
pin mode: docs.
Added
valecomponent: newmodeinput with options[project, docs], defaultproject.mode: projectgenerates a.vale.inithat enablesgitlab_base + gitlab_docspackages but disables the rules that only apply to docs.gitlab.com content:gitlab_docs.FrontMatter,gitlab_docs.InternalLinkExtension,gitlab_docs.InternalLinksCode,gitlab_docs.RelativeLinks, andgitlab_base.Spelling.gitlab_base.Spellingis disabled because the upstream rule ships withvocab: false(Spelling.yml:11), which silently ignores any project-sideVocab/<name>/accept.txt. Term-substitution and style rules (Substitutions,SubstitutionWarning,Offerings,British,Uppercase, etc.) still fire and carry the high-value discipline (e.g.airgapped→offline,self-managedcapitalization, GitLab branded-term enforcement).mode: docspreserves the v2.1.x behavior: fullgitlab_base + gitlab_docsruleset.
Changed
valecomponent default behavior shifts tomode: project. This is the new sensible default for project-level READMEs. Consumers on v2.2.0+ that ship content to docs.gitlab.com must opt in tomode: docsexplicitly. No code changes needed for projects that pin@v2.1.xand don't bump.
Fixed
posture-verifyhonors$-references intemplate_vars. The v2.0.0 implementation usedshlex.quoteon every template_var value, which wraps strings in single quotes and prevents shell expansion. Consumers like kaniko declaringvariants_all: ["${KANIKO_VERSION}-fips", ...]saw the literal${KANIKO_VERSION}reachcosign verify, which then failed withcould not parse reference. v2.2.0 uses bash double-quote semantics so$VAR/${VAR}expand against the verify subprocess's environment (inherited from the job). Literal$must be written\$in YAML when needed.
[2.1.1]
Patch release. Restores the binary component's runtime cosign install
for consumers that override build_image to a non-catalog image
(rust:1.88.0, golang:1.25, etc). Surfaced during the
assay/posture/tach migrations -- their build_image overrides don't
include cosign, and v2.0.0's image-cutover MR removed the
unconditional install.
Fixed
binary-- conditional cosign install. Skips the install if cosign is already on PATH (catalog ci-go/ci-rust images), curl- installs v2.6.3 otherwise. Restores compatibility with the wide range of consumer-overriddenbuild_imagevalues that worked under v1.x.
[2.1.0]
Additive release. Goal: stable footing for consumer migration. One new component, in-repo sim harness expanded, and the buildkit/kaniko engine components are explicitly deferred with documented gaps so the v2.1.0 contract is honest about what ships.
Added
build-container-buildahcomponent. Functionally equivalent to the legacycontainercomponent, named for symmetry with the buildkit and kaniko siblings that will follow in later v2.x. Exposes animageinput so consumers (and the sim harness) can override the catalog-managedci-buildahreference. Seetemplates/build-container-buildah/README.md.- Sim harness expansion.
.gitlab/ci/sim/sim-buildah.ymlbuilds a tiny UBI-micro fixture image via the new component end-to-end and validates cosign sign-against-digest on the just-builtci-buildahimage. The harness is the catalog's own integration test surface. lint-templatesenforcesspec.description≤ 256 chars. GitLab's parser rejects longer descriptions; the local lint now catches this before MR pipelines.
Changed
binarycomponent is CWD-resilient. A consumerbuild_scriptthatcd's elsewhere no longer breaks the subsequentdist/manipulation -- every step that touchesdist/cd's back to$CI_PROJECT_DIRfirst;shopt -s nullglobso empty globs no longer expand to literaldist/*. Surfaced by the sim-go-binary test pre-v2.0.0; carried forward as a quality fix.
Known gaps (deferred to a later v2.x release)
build-container-buildkit: POC sim (.gitlab/ci/sim/sim-buildkit-poc.yml, manual trigger viaREBUILD_BUILDKIT_POC=true) established that rootless buildkitd via rootlesskit on the gitlab.com SaaS Linux Docker executor errors withNo subuid ranges found for user 0 ("root")because rootlesskit refuses to run as root and the runner provides a root user. Real fix needs a non-root user baked intoci-buildkitplus/etc/subuid+/etc/subgidplus GitLab runner accepting the non-root USER directive. The image rework lands in a later v2.x.build-container-kaniko: blocked onfactory/kanikopublishing the chainguard-fork kaniko image at:latest. Theci-kanikoContainerfile + build job exist (withallow_failure: true) and unblock automatically once the upstream ships.
[2.0.2]
Patch release. Drops a redundant catalog publish step that 422-failed the create-release job on v2.0.1 even though the release itself was created (and auto-published to the catalog) successfully.
Fixed
- create-release no longer 422s on the catalog-publish step.
glab release createon a project that is already a CI/CD Catalog resource auto-publishes the version. The explicitglab repo publish catalogthat followed was redundant and returned422 {message: Release has already been published}, failing the job. Verified via GraphQLciCatalogResourcequery immediately after v2.0.1's release-create: the version appeared in catalog within seconds.
[2.0.1]
Patch release: the v2.0.0 create-release CI job failed to authenticate
because glab sent CI_JOB_TOKEN as a Bearer token, which the
/api/v4/user endpoint rejects. v2.0.0 was manually published to the
catalog as a one-time fix; v2.0.1 makes the CI path work
end-to-end.
Fixed
-
create-release CI auth. Added
GLAB_ENABLE_CI_AUTOLOGIN: "true"to the job variables. WithGITLAB_CI=true(always set in CI), this routesCI_JOB_TOKENthroughgitlab.JobTokenAuthSource(not Bearer), which the release and catalog-publish endpoints accept. Same mechanism auto-discovers host/protocol/ca-cert fromCI_SERVER_*predefined variables.Source:
gitlab-org/cli internal/config/config_mapping.go:47-50.
[2.0.0]
Breaking release. Consumers must update their includes to the
new shape. Pin to @v1.6.1 to defer the migration.
Highlights
- Catalog-managed images. Every tool a component needs ships in
an image the catalog builds, signs, and publishes at every tag.
No more curl-installs of cosign, syft, glab, or skopeo at job
runtime. Image set:
ci-tools,ci-go,ci-go-fips,ci-rust,ci-buildah,ci-buildkit,ci-kaniko,posture-verify. $[[ component.version ]]interpolation. Every template declaresspec:component: [name, version]. Image references resolve topipeline/<image>:<your-catalog-tag>automatically.- Typed, validated inputs. Every input declares
type,description,default(or marked required by absence of default), and where appropriateoptionsenums andregexvalidators. Lint runs on every MR. - Modern publish path. Catalog publishes via
glab repo publish catalog(glab >= 1.55.0), replacing the release-cli path.
Added
containers/ci-tools.Containerfile-- shared UBI9-minimal base with cosign, syft, glab, skopeo, jq, curl, git.containers/ci-go.Containerfile-- Go toolchain layered on ci-tools.containers/ci-go-fips.Containerfile-- Go withGOFIPS140=v1.0.0.containers/ci-rust.Containerfile-- Rust toolchain + clippy + rustfmt + cargo-audit + cargo-deny + cargo-zigbuild + zig.containers/ci-buildah.Containerfile-- rootless OCI build engine.containers/ci-buildkit.Containerfile-- rootless OCI build engine (modern default; replaces archived kaniko upstream).containers/ci-kaniko.Containerfile-- Kaniko (Chainguard fork) for permanent onramp/offramp customers.containers/posture-verify.Containerfile-- tooling for the posture-verify component.CONTRIBUTING.md-- canonical template shape and tag protocol.scripts/lint-templates.py-- enforces the v2 canonical shape on every MR. Run locally withpython3 scripts/lint-templates.py.- Renovate custom-manager regex for
containers/*.ContainerfileARG VERSION pin-comments. Tool bumps auto-merge on minor/patch.
Changed
-
All 14 templates moved to canonical v2 shape. Each declares
spec:component: [name, version]; every input is typed; image references use the catalog's managed images via$[[ component.version ]]. The full input map:Old (v1, kebab-case) New (v2, snake_case) job-namejob_nameimage-nameimage_namecosign-versioncosign_version(removed where hardcoded image carries the version)syft-versionsyft_version(removed where hardcoded image carries the version)tag-suffixtag_suffixrunner-tagrunner_tagmin-alert-levelmin_alert_levelallow-failureallow_failurejob-rulesjob_rulesbuild-imagebuild_imagebuild-scriptbuild_scriptpackage-namepackage_namebinary-jobbinary_jobattest-jobattest_jobcontainer-imagecontainer_imagecontainer-jobcontainer_jobneeds-jobneeds_jobclone-pathclone_pathupstream-urlupstream_urlupstream-refupstream_refsecurity-filesecurity_filevale-versionvale_versionstyles-archivestyles_archivearch-suffixesarch_suffixesarch-jobsarch_jobsfinal-suffixfinal_suffixskip-if-tag-existsskip_if_tag_existsinstall-commandinstall_commandbuild-commandbuild_commandoutput-diroutput_dirfail-if-emptyfail_if_empty(and is nowtype: boolean, was string-typed"true") -
auditcomponent:languageinput is now constrained byoptions: [rust, go, node, python]. Unknown values fail at compose time instead of at script time. -
extra-assetscomponent:fail_if_emptyis now a propertype: boolean(was string-typed"true"in v1). -
valecomponent: keeps a runtime install of vale (vale is docs-only and not bundled inci-tools). All other curl-installs removed. -
Endorsement language removed repo-wide. "DoD-grade", "FedRAMP-aligned", and "Iron Bank compatible" framings replaced with descriptive equivalents. Standards references (NIST 800-53, CMMC 2.0 L2, SSDF v1.1, FIPS-140-3) retained as factual citations.
-
Catalog publish flow now uses
glab repo publish catalog $CI_COMMIT_TAGafterglab release create, replacing the release-cli path (release-cli is on a deprecation track for GitLab 20.0). -
Catalog tag protocol is
vX.Y.Zfor finals andvX.Y.Z-rc.Nfor release candidates.~latestexcludes pre-releases (source-confirmed); RC tags publish images but the RC publish to catalog only happens for finals.
Removed
cosign_versioninput fromattest,container,container-attest,container-manifest,container-sbom,verify. The version is pinned via the catalog image at the consumer's catalog tag.syft_versioninput fromcontainer-sbom.
Migration guide
For each catalog component you consume:
- Bump the include: change
@v1.6.1to@v2.0.0(or pin to@v1.6.1to stay on v1). - Convert input keys to snake_case (see the table above).
- Drop
cosign_version/syft_versionwhere you set them on attest/container/container-attest/container-manifest/ container-sbom/verify. - Audit: if you set
language, confirm the value is one ofrust|go|node|python(other values now error at compose time). - Binary: the default
build_imageis nowci-go. If you build Rust, setbuild_image: registry.gitlab.com/gitlab-com/public-sector/pipeline/ci-rust:$[[ component.version ]].
Internal (catalog repo only)
- Project Container Registry was enabled (was disabled, blocking
CI_REGISTRY_IMAGEresolution). - Commit-message push rule regex fixed (double-backslash bug that
rejected scoped Conventional Commits; now accepts
feat(scope)!:for breaking-change markers too).
Verification
Verified before pushing:
- The archive URL shape at a pinned ref:
?path=doc/.valenests under<project>-<ref>-doc-.vale/doc/.vale/, so--strip-components=3leavesgitlab_base/andgitlab_docs/at the StylesPath root, which is whatPackages = gitlab_base, gitlab_docsresolves against. 52 base rules, 17 docs rules, 24K archive. - The drift claim, by diffing the pinned tag against
masterthe same day. - All three styles-resolution branches: baked present (passes, names the pinned ref in the log), baked missing (exits 2), override URL that 404s (exits 2 after retries).
- Template structure: two YAML documents, every input carries
type+description, one job key, no global keywords.
The image build itself runs in this pipeline; that is where the baked layers first execute.