ci(drift): run the git-touching tests on a git version matrix #531

Merged
hexajon merged 1 commit from issue-525-git-version-matrix into main 2026-09-16 20:57:49 +00:00
Owner

Summary

Closes #525. Phase 2 of the git-currency work, after #524 pinned the output contract and the 2.38.0 floor.

The gap

CI runs one container image, pinned by digest, so every test runs against one git version. git --version is logged at ci.yml:125 and :219, but nothing asserts on it. Users run Homebrew, Xcode, Alpine, RHEL and Windows builds. A git change cannot fail CI today, by construction.

What this adds

A cron-only git-matrix job in drift.yml, with three legs:

  • the 2.38.0 floor that ADR 0038 fixes
  • the distro git in the job image, read at run time, not hardcoded
  • the latest upstream release, resolved at run time

It sits on the cron for the same reason govulncheck does: it answers a question that changes while the tree stays fixed. A PR never changes that answer, so it stays off the PR path.

Why a source build

I compared the two ways to get several git versions. A git 2.38.0 source build takes 117 s with make -j2 and the tarball is 7.1 MB. A second golang image variant is 296.8 MB. A debian:*-slim base is not a candidate, because the job needs the Go toolchain. The source build is also the only way to test the 2.38.0 floor exactly, because no current distro ships it.

The floor is static until ADR 0038 moves it, so the built binaries are cached in a runner-local named volume, keyed by version. The floor build is paid once and reused. The distro leg costs nothing. Only the latest leg rebuilds, when git releases.

Trust anchor

Every other artifact in this CI is digest-pinned, so the new download is pinned too. A hardcoded hash per version cannot cover a leg that moves, so the anchor is a key, not a hash:

  1. The workflow pins fingerprint B8868C80BA62A1FFFAF5FDA9632D3A06589DA6B1 and embeds that public key.
  2. It imports the key and asserts the fingerprint.
  3. It verifies kernel.org's signed sha256sums.asc.
  4. It takes each tarball's expected hash from that verified list.

A bad signature or a missing row fails the job before any build runs. This key is kernel.org's checksum autosigner, not the git maintainer's own release key. The workflow comment says so, so a later reader knows exactly what the anchor is.

Verification

I checked the parts that would otherwise fail unattended at 04:17 UTC:

  • The embedded key imports and its fingerprint matches the pinned value.
  • That key gives a good signature on the live sha256sums.asc.
  • The floor row in the verified list matches the SHA-256 of the tarball I downloaded and built independently: 923eade2....
  • latest resolves to 2.55.0 against the live listing, and it has a verified row. The release-candidate tarballs do not contaminate the result.
  • The new test passes and does not trip the AST guard that #524 added against direct git calls.

For the second acceptance criterion I broke conflictLineRe on purpose, ran the test, and got a failure naming git 2.47.3. I then reverted the break. The tree carries no deliberate break.

The new test

TestParseMergeTreeConflicts_RealGit runs the real git merge-tree --write-tree and parses its true output. The existing test pins the parser against fixed strings; this one pins it against the git binary on PATH. ADR 0038 names that parse as the reason for the 2.38 floor, so it is the right thing to exercise on every leg. Without it the matrix would mostly re-run tests that stub git, and could not detect a reworded CONFLICT line.

The test skips, and names the git it found, on a git too old for merge-tree --write-tree.

Before this can run

The docker backend refuses any volume mount that is not allowlisted. The runner VM must add madtea-gitcache to valid_volumes and restart, or the job fails to start. docs/contributing/ci.md now records this, and corrects the line that said the allowlist needs no change.

Known limits

  • The nightly runtime is not measured. The job runs seven test packages three times, plus up to two source builds.
  • The version-keyed cache never evicts. Old latest builds accumulate in the volume.
  • internal/cmd/git parses git stderr too, but its tests stub the runner, so the matrix cannot reach that code. A real-git test there is the way to add it.

Closes #525

