mirror of
https://github.com/astral-sh/uv.git
synced 2025-07-07 13:25:00 +00:00
Enable --all
to uninstall all managed tools (#4937)
## Summary Like #4932 but for tools.
This commit is contained in:
parent
bb703b8343
commit
55e1a7e011
4 changed files with 76 additions and 43 deletions
|
@ -2133,7 +2133,12 @@ pub struct ToolListArgs;
|
|||
#[allow(clippy::struct_excessive_bools)]
|
||||
pub struct ToolUninstallArgs {
|
||||
/// The name of the tool to uninstall.
|
||||
pub name: PackageName,
|
||||
#[arg(required = true)]
|
||||
pub name: Option<PackageName>,
|
||||
|
||||
/// Uninstall all tools.
|
||||
#[arg(long, conflicts_with("name"))]
|
||||
pub all: bool,
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
|
|
|
@ -86,7 +86,7 @@ impl InstalledTools {
|
|||
}
|
||||
|
||||
/// Return the metadata for all installed tools.
|
||||
pub fn tools(&self) -> Result<Vec<(String, Tool)>, Error> {
|
||||
pub fn tools(&self) -> Result<Vec<(PackageName, Tool)>, Error> {
|
||||
let _lock = self.acquire_lock();
|
||||
let mut tools = Vec::new();
|
||||
for directory in uv_fs::directories(self.root()) {
|
||||
|
@ -102,6 +102,7 @@ impl InstalledTools {
|
|||
};
|
||||
let tool_receipt = ToolReceipt::from_string(contents)
|
||||
.map_err(|err| Error::ReceiptRead(path, Box::new(err)))?;
|
||||
let name = PackageName::from_str(&name)?;
|
||||
tools.push((name, tool_receipt.tool));
|
||||
}
|
||||
Ok(tools)
|
||||
|
@ -256,16 +257,15 @@ impl InstalledTools {
|
|||
))
|
||||
}
|
||||
|
||||
pub fn version(&self, name: &str, cache: &Cache) -> Result<Version, Error> {
|
||||
let environment_path = self.root.join(name);
|
||||
let package_name = PackageName::from_str(name)?;
|
||||
pub fn version(&self, name: &PackageName, cache: &Cache) -> Result<Version, Error> {
|
||||
let environment_path = self.root.join(name.to_string());
|
||||
let environment = PythonEnvironment::from_root(&environment_path, cache)?;
|
||||
let site_packages = SitePackages::from_environment(&environment)
|
||||
.map_err(|err| Error::EnvironmentRead(environment_path.clone(), err.to_string()))?;
|
||||
let packages = site_packages.get_packages(&package_name);
|
||||
let packages = site_packages.get_packages(name);
|
||||
let package = packages
|
||||
.first()
|
||||
.ok_or_else(|| Error::MissingToolPackage(package_name, environment_path))?;
|
||||
.ok_or_else(|| Error::MissingToolPackage(name.clone(), environment_path))?;
|
||||
Ok(package.version().clone())
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ use tracing::debug;
|
|||
use uv_configuration::PreviewMode;
|
||||
use uv_fs::Simplified;
|
||||
use uv_normalize::PackageName;
|
||||
use uv_tool::InstalledTools;
|
||||
use uv_tool::{InstalledTools, Tool, ToolEntrypoint};
|
||||
use uv_warnings::warn_user_once;
|
||||
|
||||
use crate::commands::ExitStatus;
|
||||
|
@ -16,7 +16,7 @@ use crate::printer::Printer;
|
|||
|
||||
/// Uninstall a tool.
|
||||
pub(crate) async fn uninstall(
|
||||
name: PackageName,
|
||||
name: Option<PackageName>,
|
||||
preview: PreviewMode,
|
||||
printer: Printer,
|
||||
) -> Result<ExitStatus> {
|
||||
|
@ -25,27 +25,64 @@ pub(crate) async fn uninstall(
|
|||
}
|
||||
|
||||
let installed_tools = InstalledTools::from_settings()?;
|
||||
let Some(receipt) = installed_tools.get_tool_receipt(&name)? else {
|
||||
// If the tool is not installed, attempt to remove the environment anyway.
|
||||
match installed_tools.remove_environment(&name) {
|
||||
Ok(()) => {
|
||||
writeln!(
|
||||
printer.stderr(),
|
||||
"Removed dangling environment for `{name}`"
|
||||
)?;
|
||||
return Ok(ExitStatus::Success);
|
||||
}
|
||||
Err(uv_tool::Error::IO(err)) if err.kind() == std::io::ErrorKind::NotFound => {
|
||||
bail!("`{name}` is not installed");
|
||||
}
|
||||
Err(err) => {
|
||||
return Err(err.into());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let mut entrypoints = if let Some(name) = name {
|
||||
let Some(receipt) = installed_tools.get_tool_receipt(&name)? else {
|
||||
// If the tool is not installed, attempt to remove the environment anyway.
|
||||
match installed_tools.remove_environment(&name) {
|
||||
Ok(()) => {
|
||||
writeln!(
|
||||
printer.stderr(),
|
||||
"Removed dangling environment for `{name}`"
|
||||
)?;
|
||||
return Ok(ExitStatus::Success);
|
||||
}
|
||||
Err(uv_tool::Error::IO(err)) if err.kind() == std::io::ErrorKind::NotFound => {
|
||||
bail!("`{name}` is not installed");
|
||||
}
|
||||
Err(err) => {
|
||||
return Err(err.into());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
uninstall_tool(&name, &receipt, &installed_tools).await?
|
||||
} else {
|
||||
let mut entrypoints = vec![];
|
||||
for (name, receipt) in installed_tools.tools()? {
|
||||
entrypoints.extend(uninstall_tool(&name, &receipt, &installed_tools).await?);
|
||||
}
|
||||
entrypoints
|
||||
};
|
||||
entrypoints.sort_unstable_by(|a, b| a.name.cmp(&b.name));
|
||||
|
||||
if entrypoints.is_empty() {
|
||||
writeln!(printer.stderr(), "Nothing to uninstall")?;
|
||||
return Ok(ExitStatus::Success);
|
||||
}
|
||||
|
||||
let s = if entrypoints.len() == 1 { "" } else { "s" };
|
||||
writeln!(
|
||||
printer.stderr(),
|
||||
"Uninstalled {} executable{s}: {}",
|
||||
entrypoints.len(),
|
||||
entrypoints
|
||||
.iter()
|
||||
.map(|entrypoint| entrypoint.name.bold())
|
||||
.join(", ")
|
||||
)?;
|
||||
|
||||
Ok(ExitStatus::Success)
|
||||
}
|
||||
|
||||
/// Uninstall a tool.
|
||||
async fn uninstall_tool(
|
||||
name: &PackageName,
|
||||
receipt: &Tool,
|
||||
tools: &InstalledTools,
|
||||
) -> Result<Vec<ToolEntrypoint>> {
|
||||
// Remove the tool itself.
|
||||
installed_tools.remove_environment(&name)?;
|
||||
tools.remove_environment(name)?;
|
||||
|
||||
// Remove the tool's entrypoints.
|
||||
let entrypoints = receipt.entrypoints();
|
||||
|
@ -68,16 +105,5 @@ pub(crate) async fn uninstall(
|
|||
}
|
||||
}
|
||||
|
||||
let s = if entrypoints.len() == 1 { "" } else { "s" };
|
||||
writeln!(
|
||||
printer.stderr(),
|
||||
"Uninstalled {} executable{s}: {}",
|
||||
entrypoints.len(),
|
||||
entrypoints
|
||||
.iter()
|
||||
.map(|entrypoint| entrypoint.name.bold())
|
||||
.join(", ")
|
||||
)?;
|
||||
|
||||
Ok(ExitStatus::Success)
|
||||
Ok(entrypoints.to_vec())
|
||||
}
|
||||
|
|
|
@ -300,16 +300,18 @@ impl ToolListSettings {
|
|||
#[allow(clippy::struct_excessive_bools)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct ToolUninstallSettings {
|
||||
pub(crate) name: PackageName,
|
||||
pub(crate) name: Option<PackageName>,
|
||||
}
|
||||
|
||||
impl ToolUninstallSettings {
|
||||
/// Resolve the [`ToolUninstallSettings`] from the CLI and filesystem configuration.
|
||||
#[allow(clippy::needless_pass_by_value)]
|
||||
pub(crate) fn resolve(args: ToolUninstallArgs, _filesystem: Option<FilesystemOptions>) -> Self {
|
||||
let ToolUninstallArgs { name } = args;
|
||||
let ToolUninstallArgs { name, all } = args;
|
||||
|
||||
Self { name }
|
||||
Self {
|
||||
name: name.filter(|_| !all),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue