mirror of
https://github.com/astral-sh/uv.git
synced 2025-07-07 21:35: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)]
|
#[allow(clippy::struct_excessive_bools)]
|
||||||
pub struct ToolUninstallArgs {
|
pub struct ToolUninstallArgs {
|
||||||
/// The name of the tool to uninstall.
|
/// 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)]
|
#[derive(Args)]
|
||||||
|
|
|
@ -86,7 +86,7 @@ impl InstalledTools {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the metadata for all installed tools.
|
/// 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 _lock = self.acquire_lock();
|
||||||
let mut tools = Vec::new();
|
let mut tools = Vec::new();
|
||||||
for directory in uv_fs::directories(self.root()) {
|
for directory in uv_fs::directories(self.root()) {
|
||||||
|
@ -102,6 +102,7 @@ impl InstalledTools {
|
||||||
};
|
};
|
||||||
let tool_receipt = ToolReceipt::from_string(contents)
|
let tool_receipt = ToolReceipt::from_string(contents)
|
||||||
.map_err(|err| Error::ReceiptRead(path, Box::new(err)))?;
|
.map_err(|err| Error::ReceiptRead(path, Box::new(err)))?;
|
||||||
|
let name = PackageName::from_str(&name)?;
|
||||||
tools.push((name, tool_receipt.tool));
|
tools.push((name, tool_receipt.tool));
|
||||||
}
|
}
|
||||||
Ok(tools)
|
Ok(tools)
|
||||||
|
@ -256,16 +257,15 @@ impl InstalledTools {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn version(&self, name: &str, cache: &Cache) -> Result<Version, Error> {
|
pub fn version(&self, name: &PackageName, cache: &Cache) -> Result<Version, Error> {
|
||||||
let environment_path = self.root.join(name);
|
let environment_path = self.root.join(name.to_string());
|
||||||
let package_name = PackageName::from_str(name)?;
|
|
||||||
let environment = PythonEnvironment::from_root(&environment_path, cache)?;
|
let environment = PythonEnvironment::from_root(&environment_path, cache)?;
|
||||||
let site_packages = SitePackages::from_environment(&environment)
|
let site_packages = SitePackages::from_environment(&environment)
|
||||||
.map_err(|err| Error::EnvironmentRead(environment_path.clone(), err.to_string()))?;
|
.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
|
let package = packages
|
||||||
.first()
|
.first()
|
||||||
.ok_or_else(|| Error::MissingToolPackage(package_name, environment_path))?;
|
.ok_or_else(|| Error::MissingToolPackage(name.clone(), environment_path))?;
|
||||||
Ok(package.version().clone())
|
Ok(package.version().clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ use tracing::debug;
|
||||||
use uv_configuration::PreviewMode;
|
use uv_configuration::PreviewMode;
|
||||||
use uv_fs::Simplified;
|
use uv_fs::Simplified;
|
||||||
use uv_normalize::PackageName;
|
use uv_normalize::PackageName;
|
||||||
use uv_tool::InstalledTools;
|
use uv_tool::{InstalledTools, Tool, ToolEntrypoint};
|
||||||
use uv_warnings::warn_user_once;
|
use uv_warnings::warn_user_once;
|
||||||
|
|
||||||
use crate::commands::ExitStatus;
|
use crate::commands::ExitStatus;
|
||||||
|
@ -16,7 +16,7 @@ use crate::printer::Printer;
|
||||||
|
|
||||||
/// Uninstall a tool.
|
/// Uninstall a tool.
|
||||||
pub(crate) async fn uninstall(
|
pub(crate) async fn uninstall(
|
||||||
name: PackageName,
|
name: Option<PackageName>,
|
||||||
preview: PreviewMode,
|
preview: PreviewMode,
|
||||||
printer: Printer,
|
printer: Printer,
|
||||||
) -> Result<ExitStatus> {
|
) -> Result<ExitStatus> {
|
||||||
|
@ -25,27 +25,64 @@ pub(crate) async fn uninstall(
|
||||||
}
|
}
|
||||||
|
|
||||||
let installed_tools = InstalledTools::from_settings()?;
|
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.
|
// Remove the tool itself.
|
||||||
installed_tools.remove_environment(&name)?;
|
tools.remove_environment(name)?;
|
||||||
|
|
||||||
// Remove the tool's entrypoints.
|
// Remove the tool's entrypoints.
|
||||||
let entrypoints = receipt.entrypoints();
|
let entrypoints = receipt.entrypoints();
|
||||||
|
@ -68,16 +105,5 @@ pub(crate) async fn uninstall(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let s = if entrypoints.len() == 1 { "" } else { "s" };
|
Ok(entrypoints.to_vec())
|
||||||
writeln!(
|
|
||||||
printer.stderr(),
|
|
||||||
"Uninstalled {} executable{s}: {}",
|
|
||||||
entrypoints.len(),
|
|
||||||
entrypoints
|
|
||||||
.iter()
|
|
||||||
.map(|entrypoint| entrypoint.name.bold())
|
|
||||||
.join(", ")
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(ExitStatus::Success)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -300,16 +300,18 @@ impl ToolListSettings {
|
||||||
#[allow(clippy::struct_excessive_bools)]
|
#[allow(clippy::struct_excessive_bools)]
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(crate) struct ToolUninstallSettings {
|
pub(crate) struct ToolUninstallSettings {
|
||||||
pub(crate) name: PackageName,
|
pub(crate) name: Option<PackageName>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToolUninstallSettings {
|
impl ToolUninstallSettings {
|
||||||
/// Resolve the [`ToolUninstallSettings`] from the CLI and filesystem configuration.
|
/// Resolve the [`ToolUninstallSettings`] from the CLI and filesystem configuration.
|
||||||
#[allow(clippy::needless_pass_by_value)]
|
#[allow(clippy::needless_pass_by_value)]
|
||||||
pub(crate) fn resolve(args: ToolUninstallArgs, _filesystem: Option<FilesystemOptions>) -> Self {
|
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