Repository navigation
ci: build the standard Linux binaries in a pinned Ubuntu 22.04 container and assert the glibc floor - #2887
ci: build the standard Linux binaries in a pinned Ubuntu 22.04 container and assert the glibc floor#2887DeusData wants to merge 6 commits into
Conversation
check-binary-composition.sh gains A6-glibc-floor. When a venue declares CBM_GLIBC_FLOOR (and optionally CBM_GLIBCXX_FLOOR), no GLIBC_* or GLIBCXX_* symbol version a dynamic ELF requires may be newer than that floor. The names come from the dynamic version tables, which the loader checks at startup and every strip level keeps. Versions compare numerically (2.4 is older than 2.35); GLIBC_ABI_DT_RELR counts as 2.36. Other unnumbered names (GLIBC_PRIVATE) and a dynamic binary that requires no GLIBC_* version at all fail closed. A static ELF passes, Mach-O and PE report n/a, and with no floor declared A6 reports n/a, so local builds, the local artifact smoke and contract stubs on newer distros are unaffected. A malformed floor is a usage error (exit 2). The release candidate derivation contract proves it on the stripped stub candidate: floor 1.0 fails and 999.0 passes on glibc Linux, n/a on Mach-O and PE, fail closed on a non-glibc Linux, exit 2 on a malformed floor. Refs #2486 Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
The standard (dynamic) Linux release binaries were built on the hosted runners Ubuntu 24.04 userland, so they required GLIBC_2.38 and GLIBCXX_3.4.32 and refused to start on Ubuntu 22.04 (#2486). A new build-linux job in _build.yml builds linux-amd64 and linux-arm64 inside ubuntu:22.04 pinned by the digest of its multi-arch index (sha256:076e7bf8..., resolved 2026-11-02 from Docker Hub, one pin for both arches) with the distro toolchain (GCC 11.4 against glibc 2.35), and declares CBM_GLIBC_FLOOR=2.35 / CBM_GLIBCXX_FLOOR=3.4.30 (what the image provides) for the composition gate, so every release candidate is checked. The hosted runner only hosts the container, so a runner image retirement no longer moves the floor. build-unix keeps the two macOS legs, the portable legs are unchanged, select-package needs the new job and the artifact names stay the same. pr.yml gains a report-only glibc-floor leg: the production binary built in the same image and checked by the composition gate with the same floor whenever the selection holds smoke-ubuntu. It is not in ci-ok needs and has no lane, so it never blocks or passes a merge. The venue parity contract asserts the digest pin, one shared digest for the release and PR legs, both arches, the declared floors and that no standard Linux leg returns to a runner userland; the assertion follows the shape of the contract test in #2573. The release gate chain contract lists build-linux among the native candidate producers. Refs #2573 #2486 Co-authored-by: lorenzozanee <wyz0707@proton.me> Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Built in the pinned ubuntu:22.04 container, the standard Linux binary requires at most glibc 2.35 and GLIBCXX_3.4.30. Update the comments that said 2.38+: install.sh, the npm and PyPI wrappers, cli.c, the Homebrew formula, smoke-test.sh, _smoke.yml, check-glibc-compat.sh and the local glibc-floor venue. The installers keep choosing the -portable archive, since a 2.35 floor still excludes Debian 11, RHEL 8 and Ubuntu 20.04. The local smoke-glibc-floor lane keeps its assertion and now says what it proves: a binary built in the noble test image refusing to start there shows the venue is older than the build image. Refs #2486 Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
The glibc-floor job builds the production binary in the pinned ubuntu:22.04 release container and holds it to the release glibc floor (2.35 / GLIBCXX 3.4.30). It ran report-only; it now gates like every other lane. - select-lanes.sh: new lane glibc-floor, part of the full set and selected whenever smoke-ubuntu is (a FOLLOWS rule), i.e. whenever the production Linux binary can change. Docs-only and tests-only changes do not select it. - pr.yml: the glibc-floor job runs when its own lane is selected, and ci-ok needs it. - require-all-green.sh: maps the glibc-floor job to its lane, so a selected glibc-floor that fails, is skipped or cancelled, or never runs fails ci-ok; when it is not selected it may skip. - Contracts: the selector table and the FULL set carry the lane, and every selector case asserts glibc-floor is selected exactly when smoke-ubuntu is; the lane-gate contract adds green, failed, skipped, cancelled, missing and unselected cases; the lane-wiring contract requires the job to serve the lane, ci-ok to need it, and no continue-on-error on it. - scripts/ci/README.md notes the rule. Refs #2486 Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
… smoke The composition gate asserts the glibc 2.35 floor on the built bytes, but nothing ran those bytes on a 22.04 userland before a release. - scripts/ci/check-glibc-floor-runtime.sh (new canonical entry): runs a linux binary in a plain container of a digest-pinned image with nothing installed and requires --version, cli index_repository on a tiny C and Python fixture (status indexed) and cli list_projects (lists the fixture) to succeed. A binary built on a newer userland fails at --version, with the loader errors in the log. - _smoke.yml smoke-unix: a gating step runs it on every Linux leg (amd64 and arm64, each on its own artifact) with the same ubuntu:22.04 digest that build-linux builds in. - test_venue_parity_contract.sh: the step must exist once on smoke-unix, run for every Linux leg, gate (no continue-on-error) and pin the same digest as build-linux and the PR glibc-floor leg; the core smoke-unix matrix must hold linux amd64 and arm64; the new entry joins the --help and strict-flag interface probes. - scripts/ci/README.md lists the new entry. Refs #2486 Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
|
Two follow-up commits on top of the three reviewed ones, both from the maintainer decisions of 2026-11-02. |
What does this PR do?
The standard (dynamic) Linux release binaries were built on the hosted runners' own Ubuntu 24.04 userland, so they required
GLIBC_2.38andGLIBCXX_3.4.32and refused to start on Ubuntu 22.04 (#2486). This PR moves both of them into a pinned Ubuntu 22.04 build container and checks the resulting glibc floor on every built binary._build.yml): a newbuild-linuxjob builds linux-amd64 and linux-arm64 insideubuntu:22.04@sha256:076e7bf82319619f28dd0aa9c02ab34b055ad37100395b4f49a2ffba04fb5e11(the multi-arch index digest, one pin for both arches). The distro toolchain (GCC 11.4 against glibc 2.35 headers) is what sets the floor; the hosted runner only hosts the container, so a runner image retirement no longer moves it.build-unixkeeps the two macOS legs, the portable (static) legs are unchanged, andselect-packageneeds the new job. Artifact names are unchanged.scripts/ci/check-binary-composition.sh, A6-glibc-floor): when a venue declaresCBM_GLIBC_FLOOR(and optionallyCBM_GLIBCXX_FLOOR), noGLIBC_*/GLIBCXX_*version a dynamic ELF requires may be newer. Versions compare numerically andGLIBC_ABI_DT_RELRcounts as 2.36 (the release that added it); other unnumbered names (GLIBC_PRIVATE) and a dynamic binary with noGLIBC_*requirement fail closed.build-linuxdeclares 2.35 / 3.4.30 (what the pinned image's libc and libstdc++ provide), so all three release candidates are checked inprepare-release-candidates.sh. Undeclared, A6 reports n/a, so local builds, the local artifact smoke and the contract stubs on newer distros are unaffected.pr.yml): a report-onlyglibc-floorleg builds the production binary in the same pinned image and runs the composition gate with the same floor whenever the selection holdssmoke-ubuntu. It is not in ci-ok'sneedsand has no lane, so it never blocks or passes a merge (whether it should gate is a separate decision).test_venue_parity_contract.shasserts the digest pin, one shared digest for the release and PR legs, both arches, the declared floors, and that no standard Linux leg returns to a runner userland (this assertion follows the shape of the contract test in ci: build Linux amd64 release on Ubuntu 22.04 #2573).test_release_gate_chain_contract.shlistsbuild-linuxamong the native candidate producers.test_release_candidate_derivation_contract.shproves A6 on the stripped stub candidate: RED at floor 1.0 and GREEN at 999.0 on glibc Linux, n/a on Mach-O / PE, fail-closed on a non-glibc Linux, exit 2 on a malformed floor.Refs #2573 #2486
Evidence (local: Colima on Apple silicon, arm64 native, amd64 under Rosetta)
Same commit, headless production build (
scripts/build.sh CC=gcc CXX=g++; the shipped composition differs only by swappingsrc/ui/embedded_stub.cfor the generated asset data), the exact apt line of the new job:Two arm64 builds of the branch in the pinned image were byte-identical (sha256 78e7b712...). The 22.04-built binaries start (
--version) and index a small fixture (cli index_repository,list_projects,search_graph) in a plainubuntu:22.04container with nothing installed, linking the image's own libc 2.35, libstdc++ 12.3 and zlib.What only hosted CI and a release dry run can show
ubuntu-latestandubuntu-24.04-armrunners (checkout, setup-node, attest and upload running inside the container);npm ci+ embed) inside the container, and the frontend integrity scan there;glibc-floorleg on a hosted runner (it runs on this PR, since a workflow change selects every lane);Thank you to @lorenzozanee for #2573: the diagnosis there and its workflow contract test shaped this change, and the contract assertion here follows its shape. Thank you to @shockme for the precise loader errors and the Ubuntu 22.04 lead in #2486.
Checklist
git commit -s) -- required, CI rejectsunsigned commits (DCO, see CONTRIBUTING.md)
make -f Makefile.cbm test) -- the affected contract steps were run locally; the full suite runs in PR CImake -f Makefile.cbm lint-ci) -- the only C change is a comment (clang-format clean on the hunk)Follow-up: the PR leg gates, and the release smoke runs the binary on plain 22.04 (maintainer decisions, 2026-11-02)
Two commits on top of the three above. They supersede the last sentence of item 3: the PR
glibc-floorleg is no longer report-only.glibc-flooris a gating PR lane (ci: make the glibc-floor PR leg a gating lane).scripts/ci/select-lanes.shselectsglibc-floorexactly when it selectssmoke-ubuntu, i.e. for every change that can alter the production Linux binary and never for a docs-only or tests-only change.ci-okneeds the job, andrequire-all-green.shmaps it to its lane: a selectedglibc-floorthat fails, is skipped or cancelled, or never runs failsci-ok; an unselected one may skip like every other lane. The lane-selector table, the lane-gate contract (green, failed, skipped, cancelled, missing and unselected cases) and the lane-wiring contract (the job serves the lane,ci-okneeds it, nocontinue-on-error) pin this.ci: run the dynamic Linux binary on plain ubuntu:22.04 in the release smoke). New canonical entryscripts/ci/check-glibc-floor-runtime.sh: it runs a binary in a plain container of a digest-pinned image with nothing installed and requires--version,cli index_repositoryon a tiny C + Python fixture ("status":"indexed") andcli list_projects(lists the fixture) to succeed. In_smoke.ymlevery smoke-unix Linux leg (amd64 and arm64, each on its own shipped artifact) runs it as a gating step with the sameubuntu:22.04@sha256:076e7bf8...digest thatbuild-linuxbuilds in.test_venue_parity_contract.shasserts the step exists once, runs for every Linux leg, has nocontinue-on-error, and pins the same digest asbuild-linuxand the PR leg; the new entry also joins the--helpand strict-flag interface probes.Evidence (local, Colima on Apple silicon; the step's own command line, run with the step's env):
ubuntu:22.04@sha256:076e7bf8...--platform linux/amd64because the host is arm64--version:GLIBC_2.38' not found,GLIBCXX_3.4.32' not found(the #2486 loader errors)Every new contract assertion was checked RED-on-revert: removing the selector rule, the lane from the universe, the
ci-okjob mapping, theci-okneed, the job's own lane condition, or addingcontinue-on-errorturns the lane contracts red; removing the runtime step, changing its digest, using a tag, limiting it to amd64, or making itcontinue-on-errorturns the venue-parity contract red.Only the release dry run shows the runtime step on the hosted
ubuntu-latestandubuntu-24.04-armrunners (Docker there pulls the pinned index digest for the runner's own architecture, as the portable legs' old-glibc check already does).