Skip to content

fix(install): resolve driver repo version and package release in plans - #307

Merged
r0x0r merged 2 commits into
mainfrom
fix/driver-dry-run-repo-version-placeholder
Aug 27, 2026
Merged

fix(install): resolve driver repo version and package release in plans#307
r0x0r merged 2 commits into
mainfrom
fix/driver-dry-run-repo-version-placeholder

Conversation

@r0x0r

@r0x0r r0x0r commented Aug 24, 2026

Copy link
Copy Markdown
Collaborator

Summary

rocm install driver --dry-run printed an unexpanded shell-style placeholder on
the repo_version: line, and the same unresolved template would have been
written into the apt sources list on Debian/Ubuntu:

repo_version: ${ROCM_CLI_AMDGPU_VERSION:-7.2.4}

The driver install plan stored the repo version and package release as raw shell
parameter-expansion templates (${ROCM_CLI_AMDGPU_VERSION:-7.2.4} and
${ROCM_CLI_AMDGPU_PACKAGE_RELEASE:-70204}). Two problems followed:

  1. Cosmetic leak — the human-readable plan summary printed the version
    template verbatim on its repo_version: line, showing an unexpanded shell
    placeholder in user-facing dry-run output.
  2. Install-breaking bug (a real behavior change to fix) — the Debian/Ubuntu
    apt source line wrapped the template in single quotes, so the shell never
    expanded it and the literal placeholder would have been written into
    /etc/apt/sources.list.d/amdgpu.list.

Fix

Resolve both templates once, at plan-build time in build_driver_install_plan,
to their effective values (the env var when set and non-empty — matching shell
:- default semantics — otherwise the documented default) and bake the concrete
values into every rendered URL, repo path, and command. The apt sources-list
line now interpolates in Rust before the string is embedded in the single-quoted
shell literal, so it no longer depends on an expansion that could never happen.

The resolver is generalized to resolve_shell_default_template and hardened to
pass bare ${VAR} and nested-default shapes through unchanged rather than
emitting a partially rewritten value.

Behavioral note

This is not a display-only change. A user who previews the plan and later
copy-pastes the printed commands into a different shell now gets the version
baked in from the previewing process's environment rather than resolved by the
pasting shell. That is more predictable than before, and it is what the apt fix
requires, but it is a genuine behavior change to the emitted commands.

Environment variables

The effective version/release are controlled by ROCM_CLI_AMDGPU_VERSION and
ROCM_CLI_AMDGPU_PACKAGE_RELEASE; both fall back to the documented defaults when
unset or empty.

Testing

  • Unit tests for the resolver: default, env override, empty-as-unset,
    non-template pass-through, bare-var and nested-default pass-through.
  • Every distro plan test updated to assert the concrete resolved values.
  • A render-level regression test asserting the dry-run summary shows the
    resolved version rather than the raw placeholder.
  • End-to-end scenario @id:install-driver-dry-run-resolves-repo-version
    (tests/e2e-cucumber/features/examine.feature, tagged @requires-os:linux).
    install driver --dry-run short-circuits before any mutation and prints
    repo_version: unconditionally, so the scenario runs on the no-GPU mock lane
    and exits 0 on any Linux host. The assertion is discriminating: the old
    ${ROCM_CLI_AMDGPU_VERSION:-7.2.4} output fails both the !contains("${")
    and the leading-ascii-digit checks.
  • Tests that read these process env vars serialize on a shared lock and
    save/restore prior values so they stay deterministic under edition-2024 env
    semantics.

Gates

  • cargo fmt --all -- --check — clean
  • cargo clippy --workspace --all-targets -- -D warnings — clean
  • cargo test -p rocm --bin rocm driver — 26 passed
  • cargo xtask e2e -- -n "The driver install dry-run shows the effective repo version" — exit 0
  • diff leak scan — clean

@r0x0r
r0x0r requested a review from a team as a code owner August 24, 2026 10:57
@r0x0r
r0x0r requested a review from juhovainio August 24, 2026 10:57
@volen-silo

Copy link
Copy Markdown
Collaborator

Review: changes requested

Scope: 1 file, +82/-1 — resolve_repo_version helper plus its use on the repo_version: line of render_driver_install_plan, and 5 new tests. Clippy (cargo clippy -p rocm --all-targets -- -D warnings) is clean, cargo test -p rocm --bin rocm driver_plan passes (20), and the new regression test genuinely fails before the fix and passes after — verified locally.

The helper itself is correct for the input it actually receives. The blocking items are about project conventions and the layer the fix sits at.


Blocking

1. User-observable output change with no scenario and no justification (AGENTS.md §3, checklist §13 item 5)

AGENTS.md §3 is explicit: if a change alters what a user of the CLI can observe — command output included — the behavior must be covered by a Gherkin scenario in tests/e2e-cucumber/features/, and "a unit test asserting the internal helper does NOT discharge this". If a scenario already covers it, the PR text must name the @id:; if the change is purely internal, the PR text must say why no scenario is needed.

This PR changes rocm install driver --dry-run stdout. I grepped tests/e2e-cucumber/features/ — nothing covers install driver or its dry-run output (the only dry-run hit is @id:fix-dry-run-changes-nothing in diagnose.feature, which is rocm diagnose fix, unrelated). The PR body cites only the Rust tests and doesn't address the requirement either way.

Minimum to unblock: add a scenario covering the dry-run summary line, or state in the PR body why one isn't warranted here.

2. New tests mutate process-global env without the lock this repo already established for exactly this

apps/rocm/src/main.rs:24039 and :24055 call std::env::set_var/remove_var under bare unsafe with no serialization. The justifying comments — "A made-up variable name that nothing else sets keeps this race-free" (:24034) and "Uses a unique per-test variable name so it cannot race" (:24041-24042) — address a name collision, which was never the hazard. set_var became unsafe in edition 2024 because concurrent env mutation races with any concurrent env::var read anywhere in the process, regardless of variable name, and cargo test runs these functions multi-threaded in one binary. So the comments assert a safety property the code doesn't have, which is worse than no comment.

Concretely reachable: the new driver_plan_dry_run_repo_version_line_is_resolved (:24069) calls resolve_repo_versionenv::var, and can run concurrently with the two mutating tests in the same binary.

This codebase already solved this twice, and neither pattern was used:

  • ScopedEnvVar (RAII save/restore) at apps/rocm/src/main.rs:16579-16605, serialized via BUILTIN_ENGINE_ENV_LOCK in with_scoped_builtin_engine_env
  • PROCESS_ENV_TEST_LOCK: Mutex<()> at apps/rocm/src/therock.rs:3571, held at :4060, :4316, :4367, :4420, with the comment "env is shared across all test threads"

Fix is small: add a module-level mutex in the main.rs test module (or reuse ScopedEnvVar), hold it in all three tests that touch env — including the render-level regression test — and correct the misleading comments.

2b. Related, same fix: driver_plan_dry_run_repo_version_line_is_resolved (:24069) asserts repo_version: 7.2.4 while reading the real ROCM_CLI_AMDGPU_VERSION. It fails for any developer or runner that legitimately exports that override. Save/clear/restore it under the same guard.

3. Resolving at render time is the wrong layer, and on apt hosts it makes the summary less truthful, not more

