fixtures: acceptance tests for every gate

One fixture per check, each pinning the specific defect or behaviour
it exists to catch: descriptor and schema validation, kconfig hazards
and effective-config resolution, kunit/kselftest adapters and their
debug-verdict paths, kmemleak verdict classification, legacy-symbol
scanning, kernel-doc and comment-audit checks, commit-wise and
squash-equivalence proofs, provenance and mbox construction, stream
integrity, and the negative-control baseline that keeps a known-clean
tree known-clean.

Signed-off-by: Konstantin Sinyuk <ksinyuk@kernel.org>
41 files changed
tree: f2263aea6d3b6e103f5c2a6ed2b7e93573011820
  1. doc/
  2. LICENSES/
  3. tools/
  4. .gitignore
  5. COPYING
  6. README.md
README.md

release-harness

Qualification gates for a Linux kernel patch series, run before it is posted.

The harness takes a series descriptor, asks Git which paths each revision actually submits, classifies those paths, runs the checks that classification selects, and reports whether each check examined what the series really contains. It runs everything in a pinned container, boots guests for the tests that need one, and records enough about each run to re-derive the result later.

It is not a CI system and does not want to be one. It answers a narrower question: is this series ready to send?

Why the accounting matters

Most of the design follows from one observation: a check that silently examines nothing passes.

So every check reports what it expected to examine and what it did, and those numbers have to agree. Expected counts come from a contract the revision names, never from a value written into a gate — a count inside a check is a count nobody can see is wrong. Exit codes carry the distinction:

CodeMeaning
0pass
1a real defect
2the gate did not run against its intended inputs

A 2 is not a softer 1. It means the answer is unavailable, and it is reported that way rather than being rounded to either side.

Getting started

Requirements: Linux, Python 3.11+, Git, Docker, and GNU tar. Everything else — sparse, smatch, checkpatch's dependencies, virtme-ng, QEMU — lives in the pinned image.

git clone https://git.kernel.org/pub/scm/linux/kernel/git/ksinyuk/release-harness.git
cd release-harness

export LAB_ROOT=$PWD/..            # workspace; defaults to the parent anyway
export LAB_REPO=$LAB_ROOT/linux.git

# One-time reference repository setup. This shallow fetch contains the base and
# all twelve commits used by the configuration shipped with the harness.
#
# drm-fabric-next is a branch, not a tag: it moves as the series is revised, so
# unlike drm-fabric-v1 there is no annotated ref freezing it. Re-run this fetch
# to pick up a new revision, and see "The series it ships configured against"
# below for the exact commit this tree was validated against.
git init --bare "$LAB_REPO"
git -C "$LAB_REPO" fetch --depth=13 \
  https://git.kernel.org/pub/scm/linux/kernel/git/ksinyuk/linux.git \
  refs/heads/drm-fabric-next:refs/heads/drm-fabric-next

tools/gates/rfc-gate.py selfcheck  # the harness against itself
tools/gates/rfc-gate.py catalogue  # every check it can run

Then build the container and the guest kernels once:

tools/gates/lab-tier.sh foundation --phase both \
  --readonly-rev drm-fabric-next~6 --provision-rev drm-fabric-next

and thereafter, per change:

tools/gates/lab-tier.sh quick --phase both \
  --readonly-rev drm-fabric-next~6 --provision-rev drm-fabric-next  # ~1.25 min
tools/gates/lab-tier.sh release --phase both   # ~22 min

See doc/configuring.md for pointing it at your own series, and doc/tier-cost-and-parallelism.md for what each tier costs and why.

Layout

tools/gates/
  rfc-gate.py         policy-driven check runner
  rfcgate/            the check engine (checks.py holds the checks themselves)
  lab-tier.sh         tier driver: foundation, quick, polish, release
  lab-env.sh          single authority for paths and pins — source it, don't parse it
  lab-run             run a command inside the pinned container
  lib/                shell libraries: control, telemetry, verdict, revsel, out-usage
  ref/                the series descriptor and its contracts
  fixtures/           self-tests, one per check, each naming the defect it pins
  guest/              workloads that run inside a booted guest
tools/comment-audit/  the comment analyser and its calibration corpus
tools/container/      the pinned toolchain image recipe
doc/                  see doc/README.md

The series it ships configured against

The reference configuration describes a real, published series: the twelve commits on the drm-fabric-next branch in ksinyuk/linux.git, adding a vendor-neutral DRM fabric topology layer. That is deliberate — every check has something real to run on, and every expected count is a number that is actually true.

Unlike the drm-fabric-v1 tag it superseded, drm-fabric-next is a branch and moves as the series is revised. This tree was validated against base e3e05bddbe9fea67639d2a3551e7cf2d473b804c, readonly milestone tip 96973c7156905d3c58a313bdc750fed7f6221093, and provisioning tip b923b88277e0b1d979411e99b2e18c2b8dc694ce. If the branch has moved since, the counts in tools/gates/ref/runtime-contracts.yaml describe that commit, not necessarily the branch's current head — re-run foundation and quick and compare before trusting a verdict against a moved branch.

Revision ids are opaque to every executable here. Nothing tests for the string readonly, and fixtures/squash-equivalence.py is the standing proof: it derives a one-revision descriptor from the real one and requires identical classification. Your series can have one revision or five, named whatever you like.

Status

It has qualified one series end to end: drm-fabric-v1, the tag the reference configuration pointed at until it was retargeted to the drm-fabric-next branch above. The tier costs quoted above are measured on that earlier run, on one machine; treat them as the right order of magnitude rather than a promise, and as not yet re-confirmed against drm-fabric-next.

The base image digest and the Sparse/Smatch source archives are pinned, and the built image is checked for the tool versions and runtime dependencies the gates consume. Distribution packages and transitive Python packages are not locked by content hash, so rebuilding is not bit-for-bit reproducible. Preserve the image ID and generated provenance record when comparing evidence across runs.

The historical comment-calibration source commit is not yet advertised by a public ref. The gate and fixtures run, but an independent reader cannot rerun that historical measurement until the commit is published or the calibration is redone against a named public Linux revision; see doc/comment-policy.md §6.

Some things are known-unfinished and say so where they are: the smoke tier exists as a design and a standalone script but is not wired into the tier driver (doc/tier-cost-and-parallelism.md §3), and the supervision layer's phased rollout is not complete (doc/design/supervised-gates.md).

Licence

GPL-2.0, with a small number of MIT-licensed files. Source files carry SPDX tags; data and documentation without an inline tag are covered by COPYING and any directory-level licensing note it names.