Repository navigation
Conversation
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: true |
Reviewer's GuideThe PR introduces a blocking, architecture-aware installation test for every Linux .deb and .rpm before publication, using digest-pinned local Podman images and a repository-owned verifier to exercise startup, manual-page availability, and declared dependencies. It wires the gate to the matching shared-actions revision, covers release dry runs, documents its behavior and arm64 limitation, and adds comprehensive workflow and sandbox contract tests. Sequence diagram for Linux package validation before publicationsequenceDiagram
participant Workflow
participant Podman
participant Sandbox
participant Package
participant Verifier
participant Upload
Workflow->>Podman: podman build --arch
Podman-->>Workflow: Local validation images
Workflow->>Sandbox: validate-linux-packages
Sandbox->>Package: Install .deb or .rpm
Sandbox->>Verifier: verify-installed-package netsuke
Verifier->>Package: netsuke --version
Package-->>Verifier: Startup result
Verifier->>Package: man -w netsuke
Package-->>Verifier: Manual page path
Verifier->>Package: dpkg-query -W or rpm -q --requires
Package-->>Verifier: Declared dependencies
alt Validation passes
Verifier-->>Workflow: Success
Workflow->>Upload: Publish packages
else Validation fails
Verifier-->>Workflow: Failure
Workflow-->>Upload: Do not publish
end
Flow diagram for architecture-aware Linux package validationflowchart TD
Start[Built .deb and .rpm] --> Target[Determine package architecture]
Target --> Host{Target matches runner?}
Host -- No --> Emulation[Install qemu-user-static and enable binfmt]
Host -- Yes --> Image
Emulation --> Image[Build digest-pinned local Podman images]
Image --> Install[Install packages in matching sandbox]
Install --> Verify[Run verify-installed-package]
Verify --> Result{All checks pass?}
Result -- Yes --> Publish[Publish artifacts]
Result -- No --> Stop[Fail lane and stop publication]
File-Level Changes
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
d3b1e5a to
945810c
Compare
e1a025c to
f66254f
Compare
b72aa0f to
3664d10
Compare
Provide digest-pinned Debian and Rocky sandboxes plus a post-install verifier for the release packaging gate.
The Linux packaging lane built .deb and .rpm archives, inspected their metadata, and uploaded them. Metadata cannot prove a package works: a dependency can go undeclared, a manual page can be left out of the archive, or the binary can fail to start under the target glibc. Any of those reached users because nothing ever installed what the lane produced. Run `validate-linux-packages` between packaging and upload, pinned to the same shared-actions revision as `linux-packages`, so the validator that judges the package is the revision that built it. The gate is `if: inputs.platform == 'linux'` only: it runs in the release dry run as well as on tags, and `continue-on-error` is deliberately absent so a broken package stops the lane rather than annotating it. Install each package in an image built for the package's own architecture, from a digest-pinned base that can actually run it. The previously tagged images could not: Debian bookworm (glibc 2.36) and Rocky Linux 9 (2.34) are below the GLIBC_2.39 floor the x86_64 release requires, so move to Debian trixie and Rocky Linux 10. Both also exclude documentation by default, which would make `man -w` fail for every package; strip dpkg's `path-exclude` and RPM's `tsflags=nodocs` and `%_excludedocs` so the installed manual page is really present. The two images differ in one deliberate way. Debian installs `ninja-build` because `dpkg -i` resolves dependencies and a package that declares a dependency it cannot get would fail the install for an unrelated reason. The RPM image withholds it because `rpm -i --nodeps` skips resolution, so a declared dependency that is absent is the only way to prove the declaration exists. Verification is the repository's own POSIX script: `--version` runs, `man -w` returns a non-empty path, and `ninja-build` appears in the declared dependencies via `dpkg-query` or `rpm -q --requires`. A host with neither tool fails rather than passing vacuously. The shared action's own Polythene revision predates local-image support and would treat the `localhost/` tag as a registry host, so pass a repository-owned PEP 723 wrapper pinned at a Polythene commit that handles local Podman images. It is named `polythene_cli.py` because a sibling `polythene.py` would shadow the package it imports on `sys.path`. Arm64 packages are metadata-validated but not installed on x64 runners: the action skips the sandbox for a foreign architecture. The developers' guide records that limitation rather than implying coverage the lane does not have. Cover the gate with two contract suites, kept apart to stay inside the 400-line module limit, and record the reasoning in the developers' guide. Co-Authored-By: Claude Code <noreply@anthropic.com>
The shared validate-linux-packages action skips both its sandbox install and its architecture check when the package architecture differs from the runner's. Both Linux lanes run on x64 runners, so the arm64 packages were checked against their metadata and file lists but never installed: the lane could pass without either package's binary or verifier running. Install them anyway. A new `cross-arch` output on the sandbox step makes the distinction explicit, and an "Install packages under emulation" step runs install-emulated-package.sh for each .deb and .rpm in the image built for the package's architecture. The script installs with `dpkg -i`, or with `rpm -i --nodeps --nosignature` because the RPM image deliberately withholds ninja-build, then runs the same verifier the action would have run. The QEMU binfmt handlers the emulated container needs are already registered by the sandbox step before the foreign-arch images are built. Cover the script's per-format and failure paths in a third contract module, and record the change in the developers' guide. Co-Authored-By: Claude Code <noreply@anthropic.com>
The installer is checked in executable, but invoking it through the interpreter its shebang names does not depend on the executable bit surviving a checkout or an archive. Pass the script to `sh` explicitly and hold the contract to that form. Co-Authored-By: Claude Code <noreply@anthropic.com>
`_isolated_path` created a symlink and returned a value, mixing a command with a query; a second call for the same directory would raise rather than return a path. Move the link into `_link_grep` and build PATH from the stub directory directly, so the helper that reads has no side effect. Co-Authored-By: Claude Code <noreply@anthropic.com>
The Containerfile contract test scanned the raw file text, so the Debian image's comment naming `ninja-build` could satisfy the required-package check even if the install instruction were removed, and a comment could also trip the RPM image's forbidden-package check. Both directions weaken the gate: the check exists to prove what the image actually installs. Filter comment lines before the assertions so only instructions count. Co-Authored-By: Claude Code <noreply@anthropic.com>
`ty` rejects `in` against an `object`, and the workflow loader types step values as `object` because YAML is untyped. The membership tests were therefore both unchecked and untypeable: a renamed `bin-name` key or a restructured `needs` list would have surfaced only as a test failure with a confusing message. Assert the binary name is a string before searching for it, and read `needs` through `require_list` so a non-list fails with the loader's own diagnostic. Co-Authored-By: Claude Code <noreply@anthropic.com>
Importing a module must not run the CLI. `uv run <path>` executes this script as `__main__`, so the guard leaves the action's behaviour unchanged while making the module safe to import. Co-Authored-By: Claude Code <noreply@anthropic.com>
The Podman stub records the command without running it, so checking that each fragment appears somewhere in the call accepts a swapped order. The script's contract is install *then* verify in one container; a swap would verify against a package that was never installed. Assert the recorded order as well as the presence of both commands. Co-Authored-By: Claude Code <noreply@anthropic.com>
The emulation step failed only when `dist` held no package at all. If packaging produced, say, a `.deb` but no `.rpm`, the loop installed the one format and exited 0. On a cross-architecture lane the shared action skips its sandbox, so this step is the only install those packages ever get and the uninstalled format would be published green. Require one `.deb` and one `.rpm` before building the install list, and hold that with a contract case that withholds each format in turn. Record the requirement in the developers' guide. Co-Authored-By: Claude Code <noreply@anthropic.com>
Both Linux package contract modules recomputed the repository root from `__file__`, duplicating a value `workflow_loading` already exports. Import `REPO_ROOT` from that module instead, so the two cannot drift apart if the layout changes. The shared import makes `pathlib.Path` a typing-only name in both modules, so it moves under `if typ.TYPE_CHECKING:`. The sandbox module is at its 400-line ceiling, so the two comments that trimmed to make room say the same thing in fewer words. Co-Authored-By: Claude Code <noreply@anthropic.com>
The wrapper ran `main()` and discarded whatever it returned. Polythene's entry point raises `SystemExit` today, so nothing was lost, but a future revision that returns a status instead would make the wrapper exit 0 after a failed validation — turning the gate into a no-op. Wrap the call in `raise SystemExit(main())`, which behaves identically while the entry point raises and propagates a returned status otherwise. Hold that in the contract test: the wrapper's required lines are now a table, so the entry point, the PEP 723 metadata, and the exit-status propagation are each asserted, and a later edit cannot drop one silently. Co-Authored-By: Claude Code <noreply@anthropic.com>
Add a Mermaid sequence diagram to the Linux package installation validation section of the developer's guide. It shows the workflow building a validation image, handing the package to the shared action, installing it in the sandbox, running the verifier's three checks, and publishing only on success. The figure carries a "For screen readers" description before the block and a visible caption after it, matching the documentation style guide's diagram convention so assistive technology has a text equivalent of the sequence. Co-Authored-By: Claude Code <noreply@anthropic.com>
The sequence diagram and its caption described only the shared action's sandbox install, while the surrounding prose states that a package whose architecture differs from the runner's is installed and verified by the `Install packages under emulation` step instead. The screen-reader description and the caption now name that path, so the figure no longer reads as the whole story for the arm64 lane. Also replace "install's" with "installation's" in the contract-test paragraph, where the possessive noun form was meant. Co-Authored-By: Claude Code <noreply@anthropic.com>
The gate contract matched source fragments of the workflow step's shell script. That broke on a rename or a reflow while the behaviour stayed correct, and it could not see a mapping bug that kept every fragment intact, such as a wrong image behind an earlier `continue`. Run the step instead, with a recording `sh` first on PATH and a `dist` holding one `.deb` and one `.rpm`. The new case asserts that each package reaches `install-emulated-package.sh` once, with its own image, the package architecture, and the binary name. Swapping the two images in the step fails it with a diagnostic naming the package. The gate test now holds only the wiring its name promises: that the step reads the architecture and both image tags from the steps that produced them. The developer's guide attributes the target-to-architecture mapping to the sandbox suite, which is where that test lives, and the emulation suite to the image selection it now covers. Co-Authored-By: Claude Code <noreply@anthropic.com>
415810f to
4419cd4
Compare
|
@coderabbitai please assess the implementation in this PR for completeness and correctness against the original issue: |
|
Tip For best results, initiate chat on the files or code changes. Analysis chain
Check against the original issue
Correctness of the installation gateRetain the cross-architecture installation step. It closes a real gap in the pinned shared action. I inspected the shared action at the specified revision. Its The PR compensates for that behaviour:
The workflow places both validation paths before artefact upload. Neither path uses The validation images address the practical prerequisites: compatible glibc versions, manual-page tooling, and removal of documentation exclusions. The RPM installation deliberately skips dependency enforcement, but the verifier separately checks the required Tests and runtime evidenceUse the new contract suites as regression protection for pin equality, ordering, dry-run wiring, verifier failure paths, architecture setup, image selection, and missing package formats. Do not treat the stubbed container calls as proof of real installation. At the latest inspection of Release Dry Run 41301235870:
I inspected the tests but did not execute repository code in the review sandbox. Before merging, verify that the arm64 job completes successfully and that You are interacting with an AI system. |
Summary
Closes #799.
The Linux packaging lane built
.deband.rpmarchives, inspected theirmetadata, and uploaded them. Metadata cannot prove a package works: a
dependency can go undeclared, a manual page can be left out of the archive, or
the binary can fail to start under the target glibc. Any of those reached users
because nothing ever installed what the lane produced.
This change runs
validate-linux-packagesbetween packaging and upload, pinnedto the same shared-actions revision as
linux-packages, so the validator thatjudges the package is the revision that built it.
What the gate does
.deband.rpmin an image built for the package'sown architecture, from a digest-pinned base.
netsuke --versionruns;man -w netsukeresolves to a non-empty path;ninja-buildappears in the declared dependencies, read withdpkg-query -Worrpm -q --requires.if: inputs.platform == 'linux'only, and carry nocontinue-on-error, so abroken package stops the lane rather than annotating it.
Decisions worth reviewing
tagged images could not run the binary: bookworm is glibc 2.36 and Rocky 9 is
2.34, both below the
GLIBC_2.39floor the x86_64 release requires.by default, which would make
man -wfail for every package. The imagesstrip dpkg's
path-excludeand RPM'stsflags=nodocs/%_excludedocs.ninja-buildbecausedpkg -iresolves dependencies; RPM withholds itbecause
rpm -i --nodepsskips resolution, so an absent declared dependencyis the only way to prove the declaration exists.
revision predates local-image support and would treat the
localhost/tag asa registry host. The PEP 723 wrapper pins a commit that handles local Podman
images; it is named
polythene_cli.pybecause a siblingpolythene.pywouldshadow the package it imports.
locally, never pushed, so the packaging job stays
contents: read.Cross-architecture install
The shared action skips both its sandbox install and its architecture check
when the package architecture differs from the runner's, which is the arm64
lane on an x64 runner. A follow-up step installs those packages anyway: it first
requires both a
.deband an.rpmindist, so a packaging run that producedonly one format stops the lane instead of publishing the other having never
run. It then runs
install-emulated-package.shfor each package in thearchitecture-tagged image under the QEMU binfmt handlers the sandbox step
registers, and finishes with the same verifier. The arm64 lane therefore cannot
pass without executing the package.
Validation
All nine deterministic commit gates pass at the head this PR reports:
make check-fmtmake lint(cargo doc, clippy-D warnings, Whitaker, the Python lintcascade, yamllint, actionlint)
make typecheckmake test(nextest plus doctests)make doc-coveragemake test-workflow-contractsmake markdownlintmake nixiemake test-linux-package-metadataThe new contract suites stub Podman and assert the recorded invocations, so
they need neither Podman nor QEMU on the test host. The real install runs in
CI, on both the native and the emulated lane.
The generated
typos.tomlrefresh this branch once stacked on landed first asits own change (#1051), so this diff carries only the package validation work.
References
https://lody.ai/leynos/sessions/${LODY_SESSION_ID}
🤖 Generated with Claude Code
Summary by Sourcery
Gate Linux package publication on successful installation and runtime validation across native and cross-architecture release lanes.
New Features:
.deband.rpmpackages before publication, including binary startup, manual-page, and dependency checks.Bug Fixes:
Enhancements:
CI:
Documentation:
Tests: