Skip to content

refactor(xtask): unify release packaging and install lifecycle E2E - #139

Merged
rominf merged 7 commits into
mainfrom
refactor-xtask-release-lifecycle
Jul 31, 2026
Merged

refactor(xtask): unify release packaging and install lifecycle E2E#139
rominf merged 7 commits into
mainfrom
refactor-xtask-release-lifecycle

Conversation

@rominf

@rominf rominf commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator

Summary

Consolidates the release packaging and install-lifecycle verification onto a single cross-platform Rust harness, replacing four platform-specific shell/PowerShell scripts.

  • cargo xtask package — a new pure-Rust command that builds the release distribution bundle (a .tar.gz on Unix, a .zip on Windows), each with a SHA-256 sidecar and, when a signing key is configured, a detached signature. It packages both the rocm and rocmd binaries and preserves the external artifact contract (bundle layout, checksum syntax, signature presence, installer compatibility) of the scripts it replaces.
  • Shared paths module — extracts the workspace-path resolution (workspace root, active target dir, release binary dir, platform binary name) that had drifted across e2e.rs, demos.rs, and the old packaging scripts into one module with a unit-testable pure core.
  • @lifecycle E2E set — replaces the standalone acceptance-install-upgrade-tui-uninstall scripts with an opt-in scenario set in the cucumber E2E suite. The scenarios drive cargo xtask package, run the real install.sh / install.ps1, and assert the install / upgrade / TUI / uninstall contract: checksum and detached-signature verification (including rejection of bad checksums, bad or missing signatures, and required-signature mode without a public key), first-install PATH setup, isolated-config smoke checks, and clean uninstall. All side effects are rooted under a per-scenario temp dir and mutated OS state is restored on teardown even if a step panics.
  • CI wiring — points the CI, nightly, and release workflows at cargo xtask package and the @lifecycle set instead of the removed scripts, and widens CI change-detection so the heavy job triggers on xtask/ and the lifecycle sources.

Why

Packaging and install-acceptance logic was duplicated across separate Linux (shell) and Windows (PowerShell) scripts that had drifted apart and duplicated path-resolution logic already present in the xtask crate. A single cross-platform harness removes that drift, makes the packaging and lifecycle contract unit- and scenario-testable, and keeps the two platforms behaviorally in step.

Design notes

  • Signing inputs are read from the environment for parity with the scripts they replace, so existing release configuration keeps working.
  • The @lifecycle set is gated behind E2E_INCLUDE_LIFECYCLE / E2E_ONLY_LIFECYCLE so the default cargo xtask e2e run stays fast; CI opts in explicitly.
  • The paths module splits a pure, unit-testable core from filesystem lookups so the resolution logic can be tested without a built workspace.

Test plan

Verified on Linux:

  • cargo test -p xtask — 62 passed, including the new package tests (bundle layout, two-space lowercase-hex checksum syntax, stage-checksum-sign round trip, required-signature-without-key failure) and the paths unit tests.
  • cargo test -p e2e-cucumber --no-run — the lifecycle steps and installer fixture compile cleanly.
  • Run the Linux lifecycle scenarios locally with:
    E2E_INCLUDE_LIFECYCLE=1 E2E_ONLY_LIFECYCLE=1 cargo xtask e2e

Deferred / not yet verified

  • Windows CI checks not verified locally. The Windows lifecycle job (packaging + native-crypto signature verify + user-PATH persistence/restore + loopback-HTTP install + isolated smoke + uninstall) is wired into the workflow but was only exercised on Linux in this branch; it can only be validated on the GitHub Windows runner. This is why the PR is opened as a draft.

Risk

Medium. Touches release/nightly/CI packaging paths and removes the previous acceptance scripts; the archive/layout contract is preserved and covered by unit tests, but the Windows path needs the CI runner to confirm.

Comment thread xtask/src/package.rs Fixed
Comment thread xtask/src/package.rs Fixed
Comment thread xtask/src/package.rs Fixed
@rominf
rominf force-pushed the refactor-xtask-release-lifecycle branch 2 times, most recently from 1cff742 to 6b800a6 Compare July 22, 2026 13:32
@rominf
rominf requested review from fredespi and volen-silo July 22, 2026 15:04
@rominf
rominf marked this pull request as ready for review July 22, 2026 15:07
@rominf
rominf changed the base branch from main to ci-shared-windows-strix-runners July 23, 2026 14:25
@rominf
rominf marked this pull request as draft July 23, 2026 14:25
@rominf
rominf force-pushed the ci-shared-windows-strix-runners branch from bb89ce9 to cf10418 Compare July 24, 2026 12:23
Base automatically changed from ci-shared-windows-strix-runners to main July 27, 2026 15:14
rominf added 7 commits July 27, 2026 15:33
Replace the platform-specific `scripts/package-{linux,windows}-release`
shell/PowerShell scripts with a single `cargo xtask package` command that
builds the release distribution bundle in pure Rust: a `.tar.gz` on Unix
and a `.zip` on Windows, each with a SHA-256 sidecar and, when a signing
key is configured, a detached signature. Signing inputs are read from the
environment for parity with the old scripts, and the external artifact
contract (bundle layout, checksum syntax, signature presence, installer
compatibility) is preserved.

Extract the duplicated workspace-path resolution (workspace root, active
target dir, release binary dir, platform binary name) that had drifted
across `e2e.rs`, `demos.rs`, and the packaging scripts into a shared
`paths` module with a unit-testable pure core, and build `rocmd` alongside
`rocm` so the packaged bundle carries both binaries.

Signed-off-by: Roman Inflianskas <Roman.Inflianskas@amd.com>
Replace the standalone `acceptance-install-upgrade-tui-uninstall`
shell/PowerShell acceptance scripts with an opt-in `@lifecycle` set in the
cucumber-rs E2E suite, so the package + real-installer + install / upgrade /
TUI / uninstall flow is exercised through one cross-platform harness with
per-scenario isolation and teardown.

The scenarios drive `cargo xtask package`, run the actual `install.sh` /
`install.ps1`, and assert the external contract: checksum and detached
signature verification (including rejection of bad checksums, bad or missing
signatures, and required-signature mode without a public key), first-install
PATH setup, isolated-config smoke checks, and clean uninstall. A
`LifecycleState` roots all side effects under the scenario temp dir and
restores mutated OS state (notably the Windows user PATH) on drop, even if a
step panics. The set is gated behind `E2E_INCLUDE_LIFECYCLE` /
`E2E_ONLY_LIFECYCLE` so the default run stays fast.

Signed-off-by: Roman Inflianskas <Roman.Inflianskas@amd.com>
Point the release, nightly, and CI workflows at `cargo xtask package` and
the `@lifecycle` E2E set instead of the removed packaging and acceptance
scripts, widen the CI change-detection paths to trigger the heavy job on
`xtask/` and the lifecycle sources, and update the release-trust and testing
docs to describe the new packaging command and lifecycle suite. Refresh the
dependency manifest and third-party notices for the archive dependencies the
packaging command pulls in, and fix a stale script reference in a rocm-core
doc comment.

Signed-off-by: Roman Inflianskas <Roman.Inflianskas@amd.com>
Resolve the clippy failures in the new lifecycle steps that broke the CI
clippy gate (const fn for the state accessors, map_or_else, writeln! in
place of push_str(format!), and flatten over the Option loop), and drop
the needless format! in the Windows PATH-restore helper that would have
tripped the Windows clippy gate.

Restore three assertions that the shell/PowerShell acceptance scripts had
but the cucumber port lost: the negative isolation check that examine
never reads the real user ~/.rocm, the non-running rocmd binary being
removed after a keep-config Windows uninstall, and that an interactive
chat session does not clobber the default-engine config.

