Show dedicated message for tools with no entrypoints (#4694)

## Summary

Closes https://github.com/astral-sh/uv/issues/4688.

## Test Plan

```
❯ cargo run tool install ruff
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.14s
     Running `target/debug/uv tool install ruff`
warning: `uv tool install` is experimental and may change without warning.
Resolved 1 package in 136ms
Installed 1 package in 3ms
 + ruff==0.5.0
No entrypoints to install for tool `ruff`
```
This commit is contained in:
Charlie Marsh 2024-07-01 12:22:45 -04:00 committed by GitHub
parent 324e9fe5cf
commit 305868cdcc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 33 additions and 2 deletions

View file

@ -200,6 +200,13 @@ pub(crate) async fn install(
}) })
.collect::<BTreeSet<_>>(); .collect::<BTreeSet<_>>();
if target_entry_points.is_empty() {
// Clean up the environment we just created
installed_tools.remove_environment(&name)?;
bail!("No entry points found for tool `{name}`");
}
// Check if they exist, before installing // Check if they exist, before installing
let mut existing_entry_points = target_entry_points let mut existing_entry_points = target_entry_points
.iter() .iter()
@ -232,7 +239,6 @@ pub(crate) async fn install(
) )
} }
// TODO(zanieb): Handle the case where there are no entrypoints
for (name, source_path, target_path) in &target_entry_points { for (name, source_path, target_path) in &target_entry_points {
debug!("Installing `{name}`"); debug!("Installing `{name}`");
#[cfg(unix)] #[cfg(unix)]
@ -250,7 +256,7 @@ pub(crate) async fn install(
.join(", ") .join(", ")
)?; )?;
debug!("Adding receipt for tool `{name}`",); debug!("Adding receipt for tool `{name}`");
let installed_tools = installed_tools.init()?; let installed_tools = installed_tools.init()?;
let tool = Tool::new( let tool = Tool::new(
requirements, requirements,

View file

@ -858,3 +858,28 @@ fn tool_install_xdg_bin_home() {
.child(format!("black{}", std::env::consts::EXE_SUFFIX)) .child(format!("black{}", std::env::consts::EXE_SUFFIX))
.assert(predicate::path::exists()); .assert(predicate::path::exists());
} }
/// Test installing a tool that lacks entrypoints
#[test]
fn tool_install_no_entrypoints() {
let context = TestContext::new("3.12").with_filtered_exe_suffix();
let tool_dir = context.temp_dir.child("tools");
let bin_dir = context.temp_dir.child("bin");
uv_snapshot!(context.filters(), context.tool_install()
.arg("iniconfig")
.env("UV_TOOL_DIR", tool_dir.as_os_str())
.env("XDG_BIN_HOME", bin_dir.as_os_str()), @r###"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
warning: `uv tool install` is experimental and may change without warning.
Resolved 1 package in [TIME]
Prepared 1 package in [TIME]
Installed 1 package in [TIME]
+ iniconfig==2.0.0
error: No entry points found for tool `iniconfig`
"###);
}