Skip to content

Commit 2f22ef9

Browse files
shanselmanCopilot
andcommitted
fix: properly report failing winget operations as errors (#12)
Split run_winget into lenient mode (for queries like search/list where non-zero exit with stdout is valid) and strict mode (for mutating operations like install/uninstall/upgrade where non-zero exit should always be an error). Previously, any command that produced stdout was treated as successful regardless of exit code. Fixes #12 Co-authored-by: Copilot <[email protected]>
1 parent 6a10e28 commit 2f22ef9

File tree

1 file changed

+25
-6
lines changed

1 file changed

+25
-6
lines changed

src/cli_backend.rs

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,37 @@ impl CliBackend {
1515
}
1616

1717
async fn run_winget(&self, args: &[&str]) -> Result<String> {
18+
self.run_winget_inner(args, false).await
19+
}
20+
21+
/// Run winget in strict mode: any non-zero exit is an error.
22+
/// Use for mutating operations (install, uninstall, upgrade).
23+
async fn run_winget_strict(&self, args: &[&str]) -> Result<String> {
24+
self.run_winget_inner(args, true).await
25+
}
26+
27+
async fn run_winget_inner(&self, args: &[&str], strict: bool) -> Result<String> {
1828
let output = Command::new("winget")
1929
.args(args)
2030
.output()
2131
.await
2232
.context("Failed to run winget. Is it installed?")?;
2333

24-
// winget may return non-zero for "no results" — still valid
2534
let stdout = String::from_utf8_lossy(&output.stdout).to_string();
2635
let stderr = String::from_utf8_lossy(&output.stderr).to_string();
2736

28-
if !output.status.success() && stdout.trim().is_empty() {
29-
bail!("winget failed: {}", stderr.trim());
37+
if !output.status.success() {
38+
if strict || stdout.trim().is_empty() {
39+
// In strict mode, always fail. In lenient mode, fail only if
40+
// there's no stdout (winget returns non-zero for "no results"
41+
// but still prints a table).
42+
let detail = if stderr.trim().is_empty() {
43+
stdout.trim().to_string()
44+
} else {
45+
stderr.trim().to_string()
46+
};
47+
bail!("winget failed: {}", detail);
48+
}
3049
}
3150

3251
// winget uses \r to overwrite progress spinners in-place, and outputs
@@ -391,16 +410,16 @@ impl WingetBackend for CliBackend {
391410
args.push("--version");
392411
args.push(v);
393412
}
394-
self.run_winget(&args).await
413+
self.run_winget_strict(&args).await
395414
}
396415

397416
async fn uninstall(&self, id: &str) -> Result<String> {
398-
self.run_winget(&["uninstall", "--id", id, "--accept-source-agreements"])
417+
self.run_winget_strict(&["uninstall", "--id", id, "--accept-source-agreements"])
399418
.await
400419
}
401420

402421
async fn upgrade(&self, id: &str) -> Result<String> {
403-
self.run_winget(&["upgrade", "--id", id, "--accept-source-agreements", "--accept-package-agreements"])
422+
self.run_winget_strict(&["upgrade", "--id", id, "--accept-source-agreements", "--accept-package-agreements"])
404423
.await
405424
}
406425

0 commit comments

Comments
 (0)