Skip to content

Install the built .deb and .rpm before publishing them (#799) - #1047

Draft
leynos wants to merge 17 commits into
mainfrom
issue-799-install-the-built-deb-and-rpm-before-publishing-them
Draft

leynos wants to merge 17 commits into
mainfrom
issue-799-install-the-built-deb-and-rpm-before-publishing-them

Conversation

@leynos

@leynos leynos commented Oct 30, 2026 •

Copy link
Copy Markdown
Owner

Summary

Closes #799.

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.

This change runs 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.

What the gate does

  • Installs every built .deb and .rpm in an image built for the package's
    own architecture, from a digest-pinned base.
  • Runs the repository's POSIX verifier inside the sandbox, which checks that:
    • netsuke --version runs;
    • man -w netsuke resolves to a non-empty path;
    • ninja-build appears in the declared dependencies, read with
      dpkg-query -W or rpm -q --requires.
  • Runs in the release dry run as well as on tags. The steps are guarded by
    if: inputs.platform == 'linux' only, and carry no continue-on-error, so a
    broken package stops the lane rather than annotating it.

Decisions worth reviewing

  • Base images moved to Debian trixie and Rocky Linux 10. The previously
    tagged images could not run the binary: bookworm is glibc 2.36 and Rocky 9 is
    2.34, both below the GLIBC_2.39 floor the x86_64 release requires.
  • Documentation exclusions removed. Both distributions exclude manual pages
    by default, which would make man -w fail for every package. The images
    strip dpkg's path-exclude and RPM's tsflags=nodocs / %_excludedocs.
  • The two images differ in one deliberate way. Debian installs
    ninja-build because dpkg -i resolves dependencies; RPM withholds it
    because rpm -i --nodeps skips resolution, so an absent declared dependency
    is the only way to prove the declaration exists.
  • A repository-owned Polythene wrapper. The shared action's Polythene
    revision predates local-image support and would treat the localhost/ tag as
    a registry host. The PEP 723 wrapper pins a commit that handles local Podman
    images; it is named polythene_cli.py because a sibling polythene.py would
    shadow the package it imports.
  • Read-only permissions retained. Both images are built and consumed
    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 .deb and an .rpm in dist, so a packaging run that produced
only one format stops the lane instead of publishing the other having never
run. It then runs install-emulated-package.sh for each package in the
architecture-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-fmt
  • make lint (cargo doc, clippy -D warnings, Whitaker, the Python lint
    cascade, yamllint, actionlint)
  • make typecheck
  • make test (nextest plus doctests)
  • make doc-coverage
  • make test-workflow-contracts
  • make markdownlint
  • make nixie
  • make test-linux-package-metadata

The 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.toml refresh this branch once stacked on landed first as
its own change (#1051), so this diff carries only the package validation work.

References

echo ${LODY_SESSION_ID}

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:

  • Add installation-based validation for Linux .deb and .rpm packages before publication, including binary startup, manual-page, and dependency checks.
  • Add cross-architecture package installation under QEMU so arm64 packages are executed before release.

Bug Fixes:

  • Prevent broken Linux packages from being published when metadata checks pass but installation or runtime validation fails.
  • Ensure prerelease package versions are validated using the normalized version spelling produced by the packaging tool.

Enhancements:

  • Use digest-pinned Debian trixie and Rocky Linux 10 validation images with documentation exclusions removed.
  • Keep validation images local and pin a compatible Polythene wrapper for local-image sandbox execution.

CI:

  • Add Linux packaging workflow steps to prepare architecture-specific validation environments, build validation images, validate packages, and block upload on failure.

Documentation:

  • Document the Linux package installation validation process, architecture handling, local reproduction, and workflow contract coverage.

Tests:

  • Add workflow contract tests covering validation ordering, action revisions, image configuration, verifier behavior, version normalization, and emulated installation.

@coderabbitai

coderabbitai Bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

codescene-access[bot]

This comment was marked as outdated.

@sourcery-ai

sourcery-ai Bot commented Oct 30, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

The 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 publication

sequenceDiagram
    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
Loading

Flow diagram for architecture-aware Linux package validation

flowchart 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]
Loading

File-Level Changes

Change Details Files
Add a blocking Linux package-installation gate before artifact publication.
  • Build digest-pinned Debian trixie and Rocky Linux 10 validation images for the package architecture.
  • Run the shared Linux package validator for both formats after packaging and before upload, including release dry runs.
  • Normalize prerelease versions to the spelling emitted by nfpm.
  • Keep validation failures blocking and retain read-only job permissions.
.github/workflows/build-and-package.yml
Implement architecture-aware sandbox images and installed-package verification.
  • Provide glibc-compatible base images with documentation exclusions removed and man-db installed.
  • Use different dependency setups for Debian and RPM to verify the declared ninja-build dependency without masking omissions.
  • Verify binary startup, manual-page resolution, and package dependency metadata.
  • Pin a Polythene wrapper that supports locally built Podman images.
.github/package-validation/deb.Containerfile
.github/package-validation/rpm.Containerfile
.github/package-validation/verify-installed-package.sh
.github/package-validation/polythene_cli.py
Document the validation gate, its coverage, limitations, and local reproduction procedure.
  • Describe image choices, architecture/emulation behavior, local-image handling, and the arm64-on-x64 limitation.
  • Add Podman reproduction instructions and identify the contract test suites.
docs/developers-guide.md
Add workflow and sandbox contract coverage for the new gate.
  • Assert action pin consistency, ordering before upload, unconditional failure behavior, dry-run wiring, and read-only permissions.
  • Test version normalization, architecture mapping, image pinning and contents, verifier pass/fail paths, Polythene pinning, and RPM dependency inspection.
tests/workflow_contracts/linux_package_validation_gate_test.py
tests/workflow_contracts/linux_package_validation_sandbox_test.py

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@leynos
leynos force-pushed the issue-799-install-the-built-deb-and-rpm-before-publishing-them branch from d3b1e5a to 945810c Compare October 30, 2026 18:40
codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

@leynos
leynos force-pushed the issue-799-install-the-built-deb-and-rpm-before-publishing-them branch from e1a025c to f66254f Compare October 30, 2026 21:14
codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

@leynos
leynos force-pushed the issue-799-install-the-built-deb-and-rpm-before-publishing-them branch from b72aa0f to 3664d10 Compare October 31, 2026 00:31
codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

@leynos
leynos changed the base branch from main to chore/refresh-generated-spelling-ignore October 31, 2026 05:26
Base automatically changed from chore/refresh-generated-spelling-ignore to main November 1, 2026 01:22
leynos and others added 12 commits November 1, 2026 09:42
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>
leynos and others added 5 commits November 1, 2026 09:42
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>
codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

codescene-access[bot]

This comment was marked as outdated.

@leynos
leynos force-pushed the issue-799-install-the-built-deb-and-rpm-before-publishing-them branch from 415810f to 4419cd4 Compare November 1, 2026 11:17
@leynos

leynos commented Nov 1, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai please assess the implementation in this PR for completeness and correctness against the original issue:

## Problem

The release builds `.deb` and `.rpm` packages but never installs them.
shared-actions already provides `validate-linux-packages`, and its own test
application uses it. Netsuke does not, so a broken package would be published
first and discovered afterwards: a bad dependency, a wrong file mode, or a
missing man page. Read at `origin/main` `b1db9e5`.

## Expected

- The Linux packaging lane runs `validate-linux-packages` against each built
  `.deb` and `.rpm`, pinned at the same shared-actions revision as the rest of
  the release.
- The check confirms three things after install:
  - `netsuke --version` runs;
  - the man page resolves through `man -w netsuke`;
  - the `ninja-build` dependency is declared.
- It runs in the release dry run as well as on tags.

@coderabbitai

coderabbitai Bot commented Nov 1, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

Analysis chain

Script executed:

#!/bin/bash
set -euo pipefail
git rev-parse HEAD
gh pr view 1047 --repo leynos/netsuke --json headRefOid,baseRefOid,files,statusCheckRollup
git diff --stat origin/main...HEAD
fd -H 'AGENTS.md|release.*ya?ml|.*package.*|.*polythene.*|.*Dockerfile.*|.*Containerfile.*' . --exclude .git

Length of output: 8514


Script executed:

#!/bin/bash
set -u
fd -H 'AGENTS.md|release.*ya?ml' . --exclude .git
ast-grep outline .github/package-validation tests/workflow_contracts/linux_package_validation_gate_test.py tests/workflow_contracts/linux_package_validation_sandbox_test.py tests/workflow_contracts/linux_package_emulation_test.py --items all
for f in .github/package-validation/* .github/workflows/build-and-package.yml .github/workflows/release-dry-run.yml; do echo "=== $f"; nl -ba "$f"; done
echo '=== RELEASE CALLERS'
rg -n -C 5 'build-and-package|dry-run|build-linux|shared-actions/' .github/workflows/release.yml
echo '=== PR DIFF'
gh pr diff 1047 --repo leynos/netsuke -- .github/workflows/build-and-package.yml 2>/dev/null || gh api repos/leynos/netsuke/pulls/1047/files --paginate --jq '.[] | select(.filename == ".github/workflows/build-and-package.yml" or .filename == "docs/developers-guide.md") | {filename,patch}'

Length of output: 77656


Script executed:

#!/bin/bash
set -u
cat AGENTS.md
for f in tests/workflow_contracts/linux_package_validation_gate_test.py tests/workflow_contracts/linux_package_validation_sandbox_test.py tests/workflow_contracts/linux_package_emulation_test.py; do echo "=== $f"; nl -ba "$f"; done
echo '=== CURRENT LINUX JOBS'
gh api repos/leynos/netsuke/actions/runs/41301235870/jobs --paginate --jq '.jobs[] | select(.name | contains("build-linux")) | {id,name,status,conclusion,steps}'
echo '=== PINNED VALIDATION ACTION'
gh api repos/leynos/shared-actions/contents/.github/actions/validate-linux-packages/action.yml?ref=7d751eded578e48dacf43ab173d5820e6437c474 --jq .content | base64 -d
echo '=== PINNED ACTION TREE'
gh api repos/leynos/shared-actions/git/trees/7d751eded578e48dacf43ab173d5820e6437c474?recursive=1 --jq '.tree[] | select(.path | test("validate-linux-packages|linux-packages|polythene")) | .path'

Length of output: 104238


Script executed:

#!/bin/bash
set -eu
tmp=$(mktemp -d)
for name in validate_packages validate_locators validate_polythene validate_commands validate_architecture; do
 gh api "repos/leynos/shared-actions/contents/.github/actions/validate-linux-packages/scripts/$name.py?ref=7d751eded578e48dacf43ab173d5820e6437c474" --jq .content | base64 -d > "$tmp/$name.py"
 ast-grep outline "$tmp/$name.py" --items all
 echo "=== $name"
 nl -ba "$tmp/$name.py" | head -400
done
echo '=== RUN HEAD AND LINUX STATUS'
gh api repos/leynos/netsuke/actions/runs/41301235870 --jq '{head_sha,status,conclusion}'
gh api repos/leynos/netsuke/actions/runs/41301235870/jobs --paginate --jq '.jobs[] | select(.name | contains("build-linux")) | {id,name,status,conclusion,steps: [.steps[] | select(.name | test("validation|Validate Linux packages|emulation|Upload Linux"))]}'
echo '=== NATIVE INSTALL EVIDENCE'
gh api repos/leynos/netsuke/actions/jobs/124832471275/logs 2>/dev/null | grep -E -C 2 'PASS:|FAIL:|Skipping|Installing|ninja-build|netsuke.*(~beta|\.deb|\.rpm)' | tail -100 || true
echo '=== ARM64 INSTALL EVIDENCE IF COMPLETE'
gh api repos/leynos/netsuke/actions/jobs/124832471188/logs 2>/dev/null | grep -E -C 2 'PASS:|FAIL:|Skipping|Installing|ninja-build|netsuke.*(~beta|\.deb|\.rpm)' | tail -100 || true

Length of output: 46745


@leynos Treat the implementation as complete against #799 at the code level, with arm64 runtime verification still pending. I found no confirmed correctness defect in the reviewed changes at commit 4419cd4.

Check against the original issue

Requirement Assessment Evidence
Run validate-linux-packages against .deb and .rpm packages Met .github/workflows/build-and-package.yml, Line 559–578, selects both formats and reads the packaging output directory, dist.
Use the same shared-actions revision as packaging Met linux-packages and validate-linux-packages both use 7d751eded578e48dacf43ab173d5820e6437c474, at Line 404 and Line 561.
Run netsuke --version after installation Met .github/package-validation/verify-installed-package.sh, Line 12–16, fails if the installed command fails.
Resolve the manual page with man -w netsuke Met The verifier, Line 18–26, requires both a successful command and a non-empty path.
Confirm that the package declares ninja-build Met The verifier, Line 28–47, reads the installed package’s Debian or RPM dependency metadata. An installed copy of ninja-build cannot substitute for the declaration.
Run on tags and in the release dry run Met The validation steps depend on the Linux platform, not publication mode. release-dry-run.yml calls the same release workflow with dry-run: true.

Correctness of the installation gate

Retain 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 _validate_package() returns before installation when the package architecture is unsupported on the host. It also skips its architecture validator on that path. Calling the action alone would therefore leave the arm64 installation requirement unmet.

The PR compensates for that behaviour:

  • Build each validation image for the target architecture.
  • Register QEMU handlers when the target differs from the runner.
  • Require both package formats before emulated installation.
  • Install every discovered .deb and .rpm in its corresponding image.
  • Run the same verifier after installation in the same container.

The workflow places both validation paths before artefact upload. Neither path uses continue-on-error. The release publication job also requires a successful Linux build matrix.

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 ninja-build declaration. That meets the original issue; it does not prove that every possible RPM dependency can be resolved.

Tests and runtime evidence

Use 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:

  • amd64: The job completed successfully. Validate Linux packages passed before Upload Linux artefacts.
  • arm64: The job was still building the RPM validation image. Package validation and emulated installation had not started.

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 Install packages under emulation passes rather than skips. Until then, treat cross-architecture runtime verification as inconclusive, not as a confirmed defect. If the job fails, inspect its installation output and rerun after addressing the cause.

You are interacting with an AI system.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Install the built .deb and .rpm before publishing them

1 participant