diff --git a/crates/uv/src/commands/tool/run.rs b/crates/uv/src/commands/tool/run.rs index 5442a5f49aa6..36a3ac2c76b4 100644 --- a/crates/uv/src/commands/tool/run.rs +++ b/crates/uv/src/commands/tool/run.rs @@ -77,10 +77,13 @@ pub(crate) async fn run( cache: Cache, printer: Printer, ) -> anyhow::Result { - // treat empty command similar to `uv tool list`, list available tools - // but without propagating malformed tool errors. + // Treat empty command similar to `uv tool list`, list available tools. let Some(command) = command else { - return list_available_tools(invocation_source, &cache, printer).await; + match list_available_tools(invocation_source, &cache, printer).await { + // It is a failure because user misses a required tool name. + Ok(()) => return Ok(ExitStatus::Error), + Err(err) => return Err(err), + }; }; let (target, args) = command.split(); @@ -268,7 +271,7 @@ async fn list_available_tools( invocation_source: ToolRunCommand, cache: &Cache, printer: Printer, -) -> anyhow::Result { +) -> anyhow::Result<()> { writeln!( printer.stdout(), "Provide a command to invoke with `{invocation_source} ` \ @@ -282,7 +285,7 @@ async fn list_available_tools( Ok(lock) => lock, Err(uv_tool::Error::Io(err)) if err.kind() == std::io::ErrorKind::NotFound => { writeln!(printer.stdout(), "{no_tools_installed_msg}")?; - return Ok(ExitStatus::Success); + return Ok(()); } Err(err) => return Err(err.into()), }; @@ -292,7 +295,7 @@ async fn list_available_tools( if tools.is_empty() { writeln!(printer.stdout(), "{no_tools_installed_msg}")?; - return Ok(ExitStatus::Success); + return Ok(()); } let mut buf = String::new(); @@ -317,7 +320,7 @@ async fn list_available_tools( // Installed tools were malformed or failed fetching versions. if buf.is_empty() { writeln!(printer.stderr(), "{no_tools_installed_msg}")?; - return Ok(ExitStatus::Success); + return Ok(()); } writeln!( @@ -329,7 +332,7 @@ async fn list_available_tools( printer.stdout(), "See `{invocation_source} --help` for more information." )?; - Ok(ExitStatus::Success) + Ok(()) } /// Display a warning if an executable is not provided by package. diff --git a/crates/uv/tests/tool_run.rs b/crates/uv/tests/tool_run.rs index 209d64bcb969..bc8445b4f48b 100644 --- a/crates/uv/tests/tool_run.rs +++ b/crates/uv/tests/tool_run.rs @@ -750,8 +750,8 @@ fn tool_run_list_installed() { uv_snapshot!(context.filters(), context.tool_run() .env("UV_TOOL_DIR", tool_dir.as_os_str()) .env("XDG_BIN_HOME", bin_dir.as_os_str()), @r###" - success: true - exit_code: 0 + success: false + exit_code: 2 ----- stdout ----- Provide a command to invoke with `uv tool run ` or `uv tool run --from `. @@ -773,8 +773,8 @@ fn tool_run_list_installed() { uv_snapshot!(context.filters(), context.tool_run() .env("UV_TOOL_DIR", tool_dir.as_os_str()) .env("XDG_BIN_HOME", bin_dir.as_os_str()), @r###" - success: true - exit_code: 0 + success: false + exit_code: 2 ----- stdout ----- Provide a command to invoke with `uv tool run ` or `uv tool run --from `. @@ -899,9 +899,8 @@ fn tool_run_with_editable() -> anyhow::Result<()> { "###); // Requesting an editable requirement should install it in a layer, even if it satisfied - uv_snapshot!(context.filters(), context.tool_run().arg("--with-editable").arg("./src/anyio_local").arg("flask").arg("--version").env("UV_TOOL_DIR", tool_dir.as_os_str()).env("XDG_BIN_HOME", bin_dir.as_os_str()) - - , @r###" + uv_snapshot!(context.filters(), context.tool_run().arg("--with-editable").arg("./src/anyio_local").arg("flask").arg("--version").env("UV_TOOL_DIR", tool_dir.as_os_str()).env("XDG_BIN_HOME", bin_dir.as_os_str()), + @r###" success: true exit_code: 0 ----- stdout -----