Repository navigation
Deduplicate the [y/N] confirmation prompt - #782
jussielo-amd wants to merge 2 commits into
Conversation
fix.rs, uninstall_plan.rs and therock.rs each hand-rolled the same print-prompt/flush/read-line/parse sequence for a [y/N] confirmation. Promote the shared primitive (is_affirmative_answer, read_yes_no) into a new rocm-core::confirm module and have all three delegate to it. fix.rs keeps its own --yes/non-interactive short-circuit and refusal wording layered on top, since that's specific to the fix recipe flow and already covered by e2e assertions; the other two callers already guard the non-interactive case at their call sites, so they only need the raw read-and-parse primitive. No observable CLI behavior changes. Signed-off-by: Jussi Elo <jussi.elo@amd.com>
There was a problem hiding this comment.
🟡 Changes recommended
The shared confirmation helper can panic instead of returning output errors, and its flush-error handling changes caller behavior and misreports output failures as input failures.
2 open issues
What changed in this PR
This PR centralizes the CLI’s duplicated [y/N] prompt logic in rocm-core, addressing #769 while keeping caller-specific confirmation policies in their existing wrappers.
Changes:
- Adds and exports shared response-parsing and prompt-reading helpers, with parser unit tests.
- Updates fix, uninstall, and SDK-overwrite prompts to use the shared helper.
- Documents the helper in contributor guidance and the architecture module map.
| File | Description |
|---|---|
| docs/architecture.md | Adds the shared confirmation module to the module map. |
| crates/rocm-core/src/lib.rs | Exposes the module and re-exports its helpers. |
| crates/rocm-core/src/fix.rs | Delegates prompt handling and relocates parser tests. |
| crates/rocm-core/src/confirm.rs | Implements shared confirmation helpers and parser tests. |
| apps/rocm/src/uninstall_plan.rs | Delegates the uninstall prompt to the shared helper. |
| apps/rocm/src/therock.rs | Delegates the SDK-overwrite prompt to the shared helper. |
| AGENTS.md | Lists the confirmation helper among shared CLI components. |
Review effort: Balanced
| /// errors if the prompt can't be written or the line can't be read; a | ||
| /// declined answer is `Ok(false)`, not an error. | ||
| pub fn read_yes_no(prompt: &str) -> std::io::Result<bool> { | ||
| print!("{prompt}"); |
There was a problem hiding this comment.
Fixed in 50e0f87: print_prompt now writes via a fallible write! instead of print!, so a write failure returns Err instead of panicking.
| /// declined answer is `Ok(false)`, not an error. | ||
| pub fn read_yes_no(prompt: &str) -> std::io::Result<bool> { | ||
| print!("{prompt}"); | ||
| std::io::stdout().flush()?; |
There was a problem hiding this comment.
Fixed in 50e0f87: split into print_prompt (fallible write+flush) and read_answer (fallible read+parse) so each caller keeps its own original error policy instead of one bundled Result forcing the same decision everywhere. fix::confirm() now explicitly discards a print_prompt error and still attempts the read (restoring its pre-existing behavior); uninstall_plan::confirm_uninstall and therock::prompt_yes_no keep propagating a write failure with their original "failed to flush ..." wording.
Copilot's review on PR ROCm#782 flagged two real issues in the prior commit: the shared prompt helper used `print!`, which panics on a write failure instead of returning it, and bundling write+flush+read into one fallible step made fix::confirm() decline on any I/O error instead of attempting the read when only the write failed, as it did before this helper existed. Split the helper into print_prompt (fallible write+flush) and read_answer (fallible read+parse), each generic over Write/BufRead so the sequence is unit-tested with in-memory buffers and failing-I/O test doubles instead of only indirectly through e2e coverage. fix.rs's confirm() now explicitly discards a print_prompt error and attempts the read anyway, restoring its original (pre-dedup) behavior; the two apps/rocm callers keep treating a failed write as fatal, with their original "failed to flush ..." wording. Also drops the ambiguous crate-root re-export in favor of the qualified rocm_core::confirm:: path, and fixes a few doc-accuracy issues two independent review passes raised: an overclaimed panic scenario, a dropped "[y/N]: "-suffix note, an inaccurate caller list, and the AGENTS.md entry sitting under the apps/rocm-only "CLI" list rather than rocm-core, where the module actually lives. Signed-off-by: Jussi Elo <jussi.elo@amd.com>

Closes #769.
fix.rs,uninstall_plan.rsandtherock.rseach hand-rolled the same[y/N]prompt sequence: print the prompt, flush stdout, read a line from stdin, normalize it, and matchy/yes. This promotes that sequence into a new sharedrocm_core::confirmmodule (is_affirmative_answer,print_prompt,read_answer) and has all three call sites delegate to it.print_prompt(fallible write+flush) andread_answer(fallible read+parse) stay two separate calls rather than one bundled function, because the three callers disagree on whether a failed write should be fatal:fix.rs'sconfirm()attempts the read anyway (its original behavior), whileuninstall_plan::confirm_uninstallandtherock::prompt_yes_notreat it as fatal, each with their own original "failed to flush ..." wording. Both are generic overWrite/BufRead, so the sequence is unit-tested with in-memory buffers and failing-I/O test doubles rather than only reachable through e2e coverage.fix.rs'sconfirm()also keeps its own--yes/non-interactive short-circuit and refusal wording layered on top of the shared primitives, rather than having that promoted too: that wording ("refusing to apply") is specific to thefixrecipe flow and is asserted verbatim bytests/e2e-cucumber/tests/e2e/diagnose_steps.rs. The other two callers (uninstall_plan::confirm_uninstall,therock::prompt_yes_no) already guard the non-interactive/--yescase at their own call sites before ever reaching the prompt, so they only need the raw primitives, not a second copy of that short-circuit.This is behavior-preserving relative to the pre-#769 original, with one deliberate improvement: a prompt write failure now returns an
Errinstead of risking a panic (the oldprint!-based code could panic on a write failure for a long enough prompt; none of the real prompts here are that long, but the fallible path costs nothing and removes the possibility outright). No prompt text or printed message changed. No new Gherkin scenario is needed since there's no user-observable behavior change; the existing e2e coverage for these flows is untouched.Also adds a bullet to AGENTS.md's "Shared UI components" section (under a new
crates/rocm-core-scoped heading, since the module is shared by bothapps/rocmandrocm-coreitself, not CLI-only) and a module-map entry indocs/architecture.md, per the issue's own documentation ask, so a future fourth copy has something to find instead of re-deriving the pattern again.Review history
Copilot's initial review caught two real issues in the first commit (
d6408dd3):print!panicking instead of returningErron a write failure, and bundling write+flush+read into one fallible step changingfix::confirm()'s behavior on a write failure compared to before this module existed. Both are fixed in50e0f87d(see replies on the original threads) after two independent fresh-review passes, which also caught a test-coverage gap (the primitives were hard-wired to real stdin/stdout and untestable) and several doc-accuracy issues, all addressed in that same commit.Checklist
tests/e2e-cucumber/expectations.tomlreferences Deduplicate the [y/N] confirmation prompt (three independent copies) #769; nothing to narrow.rocm-coremodule follows the documented library-crate pattern (pub mod confirm;, accessed via the qualifiedrocm_core::confirm::path).Verification
cargo build -p rocm-core -p rocmcargo clippy --workspace --all-targets -- -D warningscargo test --workspace --all-targets --no-fail-fast(all green)cargo xtask check-crate-edgescargo xtask coverage(no floor regressions)cargo fmt --check