CI gate and tools shared by the git.pop.coop/deb Debian packages (sbuild, lintian, autopkgtest, reproducibility; no Docker)
Find a file
Marcos Méndez 9c3f60e2d6
All checks were successful
lint / shellcheck (push) Successful in 1s
selftest / deb-ci (push) Successful in 2m22s
Merge pull request 'fix: workflows set GIT_PAGER=cat' (#2) from fix/pager into main
2026-09-30 03:25:03 -03:00
.forgejo/workflows fix: anchor the template checks in lint 2026-09-30 03:16:41 -03:00
bin fix: deb-import-pypi, an apostrophe closed the quoted Python program 2026-09-30 02:46:03 -03:00
etc fix: close the remaining review findings 2026-09-30 02:37:49 -03:00
template fix: workflows never wait on the terminal 2026-09-30 03:07:34 -03:00
README.md docs: README states the autopkgtest verdicts the gate accepts 2026-09-30 02:45:17 -03:00

deb/ci: the gate and the tools of the git.pop.coop/deb packages

Every repository in git.pop.coop/deb is one Debian source package in the Debian Python Team layout (git-buildpackage: debian/master or debian/<suite>, upstream, pristine-tar). This repository holds what they share.

Path What it is
bin/deb-ci the CI gate: source package from the committed HEAD and the pristine-tar tarball, sbuild in the clean chroot tarball, lintian (errors and warnings fail), autopkgtest against trixie when the package declares tests, and a second build whose .deb files must be byte-identical
bin/deb-ci-chroot creates or refreshes the chroot tarball ~/.cache/sbuild/trixie-amd64.tar.zst (trixie, trixie-updates, trixie-security, main) without root
bin/deb-import-pypi imports an upstream sdist from PyPI into the upstream and pristine-tar branches after checking it against the sha256 PyPI publishes
etc/sbuild.conf the sbuild settings deb-ci relies on, read after the runner's own configuration so they always win
template/ files every package repository starts with: the Forgejo workflow, debian/gbp.conf, debian/salsa-ci.yml, debian/source/

No Docker anywhere: sbuild and autopkgtest run in unshare mode, as an ordinary user, on the runner keel-deb-runner (label keel-deb).

Running the gate by hand

On a Debian 13 machine with sbuild mmdebstrap uidmap lintian autopkgtest git-buildpackage pristine-tar devscripts zstd python3-packaging and a subordinate uid range for the user:

bin/deb-ci-chroot
bin/deb-ci /path/to/python-foo

Exit codes of deb-ci: 0 pass; 1 unexpected error; 2 usage or environment (no chroot tarball, unreadable configuration); 3 source package; 4 build, lintian or autopkgtest; 5 the rebuild differs.

What the gate does and does not prove

  • The chroot is the tarball, never a substitute. sbuild's default is to ignore a tarball older than 7 days and bootstrap a new chroot from the changelog's distribution, which is unstable: builds would silently move to sid. etc/sbuild.conf switches that off and deb-ci names the tarball by absolute path. Refreshing it is deb-ci-chroot's job, run weekly on the runner; deb-ci prints the tarball's age on every run.
  • The autopkgtest release is fixed to trixie. The changelog says unstable, which is what Salsa expects, and sbuild's %r would pass that on. DEB_CI_RELEASE changes it for every step at once.
  • The verdicts are read from the build log. Lintian must report pass or info; a skipped or uninstallable lintian fails the gate. When the package has debian/tests/control or a Testsuite field, autopkgtest must report pass, or no tests when only superficial tests ran (such as the import test of autopkgtest-pkg-python), and its per-test lines must show at least one PASS and nothing skipped, flaky or failed. The unshare backend cannot run tests that need isolation-container or isolation-machine, so those count as skipped.
  • The identical rebuild catches nondeterminism, not every reproducibility bug. Both builds use the same chroot, build path, locale and timezone; only the time differs. Varying the environment (reprotest) is future work.
  • Only committed content is built. deb-ci builds HEAD in a private clone, where upstream .gitattributes (export-subst, eol, filters) cannot change a byte of the upstream files.

Importing a new upstream version

The CI of a pull request sees only the base repository's upstream and pristine-tar branches. Import the new version with deb-import-pypi, push upstream, pristine-tar and the upstream/<version> tag to the base repository first (they carry the verified tarball, not packaging), then open the pull request for the debian/* branch.

Versions

  • Not in Debian: <upstream>-0+keel1. It sorts below -1, so a later Debian upload replaces it, and it carries the +keelN suffix that the Keel archive tooling requires.
  • Backport of a package in sid: <sid version>~bpo13+keel1, with the changelog distribution trixie (the Keel archive's codename, not Debian's trixie-backports).
  • Package names are Debian's, never Keel's.

Security of the runner

The runner executes jobs directly on its host, as the user keel-deb-runner, and a pull request's workflow file comes from the pull request itself. Anyone whose pull request runs can therefore run commands as that user: read the runner's registration and replace the chroot tarball that later builds use. Until jobs from untrusted pull requests need an approval, or run on a throwaway machine, only members of the organization should open pull requests whose CI runs. The package repositories pin nothing of this repository yet: their workflow clones deb/ci main, so a change here reaches every package at once, and goes through the same review.