feat(renovate): freeze lab/** by default, carve out build tooling

What

Freezes gitlab-com/public-sector/lab/** by default: reproduction material stops being renovated unattended, while staying fully visible and one checkbox from a thaw.

Why

A lab exists to reproduce a captured scenario, so its pins are the experimental conditions its receipts describe — not choices that should track the moving world. Renovating them does not maintain the lab, it voids the evidence.

This is not hypothetical. Three of the six benchmark subjects in lab/rootless were bumped before this rule existed:

MR Change Files
!9 (merged) alpine 3.20 → 3.24 bench/go-scratch/Dockerfile, archive/pss-matrix/Dockerfile
!10 (merged) golang 1.22 → 1.26 bench/go-scratch/Dockerfile, archive/pss-matrix/Dockerfile
!11 (merged) spring-boot 3.3.5 → 3.5.16 bench/java-maven/pom.xml
!4 (merged) flask 3.1.0 → 3.1.3 bench/python-ubi/requirements.txt

The drift is undetectable from the receipts. A timing.json reads {"cell":"buildah-rootless-go-scratch","dockerfile":"go-scratch","wall_seconds":30,"result":"pass"} — it names its subject only by name and records no versions, so it still claims a pass while the thing it passed on changed underneath it.

How

Implemented with matchRepositories in the shared preset. This needs no inheritConfig, no second runner pass, and no per-group config repo — the mechanism originally sketched for this was considerably heavier.

The glob was checked against real minimatch at both nesting depths, since the project-trustchain and project-lattice labs sit one level deeper than lab/rootless and would otherwise have stayed live:

MATCH  gitlab-com/public-sector/lab/rootless
MATCH  gitlab-com/public-sector/lab/project-trustchain/architecture
MATCH  gitlab-com/public-sector/lab/project-lattice/mcp
no     gitlab-com/public-sector/reference
no     gitlab-com/public-sector/manifold

Three deliberate choices

  1. dependencyDashboardApproval, not enabled: false. Silence would cost visibility — you would stop learning that published evidence sits on a base with a known CVE. The dashboard still lists every available update; nothing is created until a human ticks the box.

  2. A .gitlab-ci.yml carve-out. Build tooling is not evidence, and letting it rot makes the lab unbuildable, which loses the evidence just as surely. The split is per-path, not per-project: every .gitlab-ci.yml-only lab MR merged so far was harmless, every bench/ or archive/ one was not. Rule order is load-bearing — later packageRules win, so the carve-out follows the freeze.

  3. A vulnerabilityAlerts override, so a frozen lab still opens an MR for a real advisory. A freeze is not a licence to sit on a vulnerable base, least of all in a lab cited to a customer.

Validation

  • renovate-config-validator --strict clean across all three config files (pinned to renovate@43.272.0; an unpinned npx resolves a version predating managerFilePatterns and reports false errors).
  • Control-tested the validator: a deliberately bogus matchRepositoriesTYPO in the same position is rejected, so acceptance of matchRepositories is meaningful rather than a silently-ignored key.
  • Glob semantics verified against minimatch directly (above).
  • Diff kept surgical — an earlier pass reformatted every inline array and produced 91 lines of churn for a 20-line change; that was reverted.

Follow-up, not in this MR

Receipts should record their subject's resolved versions/digests, so this class of drift becomes detectable rather than silent. Separately, the four lab/rootless files above need reverting to restore the captured conditions.

Merge request reports

Loading