render_driver_install_plan receives a &DriverInstallPlan whose repo_version_expr is the single source of truth for the render. Reading ambient process env inside the renderer (main.rs:3472) means the same plan can render differently depending on when it's rendered. build_driver_install_plan (main.rs:2903) already owns that expression and is the natural place to resolve it.

That's a style point on its own. What makes it blocking is what it collides with:

The apt/Debian command at apps/rocm/src/main.rs:3162-3165 embeds the template inside a single-quoted string:

printf '%s\n' 'deb [...] https://repo.radeon.com/graphics/${ROCM_CLI_AMDGPU_VERSION:-7.2.4}/ubuntu <codename> main' | sudo tee /etc/apt/sources.list.d/amdgpu.list >/dev/null

POSIX single quotes suppress all expansion. Verified empirically — the literal ${ROCM_CLI_AMDGPU_VERSION:-7.2.4} is what printf emits, so that's what lands in the sources list. The dnf/zypper paths embed the same template unquoted (sudo dnf install -y <url>), so those do expand correctly; apt is the odd one out.

This is pre-existing, not introduced here — but it means that for Ubuntu 22.04/24.04, Debian 12/13, and ID_LIKE-matched derivatives (build_driver_install_plan, main.rs:2941-2966, likely the largest user segment), the new repo_version: 7.2.4 line now asserts an effective version that the executed command never uses. Before this PR the raw placeholder on the summary line at least mirrored what actually got written; now the summary and the command disagree.

Two acceptable resolutions, both fine by me:

  • Preferred: resolve once in build_driver_install_plan and interpolate the concrete value into the summary and all three command builders (main.rs:3163, amdgpu_install_rpm_url :3427, amdgpu_install_sles_rpm_url :3433), dropping shell-template expansion entirely. This fixes the apt bug in the same change. Cost is ~12 existing tests that assert the raw template verbatim — mechanical.
  • Or: keep this PR display-only, but open an issue for the apt single-quoting bug and reference it here, so the mismatch is tracked rather than papered over.

Worth stating plainly either way: the apt driver install path looks broken today independent of any env override, because a URL containing a literal ${...} goes into /etc/apt/sources.list.d/amdgpu.list and apt-get update will choke on it.