## Summary Closes #525. Phase 2 of the git-currency work, after #524 pinned the output contract and the 2.38.0 floor. ## The gap CI runs one container image, pinned by digest, so every test runs against one git version. `git --version` is logged at `ci.yml:125` and `:219`, but nothing asserts on it. Users run Homebrew, Xcode, Alpine, RHEL and Windows builds. A git change cannot fail CI today, by construction. ## What this adds A cron-only `git-matrix` job in `drift.yml`, with three legs: - the 2.38.0 floor that ADR 0038 fixes - the distro git in the job image, read at run time, not hardcoded - the latest upstream release, resolved at run time It sits on the cron for the same reason `govulncheck` does: it answers a question that changes while the tree stays fixed. A PR never changes that answer, so it stays off the PR path. ## Why a source build I compared the two ways to get several git versions. A git 2.38.0 source build takes 117 s with `make -j2` and the tarball is 7.1 MB. A second `golang` image variant is 296.8 MB. A `debian:*-slim` base is not a candidate, because the job needs the Go toolchain. The source build is also the only way to test the 2.38.0 floor exactly, because no current distro ships it. The floor is static until ADR 0038 moves it, so the built binaries are cached in a runner-local named volume, keyed by version. The floor build is paid once and reused. The distro leg costs nothing. Only the latest leg rebuilds, when git releases. ## Trust anchor Every other artifact in this CI is digest-pinned, so the new download is pinned too. A hardcoded hash per version cannot cover a leg that moves, so the anchor is a key, not a hash: 1. The workflow pins fingerprint `B8868C80BA62A1FFFAF5FDA9632D3A06589DA6B1` and embeds that public key. 2. It imports the key and asserts the fingerprint. 3. It verifies kernel.org's signed `sha256sums.asc`. 4. It takes each tarball's expected hash from that verified list. A bad signature or a missing row fails the job before any build runs. This key is kernel.org's checksum autosigner, not the git maintainer's own release key. The workflow comment says so, so a later reader knows exactly what the anchor is. ## Verification I checked the parts that would otherwise fail unattended at 04:17 UTC: - The embedded key imports and its fingerprint matches the pinned value. - That key gives a good signature on the live `sha256sums.asc`. - The floor row in the verified list matches the SHA-256 of the tarball I downloaded and built independently: `923eade2...`. - `latest` resolves to 2.55.0 against the live listing, and it has a verified row. The release-candidate tarballs do not contaminate the result. - The new test passes and does not trip the AST guard that #524 added against direct git calls. For the second acceptance criterion I broke `conflictLineRe` on purpose, ran the test, and got a failure naming git 2.47.3. I then reverted the break. The tree carries no deliberate break. ## The new test `TestParseMergeTreeConflicts_RealGit` runs the real `git merge-tree --write-tree` and parses its true output. The existing test pins the parser against fixed strings; this one pins it against the git binary on PATH. ADR 0038 names that parse as the reason for the 2.38 floor, so it is the right thing to exercise on every leg. Without it the matrix would mostly re-run tests that stub git, and could not detect a reworded `CONFLICT` line. The test skips, and names the git it found, on a git too old for `merge-tree --write-tree`. ## Before this can run The docker backend refuses any volume mount that is not allowlisted. The runner VM must add `madtea-gitcache` to `valid_volumes` and restart, or the job fails to start. `docs/contributing/ci.md` now records this, and corrects the line that said the allowlist needs no change. ## Known limits - The nightly runtime is not measured. The job runs seven test packages three times, plus up to two source builds. - The version-keyed cache never evicts. Old `latest` builds accumulate in the volume. - `internal/cmd/git` parses git stderr too, but its tests stub the runner, so the matrix cannot reach that code. A real-git test there is the way to add it. Closes #525
ci(drift): run the git-touching tests on a git version matrix
Some checks failed
drift / govulncheck (pull_request) Has been skipped
drift / forgejo-smoke (pull_request) Has been skipped
drift / git-matrix (pull_request) Has been skipped
drift / govulncheck (push) Has been skipped
drift / surface-diff (push) Successful in 1m28s
drift / forgejo-smoke (push) Has been skipped
drift / git-matrix (push) Has been skipped
ci / mcp-conformance (push) Successful in 4m16s
drift / surface-diff (pull_request) Failing after 12m57s
ci / mcp-conformance (pull_request) Failing after 13m5s
ci / gate (pull_request) Failing after 13m5s
ci / gate (push) Successful in 12m55s
78956d3154
CI runs one container image, pinned by digest, so every test runs
against one git version. Users run a different git. CI cannot see a
git change, by construction.

This adds a cron-only git-matrix job to drift.yml. It runs the
git-touching tests on three gits: the 2.38.0 floor from ADR 0038, the
distro git in the job image, and the latest upstream release. It sits
on the cron because it answers a question that changes while the tree
does not, which is the same reason govulncheck is cron-only.

The floor and the latest git are built from source. A pinned
kernel.org autosigner key verifies the signed checksum list, and each
tarball hash comes from that verified list. A bad signature or a
missing row fails the job before any build runs. The built binaries
are cached in a runner-local volume, so the floor build is paid once
and reused.

A new test runs the real git merge-tree --write-tree and parses its
true output. ADR 0038 names that parse as the reason for the 2.38
floor. A reworded CONFLICT line now fails the matrix, and the failure
names the git version that broke.

The runner VM must allowlist the madtea-gitcache volume before this
job can run. docs/contributing/ci.md records that requirement.
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
sixfold-space/madtea!531
No description provided.