Also surface a failed Windows user-PATH restore on stderr instead of
silently swallowing it (it runs in Drop, so it warns rather than panics),
and de-duplicate the @lifecycle / @requires-os rows in the tags table.

Signed-off-by: Roman Inflianskas <Roman.Inflianskas@amd.com>
The install-lifecycle scenarios invoked `cargo xtask package` and
`cargo xtask keygen` from inside the running test. On Windows that
re-enters cargo, which tries to rebuild `xtask.exe` and fails to replace
it while the harness is still executing that same binary ("Access is
denied", os error 5) — every lifecycle scenario then failed at keygen.

The harness now exports the path of the already-built xtask executable
(its own `current_exe`) as `ROCM_XTASK_BINARY`, and the steps run that
binary directly instead of shelling back through cargo. Running the
existing executable needs no rebuild and takes no lock. A standalone
`cargo test` run (no harness) falls back to `cargo xtask`, where no
xtask process is running so the rebuild is safe.

Signed-off-by: Roman Inflianskas <Roman.Inflianskas@amd.com>
Static analysis flagged the release-packaging and demo tasks for using
paths derived from environment overrides (`ROCM_BIN_DIR`,
`CARGO_TARGET_DIR`, the signing-key path) and CLI arguments (the bundle
output dir and name) in filesystem operations without validation, so a
`..` segment could point the copy/read outside the intended tree.

Validate the path's string form for a `..` traversal segment and rebuild
the path from the checked string before every such filesystem access.
Absolute overrides and target dirs stay valid — only `..` traversal is
refused — so legitimate `CARGO_TARGET_DIR`/`ROCM_BIN_DIR` values keep
working. Adds unit tests covering the rejection for both the copy and
require paths.

Signed-off-by: Roman Inflianskas <Roman.Inflianskas@amd.com>
…erver

The Windows install-lifecycle loopback download server (lifecycle_steps.rs
LoopbackServer, used by the lifecycle-windows-http-install scenario) read
the incoming HTTP request with a single read() call and treated whatever
bytes arrived as the whole request. Nothing in TCP/HTTP guarantees a
request lands in one read(): a client or the OS network stack can split it
across multiple segments/writes. When that happened, the server captured
only a prefix of the request line (e.g. "GET " with no path yet),
defaulted the parsed path to "/", and 404'd a legitimate download.

Reproduced locally (outside this fix) by forcing a raw socket to send
"GET " and the rest of the request line in two separate writes: the
server returned 404 for tests/e2e-cucumber-windows-amd64.zip instead of
200. This plausibly explains a real Windows CI failure on
lifecycle-windows-http-install ("failed to download ... An error occurred
... while sending the request"), since fragmenting a request across
writes is more a property of the client/OS TCP stack (here PowerShell's
Invoke-WebRequest on Windows) than of loopback distance.

Extract the request-head reading and parsing into a small, unit-tested
e2e_cucumber::loopback_http module: read_request_head() loops across as
many read() calls as needed until it sees the header terminator (or EOF,
or a size cap), instead of trusting a single fixed-size read(). Verified
the fix end-to-end against a real TCP loopback socket with the request
split byte-by-byte, in addition to the new unit tests covering split and
malformed requests.

Signed-off-by: Roman Inflianskas <Roman.Inflianskas@amd.com>
@rominf
rominf force-pushed the refactor-xtask-release-lifecycle branch from 0d82a01 to 2831271 Compare July 28, 2026 10:03
@rominf
rominf marked this pull request as ready for review July 29, 2026 06:48
@rominf
rominf requested a review from a team July 29, 2026 09:45

@juhovainio juhovainio left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm!

@rominf
rominf added this pull request to the merge queue Jul 31, 2026
Merged via the queue into main with commit d17fc0c Jul 31, 2026
37 of 38 checks passed
@rominf
rominf deleted the refactor-xtask-release-lifecycle branch July 31, 2026 07:58
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.

3 participants