Non-blocking

  • Helper name is narrower than its behavior. resolve_repo_version (main.rs:3465) is a generic ${VAR:-default} resolver per its own doc comment. A second identical template (${ROCM_CLI_AMDGPU_PACKAGE_RELEASE:-70204}) exists in the same plan and isn't routed through it. Something like resolve_shell_default_template invites the reuse; or the question goes away entirely if resolution moves to the build layer per blocking item 3.
  • Nested defaults degrade badly rather than falling back. split_once(":-") matches the first occurrence, so ${A:-${B:-x}} with A unset returns the still-unresolved literal ${B:-x} instead of returning the input unchanged. ${VAR-default}, ${VAR:=x}, and bare ${VAR} all fall through safely. Not reachable from the one call site today; a guard (bail if the default contains ${) or a note would keep it that way.
  • One line resolved, its neighbours not. execution_commands: still shows raw ${ROCM_CLI_AMDGPU_VERSION:-...} and ${ROCM_CLI_AMDGPU_PACKAGE_RELEASE:-70204} a few lines below the now-resolved repo_version:. Defensible — that section is literal shell source — but the reader sees both forms in one output. If display-only is the chosen direction, repo_version: 7.2.4 (${ROCM_CLI_AMDGPU_VERSION:-7.2.4}) would be honest about both.
  • Both override env vars are undocumented. ROCM_CLI_AMDGPU_VERSION and ROCM_CLI_AMDGPU_PACKAGE_RELEASE appear nowhere in docs/, README.md, or skills/. Now that the dry-run summary shows a concrete resolved version, a user is likely to ask how to change it.
  • No test for the malformed/embedded-template shapes. No coverage for ${VAR} (no :-) or a template embedded in a larger string. Both are unreachable from the current call site, so low value — mentioning only because the helper is written to be general.

Tradeoff worth noting

Fixing display only keeps this PR cosmetic and low-risk, at the cost of leaving the display/execution divergence in place for apt hosts. Resolving eagerly in Rust turns it into a behavior change (bigger blast radius, ~12 test updates) but closes the gap. Either is a reasonable call — it just shouldn't be made implicitly.


Positive signals

  • Root cause in the PR body and commit message is precise and correctly identifies why the template exists rather than just deleting it.
  • The regression test is at the render level, not just the helper — it asserts both the presence of the resolved value and the absence of the raw placeholder, so it can't pass vacuously.
  • Empty-string-as-unset is handled, correctly matching :- rather than - semantics.
  • Scoped to one logical change, DCO sign-off present, no AI footers.

@r0x0r
r0x0r force-pushed the fix/driver-dry-run-repo-version-placeholder branch from 9d7d210 to 8397c1d Compare August 27, 2026 09:03
@r0x0r

r0x0r commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator Author

Thanks for the thorough review, @volen-silo. I went with Option A and pushed a rebased, amended commit. Summary of what changed:

Blocking 3 — render layer was the wrong place + the apt single-quote bug.
Resolution now happens once at plan-build time in build_driver_install_plan, not at render time. Both the version and the package release are resolved there, and the concrete values are baked into every rendered URL, repo path, and shell command. This also fixes the Debian/Ubuntu apt source line, which wrapped the template in single quotes so the shell never expanded it — the literal ${ROCM_CLI_AMDGPU_VERSION:-7.2.4} would have been written into /etc/apt/sources.list.d/amdgpu.list. The repo_version: summary line now just prints the already-resolved plan.repo_version. I updated every distro plan test to assert the concrete values.

Blocking 2 / 2b — unsafe env mutation + regression test reading the real env var.
Added a ScopedTestEnv RAII helper in the test module: it holds one process-wide lock for the duration of any test that reads or mutates these env vars, and saves/restores prior values on drop. Every driver_plan_* test and the resolver tests now take the guard, and the version-asserting ones clear ROCM_CLI_AMDGPU_VERSION/ROCM_CLI_AMDGPU_PACKAGE_RELEASE so a value exported in the dev's or runner's shell can't leak in. The doc comment explains the real hazard (a mutation racing a concurrent env::var read anywhere in the process, not a name collision).

Blocking 1 — no scenario for the dry-run stdout change.
Added @id:install-driver-dry-run-resolves-repo-version in examine.feature (tagged @requires-os:linux) with steps in examine_steps.rs. install driver --dry-run renders the plan and returns before touching the system, so it runs on the no-GPU mock lane; the scenario asserts the repo_version: line is a concrete version with no ${ placeholder.

Non-blocking, also addressed.

  • Helper renamed resolve_repo_versionresolve_shell_default_template since it's a general ${VAR:-default} resolver, not version-specific.
  • Hardened it to pass bare ${VAR} and nested-default (${A:-${B:-x}}) shapes through unchanged instead of emitting a partially rewritten value, with tests for both.
  • Resolved package_release in the same place so the one-line-resolved neighbours are consistent.

Verification (local, macOS): cargo test -p rocm --bin rocm driver_plan (20 pass), ... resolve_shell (6 pass), cargo test -p e2e-cucumber --no-run compiles. Clippy's authoritative gate is Linux CI — locally it's blocked by pre-existing macOS-only missing_const_for_fn noise in rocm-core and my temporary macOS build shim (reverted before commit), neither of which touches this change. Watching CI to green.

@r0x0r
r0x0r force-pushed the fix/driver-dry-run-repo-version-placeholder branch 3 times, most recently from c25f792 to efca889 Compare August 27, 2026 09:18
@volen-silo

Copy link
Copy Markdown
Collaborator

Follow-up review — re-verified against the current head efca8890

The branch was rebased and the commit amended since my review (that review was against the previous head; the whole PR is now one commit on top of 4512ce7b). Scope grew from +82/-1 in one file to +308/-77 across apps/rocm/src/main.rs, tests/e2e-cucumber/features/examine.feature, and tests/e2e-cucumber/tests/e2e/examine_steps.rs. You took Option A. I re-checked every finding against the code at this head rather than against the reply.

Blocking 1 — no scenario for the user-observable output change: resolved

@id:install-driver-dry-run-resolves-repo-version is real and it works, not just present:

  • the step definitions bind to helpers that actually exist — field_value is defined in the same file, run_rocm matches its (String, String, i32) signature, and E2eWorld has both cli_output and cli_rc
  • @requires-os:linux is a live tag honoured by the harness (tests/e2e-cucumber/src/expectation.rs), not an invented one, and the @id: is unique across all feature files
  • the scenario runs on the blocking no-GPU mock lane: install driver --dry-run short-circuits in install_driver before any mutation and returns Ok, so it exits 0 on any Linux host including an unsupported distro, and repo_version: is printed unconditionally by render_driver_install_plan
  • the assertion is not vacuous — the old output ${ROCM_CLI_AMDGPU_VERSION:-7.2.4} fails both the !contains("${") check and the leading-ascii-digit check

Ran it locally: cargo xtask e2e -- -n "The driver install dry-run shows the effective repo version" — exit 0, 1 scenario passed. The E2E tests CI job is green too.

Blocking 2 — unsafe env mutation without serialization: resolved in substance, two stragglers left

ScopedTestEnv is correct. The static LOCK inside new() is a single process-wide instance; Mutex resolves via the module-level import; save() captures the original value exactly once per key so a set followed by a clear can't overwrite the true original; Drop restores in reverse. unsafe_code is deny (not forbid) at the workspace level, so the per-fn #[allow] is legitimate. And the doc comment now states the actual hazard — a mutation racing a concurrent read, not a name collision — which was the part I cared most about.

The leftover: this PR turned ~22 previously env-free build_driver_install_plan call sites in tests into env readers, because the resolution moved into that function. 20 of them took the guard. Two did not:

  • windows_install_driver_is_validate_only (apps/rocm/src/main.rs:24541)
  • wsl_install_driver_uses_rocdxg_guidance_without_dkms (apps/rocm/src/main.rs:24557)

Both call build_driver_install_plan, which calls resolve_shell_default_templatestd::env::var unconditionally at the top of the function, before the Windows/WSL branches. So they read env, unguarded, concurrently with the guarded mutators in the same binary — the exact race the guard exists to prevent, on the two call sites the sweep missed.

I'm not calling this blocking on its own: the mutating side is now serialized, which is the side edition 2024 made unsafe, and this binary already has plenty of unguarded env readers predating this PR (three disjoint env locks live in it — the new one, BUILTIN_ENGINE_ENV_LOCK, and PROCESS_ENV_TEST_LOCK in therock.rs, so full closure was never on the table here). But it's a two-line fix and the PR itself introduced those two readers, so I'd like it in this change rather than left as debris.

Blocking 2b — regression test reading the real override: resolved

driver_plan_dry_run_repo_version_line_is_resolved now takes with_amd_overrides_cleared(). Verified by running with the override actually exported:

ROCM_CLI_AMDGPU_VERSION=9.9.9 ROCM_CLI_AMDGPU_PACKAGE_RELEASE=99999 \
  cargo test -p rocm --bin rocm driver_plan

exit 0, 20 passed.

Blocking 3 — wrong layer, and the apt single-quote bug: resolved

Option A implemented properly, and I could not find a hole in it:

  • both templates resolve once at the top of build_driver_install_plan; every command builder (apt_driver_plan, dnf_driver_plan, sles_driver_plan, amdgpu_install_rpm_url, amdgpu_install_sles_rpm_url, driver_plan_via_id_like) now takes concrete &str/String values
  • repo-wide grep for ROCM_CLI_AMDGPU_VERSION / ROCM_CLI_AMDGPU_PACKAGE_RELEASE returns only the two resolution call sites, the test-isolation helper, one test assertion, and a feature-file comment describing the old behaviour. No generated command string still carries a ${...}
  • the apt sources.list line now interpolates in Rust before the string is embedded in the single-quoted shell literal, so it no longer depends on an expansion that could never happen. That was a genuine install-breaking bug for Ubuntu/Debian and derivatives, and it's fixed here
  • the $(uname -r) occurrences are all inside double quotes and still expand at execution time — no parallel bug introduced
  • the amdgpu_install_rpm_url refactor renamed the local repo_version (the path segment) to repo_version_path while the parameter took the repo_version name. I checked slot by slot: no path segment was swapped, the URL is semantically identical modulo resolution. Same for the SLES variant
  • no execution-time gap: DriverInstallPlan derives no Serialize, isn't persisted, and the commands run in the same process under the same env the resolution saw
  • repo_version_expr is fully gone from the repo; the only external consumer is the e2e step, which parses the rendered text line, not a struct field or JSON key

One behavioural nuance worth stating out loud, inherent to Option A rather than a defect: a user who previews the plan and copy-pastes the printed commands into a different shell later now gets the version baked in from the previewing process's env, not from the pasting shell. That's more predictable than before, and it's what the apt fix requires, but it is a change.

Non-blocking items from the original review

Addressed: helper renamed to resolve_shell_default_template; bare ${VAR} and nested-default shapes now pass through unchanged with tests for both; the "one line resolved, its neighbours not" inconsistency is moot now that everything resolves.

Still open, still non-blocking:

  • ROCM_CLI_AMDGPU_VERSION and ROCM_CLI_AMDGPU_PACKAGE_RELEASE remain undocumented in docs/, README.md, and skills/. More relevant now that they're the only lever on the version.
  • The helper's doc comment overclaims slightly. It says anything other than a flat ${VAR:-default} "is returned unchanged", but strip_prefix("${") / strip_suffix('}') only inspect the first and last characters, so multi-brace inputs slip through and get mis-parsed rather than returned: ${A}${B:-x} splits into var = "A}${B", default = "x" and yields x; ${VAR:-a}} folds the trailing brace into the default. Unreachable from either call site — both pass fixed literals — so this is a docstring/precision nit, not a bug. Either tighten the guard (bail if the stripped inner still contains ${ or }) or soften the sentence.
  • Related: now that both callers pass compile-time literals, the generic parser arguably doesn't earn its keep versus two direct env::var(...).ok().filter(...).unwrap_or(...) lookups. Genuinely a matter of taste — mentioning it, not asking for it.

New — the PR description is now stale, and misleading

This is the one thing I'd like fixed before merge, and it's a text edit. The body still describes the abandoned display-only approach:

  • "Resolve the template to its effective value for display only" — no longer true
  • "The executable commands still carry the literal template, so the runtime shell resolves it as before — only the summary line changes" — the opposite of what the code now does
  • "This is a cosmetic/clarity change to dry-run output; no behavior change to the executed commands" — actively wrong, and it buries the best part of this PR: the apt sources-list fix is a real behaviour change that fixes a broken install path
  • the listed test names (resolve_repo_version_*) no longer exist
  • the new scenario isn't named. AGENTS.md §3 requires the PR text to say which @id: covers the behaviour; that's in your comment but not in the body, and the body is the durable artifact
  • the title understates the change for the same reason

The commit message, by contrast, is accurate and well written — it already says most of what the body should. Lifting it into the description would be enough.

Gates

Run at this head:

  • cargo fmt --all -- --check — exit 0
  • cargo clippy --workspace --all-targets -- -D warnings — exit 0
  • cargo test --workspace --all-targets — exit 101, one failure: providers::tests::local_provider_prefers_lemonade_over_stale_builtin_services. Not this PR. It passes in isolation, and I reproduced the same failure on the merge base 4512ce7b (1 of 3 full-suite runs there), so it's a pre-existing flake in a test that spawns local HTTP servers and is load-sensitive.
  • cargo xtask e2e -- -n "The driver install dry-run shows the effective repo version" — exit 0
  • diff leak scan — clean
  • CI is green apart from the Read the Docs build, which is failing identically on every other open PR (infrastructure, unrelated)

Verdict

All three original blocking findings are resolved, and I verified each against the code rather than taking the reply at face value. Blocking 3 in particular came out better than I asked for — resolving package_release alongside the version, and hardening the helper, weren't required.

Two things left, neither of them deep:

  1. Update the PR description — it currently contradicts the diff (must-fix).
  2. Add the guard to windows_install_driver_is_validate_only and wsl_install_driver_uses_rocdxg_guidance_without_dkms (two lines, would-like).

With those, LGTM.

r0x0r added 2 commits August 27, 2026 12:25
The driver install plan stored the repo version and package release as
raw shell parameter-expansion templates (${ROCM_CLI_AMDGPU_VERSION:-7.2.4}
and ${ROCM_CLI_AMDGPU_PACKAGE_RELEASE:-70204}). Two problems followed.
The human-readable plan summary printed the version template verbatim on
its "repo_version:" line, leaking an unexpanded shell placeholder into
user-facing dry-run output. And the Debian/Ubuntu apt source line wrapped
the template in single quotes, so the shell never expanded it and the
literal placeholder would have been written into
/etc/apt/sources.list.d/amdgpu.list.

Resolve both templates once, at plan-build time, to their effective
values (the env var when set and non-empty, matching shell :- default
semantics, otherwise the documented default) and bake the concrete values
into every rendered URL, repo path, and command. The resolver is
generalized to resolve_shell_default_template and hardened to pass bare
${VAR} and nested-default shapes through unchanged rather than emitting a
partially rewritten value.

Add unit tests for the resolver (default, env override, empty-as-unset,
non-template pass-through, bare-var and nested-default pass-through) and
update every distro plan test to assert the concrete values, plus a
render-level regression test and an end-to-end scenario asserting the
dry-run summary shows the resolved version rather than the raw
placeholder. Tests that read these process env vars serialize on a shared
lock and save/restore prior values so they stay deterministic under
edition-2024 env semantics.

Signed-off-by: Roman Sirokov <roman.sirokov@amd.com>
build_driver_install_plan now resolves the repo-version template via
std::env::var at the top of the function, so every test calling it reads
process env. The sweep that added ScopedTestEnv::with_amd_overrides_cleared()
to the other call sites missed windows_install_driver_is_validate_only and
wsl_install_driver_uses_rocdxg_guidance_without_dkms, leaving two unguarded
readers racing the guarded mutators in the same test binary. Add the guard to
both. No production change.

Signed-off-by: Roman Sirokov <roman.sirokov@amd.com>
@r0x0r
r0x0r force-pushed the fix/driver-dry-run-repo-version-placeholder branch from efca889 to 0c4cde0 Compare August 27, 2026 12:26
@r0x0r r0x0r changed the title fix(install): resolve repo_version placeholder in driver dry-run output fix(install): resolve driver repo version and package release in plans Aug 27, 2026
@r0x0r

r0x0r commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator Author

Both remaining items are done.

  • PR description (must-fix): rewritten from the commit message so it matches the diff. It now leads with the two real problems — the cosmetic repo_version: leak and the install-breaking apt sources-list bug — and explicitly states this is a behavior change, not display-only. It names the scenario (@id:install-driver-dry-run-resolves-repo-version), drops the stale resolve_repo_version_* test names, and the title is updated to match the accurate commit subject.
  • Env guards (would-like): windows_install_driver_is_validate_only and wsl_install_driver_uses_rocdxg_guidance_without_dkms now take ScopedTestEnv::with_amd_overrides_cleared(), matching the other call sites. cargo test -p rocm --bin rocm driver — 26 passed.

Non-blocking, deferred and acknowledged: documenting ROCM_CLI_AMDGPU_VERSION / ROCM_CLI_AMDGPU_PACKAGE_RELEASE in docs//README/skills, and tightening the resolve_shell_default_template docstring re multi-brace inputs (unreachable from either call site today). I'd rather land those as a small dedicated follow-up than widen this PR.

Note on history: the fix commit was signed-off but not cryptographically signed, so it was failing the commit-signatures gate and blocking the push hook. I re-signed the range and force-pushed with lease; git range-diff confirms the fix commit is byte-identical (signature only) and the guard change is the one new commit on top. verify-commits, clippy, fmt and test all pass at the new head.

@r0x0r
r0x0r added this pull request to the merge queue Aug 27, 2026
Merged via the queue into main with commit 263ec0a Aug 27, 2026
18 of 19 checks passed
@r0x0r
r0x0r deleted the fix/driver-dry-run-repo-version-placeholder branch August 27, 2026 13:02
pmoutsias-amd pushed a commit that referenced this pull request Aug 27, 2026
#307)

* fix(install): resolve driver repo version and package release in plans

The driver install plan stored the repo version and package release as
raw shell parameter-expansion templates (${ROCM_CLI_AMDGPU_VERSION:-7.2.4}
and ${ROCM_CLI_AMDGPU_PACKAGE_RELEASE:-70204}). Two problems followed.
The human-readable plan summary printed the version template verbatim on
its "repo_version:" line, leaking an unexpanded shell placeholder into
user-facing dry-run output. And the Debian/Ubuntu apt source line wrapped
the template in single quotes, so the shell never expanded it and the
literal placeholder would have been written into
/etc/apt/sources.list.d/amdgpu.list.

Resolve both templates once, at plan-build time, to their effective
values (the env var when set and non-empty, matching shell :- default
semantics, otherwise the documented default) and bake the concrete values
into every rendered URL, repo path, and command. The resolver is
generalized to resolve_shell_default_template and hardened to pass bare
${VAR} and nested-default shapes through unchanged rather than emitting a
partially rewritten value.

Add unit tests for the resolver (default, env override, empty-as-unset,
non-template pass-through, bare-var and nested-default pass-through) and
update every distro plan test to assert the concrete values, plus a
render-level regression test and an end-to-end scenario asserting the
dry-run summary shows the resolved version rather than the raw
placeholder. Tests that read these process env vars serialize on a shared
lock and save/restore prior values so they stay deterministic under
edition-2024 env semantics.

Signed-off-by: Roman Sirokov <roman.sirokov@amd.com>

* test(install): guard the two remaining env-reading driver-plan tests

build_driver_install_plan now resolves the repo-version template via
std::env::var at the top of the function, so every test calling it reads
process env. The sweep that added ScopedTestEnv::with_amd_overrides_cleared()
to the other call sites missed windows_install_driver_is_validate_only and
wsl_install_driver_uses_rocdxg_guidance_without_dkms, leaving two unguarded
readers racing the guarded mutators in the same test binary. Add the guard to
both. No production change.

Signed-off-by: Roman Sirokov <roman.sirokov@amd.com>

---------

Signed-off-by: Roman Sirokov <roman.sirokov@amd.com>
Signed-off-by: pmoutsias-amd <peter.moutsias@amd.com>
mikeroySoft pushed a commit to mikeroySoft/rocm-cli that referenced this pull request Sep 1, 2026
* Add Sphinx documentation site

Adds a Sphinx documentation site under docs/rocm-docs/, single-sourced
from README.md and CONTRIBUTING.md via MyST {include} directives so
the docs and repo content stay in sync automatically. Covers
installation, getting started, command reference, demos, and
contributing/license, and is set up to publish with the rocm-docs-core
theme. Updates some links in README.md and CONTRIBUTING.md to be
absolute so they render correctly when reused via {include}.

Signed-off-by: pmoutsia_amdeng <peter.moutsias@amd.com>

* Single-source the platform-support table in the install page

Anchor the table in README.md and pull it into
docs/rocm-docs/install/installation.md via {include} instead of
duplicating it by hand.

Signed-off-by: pmoutsia_amdeng <peter.moutsias@amd.com>

* fix(docs): address review feedback on Sphinx docs site

- Add the MIT license header to the 7 new files hawkeye flagged as
  missing it.
- Fix the WSL2 platform-support-table link to docs/wsl.md: it's now an
  absolute GitHub URL so it resolves when the table is pulled into
  install/installation.md via MyST include, matching the pattern
  already used for the other README anchors.
- Pin rocm-docs-core to the exact version that resolved locally
  (1.40.0) instead of an open->= range.

Signed-off-by: pmoutsia_amdeng <peter.moutsias@amd.com>

* fix(docs): add ReadTheDocs config and a CI job to build docs with -W

- Add .readthedocs.yaml so RTD builds against docs/rocm-docs/conf.py
  with the same pinned rocm-docs-core toolchain CI uses.
- Add a docs-build CI job that runs sphinx-build -W (warnings as
  errors) against the same source RTD points at, gated on the new
  `docs` path-filter category so it only runs when doc sources,
  README.md/CONTRIBUTING.md (single-sourced via MyST includes), or
  the build config change.
- Suppress the myst.header warning from :start-after:/:end-before:
  README includes stripping the anchor heading, which is cosmetic:
  docutils re-normalizes the resulting heading depth in the rendered
  output.
- Scope external_projects to [] instead of the implicit "all" default,
  which was fetching intersphinx inventories for every project in
  rocm_docs' bundled catalog (~90 projects) - several of which 404 or
  have moved upstream. No page here uses a cross-project intersphinx
  role, so this is lossless and makes -W builds deterministic instead
  of flaky against infrastructure this repo doesn't control.
- Filter the "current project not found in projects" warning: rocm-cli
  isn't registered in rocm-docs-core's shared projects catalog yet, so
  external_projects_current_project can never resolve until that's
  added upstream. rocm_docs.projects handles the unresolved project as
  None everywhere it's used, so this is informational, not a defect.

Signed-off-by: pmoutsia_amdeng <peter.moutsias@amd.com>

* docs: use docs-relative cross-page links consistently

README.md's own #model-serving and #interactive-interfaces anchors were
rewritten to absolute GitHub URLs so they'd still work once pulled into
the docs site, but that breaks in-page anchor scrolling when the README
is viewed on GitHub. Revert both to relative anchors and instead apply
the cut-and-reauthor pattern already used for the CONTRIBUTING.md link
in installation.md: narrow the surrounding {include} in getting-started.md
and commands.md around each sentence and hand-author a docs-relative
replacement (commands.md#model-serving, getting-started.md#interactive-interfaces).

Signed-off-by: pmoutsia_amdeng <peter.moutsias@amd.com>

* docs: use descriptive nav title for Demos section

Avoids the sidebar rendering as duplicated Demos > Demos for the
single-entry section by overriding the nav link text to match the
homepage tile's wording.

Signed-off-by: pmoutsias-amd <peter.moutsias@amd.com>

* docs: fix sentence fragments in intro blurb

Joins the "single prebuilt binary" fragments into one complete
sentence in both README.md and the standalone index.rst homepage
copy, and adds a lead-in sentence before the bare `rocm` code fence
in the Getting Started "First run" section.

Signed-off-by: pmoutsias-amd <peter.moutsias@amd.com>

* docs: style guide pass on README and CONTRIBUTING

Sentence-case headings, expand DCO acronym, replace banned/informal
wording (may, e.g.), normalize slash usage, lowercase version
placeholder.

Signed-off-by: pmoutsias-amd <peter.moutsias@amd.com>

* fix(lemonade): retry interrupted backend setup (ROCm#249)

* fix(lemonade): retry interrupted backend setup

Signed-off-by: Eugene Volen <Eugene.Volen@amd.com>

* ci(e2e): build prebuilt lanes with the test-hooks feature

The suite's deterministic failure seams are compiled out without
`rocm/e2e-test-hooks`. `cargo xtask e2e` passes the feature only when it
builds the binaries itself, so every lane that pre-builds and exports
ROCM_CLI_BINARY silently tested a binary without those seams. The
scripted-Lemonade-failure scenario then never reached its premise and
failed as a regression on the one lane that selects it.

Pass the feature on all prebuilt e2e lanes, and add a workflow-contract
test so a lane cannot drift back. Release and packaging builds keep the
feature off — a shipped binary must not carry a failure-injection seam.

Also name the cause in the retry assertion, which otherwise reports a
baffling missing announcement rather than the real misconfiguration.

Signed-off-by: Eugene Volen <Eugene.Volen@amd.com>

---------

Signed-off-by: Eugene Volen <Eugene.Volen@amd.com>
Signed-off-by: pmoutsias-amd <peter.moutsias@amd.com>

* ci(dependabot): track pre-commit hook versions (ROCm#235)

The hook pins in .pre-commit-config.yaml were bumped by hand and drifted
between releases. Dependabot's pre-commit ecosystem resolves each rev
against the hook repository's tags, and skips the builtin and local
blocks, which have no upstream release to track.

Weekly and grouped, with the same 7-day cooldown as the other two
ecosystems: a hook is executable code that runs on every contributor's
machine at commit time and in the prek CI job, so a compromised release
would run before anyone reads the bump.

Signed-off-by: Roman Inflianskas <Roman.Inflianskas@amd.com>
Signed-off-by: pmoutsias-amd <peter.moutsias@amd.com>

* fix: raise the supported Linux minimum to Ubuntu 24.04 (ROCm#261)

* fix: raise the supported Linux minimum to Ubuntu 24.04

Every published Lemonade embeddable, v10.2.0 through v11.5.2, is linked
against GLIBC_2.38 and GLIBCXX_3.4.32. Ubuntu 22.04 ships glibc 2.35, so
the Lemonade engine cannot start there at all, yet README.md advertised
Linux x86_64 as "full support ... both inference engines" and docs/wsl.md
named 22.04 as a supported WSL base.

Document a single minimum of Ubuntu 24.04 for Linux and WSL2, keeping the
glibc number as the reason so the requirement stays meaningful on other
distributions, and drop 22.04 from the WSL preflight's supported set so the
automated check matches the prerequisite it enforces.

Fixes ROCm#258

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

* fix(wsl): accept Ubuntu releases after 24.04

Signed-off-by: Michael Roy <michael.roy@amd.com>

---------

Signed-off-by: Roman Inflianskas <Roman.Inflianskas@amd.com>
Signed-off-by: Michael Roy <michael.roy@amd.com>
Co-authored-by: Michael Roy <michael.roy@amd.com>
Signed-off-by: pmoutsias-amd <peter.moutsias@amd.com>

* ci(e2e): repair a pre-warm tree that records a folder elsewhere (ROCm#316)

A scenario reaches the shared pre-warm tree through a symlink at its own
`data/runtimes`. An `install sdk` run that way writes the link's path — a
per-scenario temp dir — into the manifest that lands in the SHARED
registry, and into the venv's console-script shebangs. The temp dir goes
away with the scenario and the shared runtime is left naming a folder
that no longer exists, so later, unrelated runs fail.

Drop such a runtime before deciding whether the tree is fresh, so the
pre-warm reinstalls instead of serving a dead one. `rocm update` cannot
see this: it compares versions against the index, not the runtime
against the disk.

Removing the folder is the repair, not a precaution. A poisoned venv
keeps a working `bin/python` — a symlink to the base interpreter, still
present — so the install reuses it and audits already-satisfied packages
rather than reinstalling them, leaving every shebang pointing at the
folder that went away. Measured on uv 0.9.30: reinstalling over a
poisoned venv reports success and repairs nothing.

The signal is an install root outside the tree, deliberately not
`status=unusable`. Unusable has many causes — a missing rocm_sdk probe
block alone reports it — and this deletes what it selects, so a healthy
multi-GiB runtime must not hang on a validation detail. Read-only
runtimes are exempt: `runtimes adopt` records an external folder on
purpose.

Refs: ROCm#315

Signed-off-by: Eugene Volen <Eugene.Volen@amd.com>
Signed-off-by: pmoutsias-amd <peter.moutsias@amd.com>

* fix(install): record the folder the files land in, not the link (ROCm#317)

* fix(install): record the folder the files land in, not the link

`install sdk` built the install root by joining onto the data dir and
recorded whatever that produced. Reaching `data/runtimes` through a
symlink is enough to make that name a route rather than a place: correct
while the link exists, and wrong the moment it goes.

The path outlives the command. It goes into the registry manifest, the
sidecar beside the runtime, and — through uv — the `#!` line of every
console script in the environment. So a runtime installed that way keeps
reporting itself installed at a folder that is not there, while the files
sit untouched next door and every entry point fails to start.

Resolve the root before anything is written to it. Only the root:
`python_executable` is derived from it and must keep the venv's own
`bin/python`, which is itself a symlink to the base interpreter, so
resolving that would record the system Python instead. The adopt path
already draws the line in the same place, canonicalizing the install root
next to `absolute_existing_file_path_preserving_symlink` for the
interpreter; this gives the install path the same treatment, `--prefix`
included.

The E2E harness creates exactly this shape when a scenario opts into the
shared pre-warmed runtime, so the shared tree on a runner was what got
poisoned — but nothing about the fault is test-only. Any data dir reached
through a link has it.

Refs: ROCm#315
Signed-off-by: Eugene Volen <Eugene.Volen@amd.com>

* fix(vllm): name the missing interpreter behind a failed launch

Two messages described a stale runtime as an absence.

A spawn that fails on a console script whose `#!` interpreter is gone
reports ENOENT against the script, so the error named a file that is
plainly there. Say which interpreter is missing when that is what
happened, and stay quiet when the ordinary reading is right — a genuinely
absent file, a binary, or a live interpreter.

Resolution drops a registered runtime whose recorded interpreter is not
there. That is correct — a runtime that cannot run is not a candidate —
but it left the failure describing an empty registry while `rocm runtimes
list` prints the entry. Name the manifests that were passed over and the
interpreters they record.

Both are additive: the note only ever decorates an error already being
returned, and a registry it cannot read yields no note rather than
replacing the original failure.

First tests for this area, which had none.

Refs: ROCm#315
Signed-off-by: Eugene Volen <Eugene.Volen@amd.com>

* test(install): compare resolved paths, not verbatim ones, on Windows

The Windows lane failed on the prefix rather than the behaviour.
`canonicalize` hands back `\\?\C:\…` there, so a test that built its
expectation that way compared a verbatim path against the plain one the
resolver deliberately returns, and three assertions turned on the
difference.

Build the expectations with the resolver instead, which strips the prefix
for the same reason the product does: a stored path is later compared
against ordinary ones, and `\\?\C:\…` never `starts_with`-matches `C:\…`.
That the prefix really is stripped stays asserted on its own, so this does
not become the function agreeing with itself.

This also removes a second Windows-only trap: `temp_dir()` there can
return an 8.3 short path, which `canonicalize` expands, so the two sides
disagreed on the folder name as well as the prefix.

Same fault in the scenario's step definitions, where the comparison would
have failed only on the Strix Windows lane. That crate cannot reach
`rocm-core`, and a dependency for six lines of string handling is the
worse trade, so it strips the prefix locally with the reason recorded.

Refs: ROCm#315
Signed-off-by: Eugene Volen <Eugene.Volen@amd.com>

* fix(install): give up on a parent component consistently across hosts

The guard against resolving a `..` past a missing directory never fired
on Windows, because the two platforms disagree about what such a path
means. Windows collapses `..` lexically, so `<root>/missing/..` "exists"
and canonicalizes to `<root>`; Unix walks the path through the filesystem
and finds nothing. The walk therefore stopped a level early on Windows,
resolved to the wrong folder, and re-attached the tail — recording a
different folder depending on the host, which is precisely what this
function exists to prevent.

Check for the parent component before asking whether the candidate
exists, so both hosts give up in the same place.

The give-up stays narrow: a `..` whose parent is really there is
unambiguous everywhere and still resolves, which is now pinned by its own
test so a future tightening cannot quietly leave ordinary paths
unresolved.

Found by the Windows lane; it does not reproduce on Linux.

Refs: ROCm#315
Signed-off-by: Eugene Volen <Eugene.Volen@amd.com>

---------

Signed-off-by: Eugene Volen <Eugene.Volen@amd.com>
Signed-off-by: pmoutsias-amd <peter.moutsias@amd.com>

* fix(install): resolve driver repo version and package release in plans (ROCm#307)

* fix(install): resolve driver repo version and package release in plans

The driver install plan stored the repo version and package release as
raw shell parameter-expansion templates (${ROCM_CLI_AMDGPU_VERSION:-7.2.4}
and ${ROCM_CLI_AMDGPU_PACKAGE_RELEASE:-70204}). Two problems followed.
The human-readable plan summary printed the version template verbatim on
its "repo_version:" line, leaking an unexpanded shell placeholder into
user-facing dry-run output. And the Debian/Ubuntu apt source line wrapped
the template in single quotes, so the shell never expanded it and the
literal placeholder would have been written into
/etc/apt/sources.list.d/amdgpu.list.

Resolve both templates once, at plan-build time, to their effective
values (the env var when set and non-empty, matching shell :- default
semantics, otherwise the documented default) and bake the concrete values
into every rendered URL, repo path, and command. The resolver is
generalized to resolve_shell_default_template and hardened to pass bare
${VAR} and nested-default shapes through unchanged rather than emitting a
partially rewritten value.

Add unit tests for the resolver (default, env override, empty-as-unset,
non-template pass-through, bare-var and nested-default pass-through) and
update every distro plan test to assert the concrete values, plus a
render-level regression test and an end-to-end scenario asserting the
dry-run summary shows the resolved version rather than the raw
placeholder. Tests that read these process env vars serialize on a shared
lock and save/restore prior values so they stay deterministic under
edition-2024 env semantics.

Signed-off-by: Roman Sirokov <roman.sirokov@amd.com>

* test(install): guard the two remaining env-reading driver-plan tests

build_driver_install_plan now resolves the repo-version template via
std::env::var at the top of the function, so every test calling it reads
process env. The sweep that added ScopedTestEnv::with_amd_overrides_cleared()
to the other call sites missed windows_install_driver_is_validate_only and
wsl_install_driver_uses_rocdxg_guidance_without_dkms, leaving two unguarded
readers racing the guarded mutators in the same test binary. Add the guard to
both. No production change.

Signed-off-by: Roman Sirokov <roman.sirokov@amd.com>

---------

Signed-off-by: Roman Sirokov <roman.sirokov@amd.com>
Signed-off-by: pmoutsias-amd <peter.moutsias@amd.com>

* fix(cli): accept negative float flag values in space form (EAI-8243) (ROCm#306)

* fix(cli): accept negative float flag values in space form

The space form of negative-number float flags (e.g. `serve --temperature
-1`) was rejected by clap with a confusing "unexpected argument '-1'"
error, because clap parsed the leading-dash token as a flag rather than a
value. Only the equals form (`--temperature=-1`) reached the range
validator that reports the valid range.

Enable `allow_negative_numbers` on the affected float flags
(`--temperature` and `--top-p` on both `chat` and `serve`) so the space
form reaches the value parser and both forms validate identically,
surfacing a clear range-validation message instead of an unexpected
argument error.

Add regression tests covering both the space and equals forms for
`--temperature` and `--top-p` on `chat` and `serve`.

Signed-off-by: Roman Sirokov <roman.sirokov@amd.com>

* fix(cli): extend negative-value fix to --max-tokens and add serve scenario

The negative-number-as-flag gotcha is a tokenizer-level ambiguity that
fires before any value parser runs, so it is not float-specific:
`--max-tokens -1` (space form) on both chat and serve was still rejected
as an unexpected argument while `--max-tokens=-1` reached parse_positive_u32.
Add allow_negative_numbers to --max-tokens on both commands so the space
form reaches the value parser and reports a clear error, matching the
--temperature/--top-p behavior fixed here.

Per AGENTS.md §3, the user-observable stderr change is now covered by an
ungated Gherkin scenario, @id:serve-negative-temperature-rejected, in
model_serving.feature. The value parser runs inside argument parsing
before engine selection or GPU pre-flight, so the scenario needs no GPU
and no engine and gates every PR (mirrors the ungated
@id:fix-position-argument-rejected precedent).

Also: reword the shared rationale comment to cover all three numeric value
flags (and note clap only treats a token as negative when the whole
remainder parses as one, so missing/malformed values still error at parse
time), give the chat regression test a symmetric comment, and extend both
space-form regression tests with the --max-tokens cases.

Signed-off-by: Roman Sirokov <roman.sirokov@amd.com>

* test(e2e): number the negative-temperature serve scenario

The new scenario was the only unnumbered title in model_serving.feature;
every other scenario uses 'Scenario: <N> - <Title>'. Assign the next free
number (15) to keep the file consistent. No behavior change.

Signed-off-by: Roman Sirokov <roman.sirokov@amd.com>

---------

Signed-off-by: Roman Sirokov <roman.sirokov@amd.com>
Signed-off-by: pmoutsias-amd <peter.moutsias@amd.com>

* fix(serve): validate --device via clap possible values (exit 2) (ROCm#304)

* fix(serve): validate --device via clap possible values (exit 2)

Model `rocm serve --device` as a clap `ValueEnum` (`DevicePolicyArg`) so an
invalid value is rejected by clap's usage validation (exit code 2) with the
valid choices listed, instead of parsing as a free-form string and failing
later in application logic with a generic "unsupported device policy" error
(exit code 1).

This makes `--device` consistent with every other enum-style argument in the
CLI (`--engine`, config set-*, engines install, etc.), all of which use clap
possible values. The historical aliases (`auto`/`gpu` for gpu_required, `cpu`
for cpu_only) stay accepted for backward compatibility but are hidden from the
advertised list, and the intentional cpu_only app-level rejection (exit 1 with
a GPU-required message) is preserved.

Adds regression tests asserting invalid `--device` is a clap InvalidValue usage
error and that every value and alias still parses, and reworks the device
possible-values sync test to read clap's structural possible values rather than
a hand-written doc string.

Signed-off-by: Roman Sirokov <roman.sirokov@amd.com>

* fix(serve): correct --device help text and hide cpu_only choice

The per-variant ValueEnum doc comments render in `serve --help`, shell
completions, and the invalid-value suggestion list. `gpu_preferred`
previously read "Prefer a ROCm GPU when one is available", which is
misleading: parse_device_policy maps gpu_preferred to GpuRequired and
rocm serve has no CPU fallback path. Reword it to state it behaves
identically to gpu_required.

cpu_only is always rejected (rocm serve requires GPU execution), so hide
it from the advertised choices with #[value(hide = true)] while keeping it
parseable so the deliberate rejection message is preserved.

Add device_policy_arg_maps_through_parse_device_policy to guard the
DevicePolicyArg::as_policy_str -> parse_device_policy mapping (gpu_required
and gpu_preferred resolve to GpuRequired; cpu_only is rejected), closing
the previously untested conversion path.

Signed-off-by: Roman Sirokov <roman.sirokov@amd.com>

* docs(serve): stop advertising hidden cpu_only device value

Hiding `cpu_only` from `--device` help/completion left three surfaces still
presenting it as a real choice. Align them with what the CLI now advertises:

- README serve synopsis dropped `cpu_only` from the --device value list.
- Removed the '--gpu is ignored with --device cpu_only (the model runs on CPU)'
  sentence: cpu_only is rejected (exit 1), never runs on CPU, so the line
  advertised a fallback path that does not exist (AGENTS.md §6).
- Corrected the drifted test comment to note cpu_only is #[value(hide = true)]
  and that serve_device_help_lists_match_device_policy_names compares against
  the full DevicePolicy set (hidden entries included).

Doc/comment only; no behavior change.

Signed-off-by: Roman Sirokov <roman.sirokov@amd.com>

---------

Signed-off-by: Roman Sirokov <roman.sirokov@amd.com>
Signed-off-by: pmoutsias-amd <peter.moutsias@amd.com>

* fix(dash): launcher shows live serving instances; amd-smi detection off critical path (eai-8190) (ROCm#295)

* fix(dash): seed launcher front door with live serving instances

The launcher front door built an empty AppState, so it always rendered
the idle variant even when a model was actively serving. Read the
managed-service registry (the same authority `rocm services` reads)
once per hub-loop pass and seed the AppState's instances from it.

Also treat `Ready` as serving everywhere the dashboard counts running
instances (`is_serving()`), matching the `Running`+`Ready` treatment
already used elsewhere (e.g. home.rs) -- a served model reports
`Ready`, not `Running`, so the count previously undercounted actual
serving models.

Adds apps/rocm's direct dependency on rocm-dash-core (previously only a
transitive dep) so the launcher can build `Instance`s from the
registry's `DiscoveredService` records.

Signed-off-by: Roman Sirokov <roman.sirokov@amd.com>

* fix(dash-daemon): run amd-smi detection off the run loop's critical path

Detecting amd-smi (`amd-smi version` plus the first `system_info()`)
can take up to ~15s on real hardware. Running it inline before the run
loop's first tick blocked managed-service discovery and the first
snapshot broadcast behind it, so an already-running model did not
surface in the dashboard until GPU detection finished -- a visible
~15-20s "0 models running" lag while `rocm services` already reported
it live.

Spawn detection in the background and adopt the result via a oneshot
channel the moment it lands, without ever blocking the loop while it
is in flight. The loop now starts ticking immediately, so serving
instances appear within one discovery tick; GPU metrics fill in once
detection completes.

Adds a regression test asserting on ordering (the instance must
surface in a snapshot whose gpu_system_info is still None) rather than
wall-clock timing, since a pure "arrived within Ns" check would be
flaky under subscriber starvation.

Signed-off-by: Roman Sirokov <roman.sirokov@amd.com>

* test(dash): exercise off-critical-path detection and launcher front door

Address review feedback on the amd-smi-off-critical-path change.

- Gate the "amd-smi unavailable" warning behind gpu_init_done so a healthy
  host never flashes it during the detection window; settle as unavailable
  (and say so once) if the detection task ever ends without a result.
- Add an amd_smi_skip_kfd_preflight test seam so the daemon regression test
  drives a fake amd-smi through the real detection path instead of
  short-circuiting on a GPU-less CI host (the /dev/kfd guard stays mandatory
  in production). The test now genuinely fails if detection moves back onto
  the critical path, and asserts the surfaced snapshot carries no premature
  "amd-smi unavailable" warning.
- Add direct unit tests for launcher_serving_instances (ready record maps to
  a live Instance; unbound :0 record is dropped) and a behavioural launcher
  scenario driving bare rocm through a PTY to prove the front door shows
  "Serving <model>" rather than "Idle" for a live registry service.

Signed-off-by: Roman Sirokov <roman.sirokov@amd.com>

---------

Signed-off-by: Roman Sirokov <roman.sirokov@amd.com>
Signed-off-by: pmoutsias-amd <peter.moutsias@amd.com>

* Fix diagnose emitting invalid UNKNOWN render-group remediation (ROCm#302)

check_4_render_group built the render-group remediation command from the
/dev/kfd owner group obtained via `stat -c %G`. When the device GID has no
matching group name, stat prints the literal "UNKNOWN", and the existing
guard only rejected empty strings. That let UNKNOWN leak into the suggested
fix, producing `sudo usermod -a -G UNKNOWN,video "$USER"` in the plan,
summary, and --json output -- a command that fails because no such group
exists.

Reject "UNKNOWN" (case-insensitive) alongside empty values so the group
falls back to "render". This makes diagnose's fallback coincide with the
"render,video" that fix.rs hardcodes in the UNKNOWN/empty case; it does not
unify the two group sources, which are still computed independently and can
diverge when /dev/kfd has a real group name that is not "render".

Add a regression test covering both the "UNKNOWN" and "unknown" sentinel
casings falling back to the render group.

Signed-off-by: Roman Sirokov <roman.sirokov@amd.com>
Signed-off-by: pmoutsias-amd <peter.moutsias@amd.com>

* docs(readme): reframe install channels around published release, document rocm examine, describe Configuration section

Signed-off-by: pmoutsias-amd <peter.moutsias@amd.com>

* docs(readme): remove rocm model from command reference

Not one of the commands being hardened for this release; team decision
was to drop it from the reference for now rather than document it.

Signed-off-by: pmoutsias-amd <peter.moutsias@amd.com>

* docs(conf): switch to rocm-ai flavor, enable repo and download buttons

Signed-off-by: pmoutsias-amd <peter.moutsias@amd.com>

* docs(conf): revert repository/download header buttons

use_repository_button and use_download_button rely on rocm-docs-core
resolving a repository_url from the current git branch. GitHub
Actions' pull_request checkout leaves a detached HEAD on the
synthetic merge ref, so get_branch() returns an empty URL, and
sphinx_book_theme crashes trying to unpack it, failing the -W build.

Signed-off-by: pmoutsias-amd <peter.moutsias@amd.com>

* docs(conf): fix flavor fallback and pin repository buttons

- Broaden the known-warning suppression filter to also cover the
  "rocm-ai" flavor, which isn't in the published rocm-docs-core
  release yet and would otherwise fail the -W build even though
  rocm_docs.theme already falls back to "rocm" gracefully.
- Re-enable use_repository_button/use_download_button, pinning
  repository_url/repository_branch explicitly instead of letting
  rocm-docs-core infer them from the local git branch. CI's
  pull_request checkout leaves a detached HEAD on the synthetic merge
  ref, which resolves to an empty repository_url and crashes
  sphinx_book_theme's repository button; use_download_button was
  never actually affected by this, it only reads local source files.

Verified by building against the pinned rocm-docs-core==1.40.0 in a
clean venv (matching CI) -- build succeeds with 0 warnings.

Signed-off-by: pmoutsias-amd <peter.moutsias@amd.com>

---------

Signed-off-by: pmoutsia_amdeng <peter.moutsias@amd.com>
Signed-off-by: pmoutsias-amd <peter.moutsias@amd.com>
Signed-off-by: Eugene Volen <Eugene.Volen@amd.com>
Signed-off-by: Roman Inflianskas <Roman.Inflianskas@amd.com>
Signed-off-by: Michael Roy <michael.roy@amd.com>
Signed-off-by: Roman Sirokov <roman.sirokov@amd.com>
Co-authored-by: Eugene Volen <150254791+volen-silo@users.noreply.github.com>
Co-authored-by: Roman Inflianskas <Roman.Inflianskas@amd.com>
Co-authored-by: Michael Roy <michael.roy@amd.com>
Co-authored-by: Roman <roman.sirokov@amd.com>
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.

2 participants