mirror of https://github.com/astral-sh/uv
Enable `uv tool uninstall uv` on Windows (#8963)
## Summary Extending self-delete and self-replace functionality to uv itself on Windows. Closes https://github.com/astral-sh/uv/issues/6400.
This commit is contained in:
parent
389a26ef9e
commit
3ee2b10738
|
|
@ -4432,6 +4432,7 @@ dependencies = [
|
|||
"reqwest",
|
||||
"rkyv",
|
||||
"rustc-hash",
|
||||
"self-replace",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"similar",
|
||||
|
|
|
|||
|
|
@ -184,6 +184,8 @@ impl InstalledTools {
|
|||
environment_path.user_display()
|
||||
);
|
||||
|
||||
// TODO(charlie): On Windows, if the current executable is in the directory,
|
||||
// we need to use `safe_delete`.
|
||||
fs_err::remove_dir_all(environment_path)?;
|
||||
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -86,6 +86,8 @@ pub(crate) fn create(
|
|||
if allow_existing {
|
||||
debug!("Allowing existing directory");
|
||||
} else if location.join("pyvenv.cfg").is_file() {
|
||||
// TODO(charlie): On Windows, if the current executable is in the directory,
|
||||
// we need to use `safe_delete`.
|
||||
debug!("Removing existing directory");
|
||||
fs::remove_dir_all(location)?;
|
||||
fs::create_dir_all(location)?;
|
||||
|
|
|
|||
|
|
@ -99,6 +99,9 @@ walkdir = { workspace = true }
|
|||
which = { workspace = true }
|
||||
zip = { workspace = true }
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
self-replace = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
assert_cmd = { version = "2.0.16" }
|
||||
assert_fs = { version = "1.1.2" }
|
||||
|
|
|
|||
|
|
@ -125,20 +125,13 @@ pub(crate) fn install_executables(
|
|||
return Ok(ExitStatus::Failure);
|
||||
}
|
||||
|
||||
// Check if they exist, before installing
|
||||
// Error if we're overwriting an existing entrypoint, unless the user passed `--force`.
|
||||
if !force {
|
||||
let mut existing_entry_points = target_entry_points
|
||||
.iter()
|
||||
.filter(|(_, _, target_path)| target_path.exists())
|
||||
.peekable();
|
||||
|
||||
// Ignore any existing entrypoints if the user passed `--force`, or the existing recept was
|
||||
// broken.
|
||||
if force {
|
||||
for (name, _, target) in existing_entry_points {
|
||||
debug!("Removing existing executable: `{name}`");
|
||||
fs_err::remove_file(target)?;
|
||||
}
|
||||
} else if existing_entry_points.peek().is_some() {
|
||||
if existing_entry_points.peek().is_some() {
|
||||
// Clean up the environment we just created
|
||||
installed_tools.remove_environment(name)?;
|
||||
|
||||
|
|
@ -159,14 +152,26 @@ pub(crate) fn install_executables(
|
|||
.join(", ")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
let itself = std::env::current_exe().ok();
|
||||
|
||||
for (name, source_path, target_path) in &target_entry_points {
|
||||
debug!("Installing executable: `{name}`");
|
||||
|
||||
#[cfg(unix)]
|
||||
replace_symlink(source_path, target_path).context("Failed to install executable")?;
|
||||
|
||||
#[cfg(windows)]
|
||||
if itself.as_ref().is_some_and(|itself| {
|
||||
std::path::absolute(target_path).is_ok_and(|target| *itself == target)
|
||||
}) {
|
||||
self_replace::self_replace(source_path).context("Failed to install entrypoint")?;
|
||||
} else {
|
||||
fs_err::copy(source_path, target_path).context("Failed to install entrypoint")?;
|
||||
}
|
||||
}
|
||||
|
||||
let s = if target_entry_points.len() == 1 {
|
||||
""
|
||||
|
|
|
|||
|
|
@ -180,6 +180,9 @@ async fn uninstall_tool(
|
|||
// Remove the tool itself.
|
||||
tools.remove_environment(name)?;
|
||||
|
||||
#[cfg(windows)]
|
||||
let itself = std::env::current_exe().ok();
|
||||
|
||||
// Remove the tool's entrypoints.
|
||||
let entrypoints = receipt.entrypoints();
|
||||
for entrypoint in entrypoints {
|
||||
|
|
@ -187,6 +190,15 @@ async fn uninstall_tool(
|
|||
"Removing executable: {}",
|
||||
entrypoint.install_path.user_display()
|
||||
);
|
||||
|
||||
#[cfg(windows)]
|
||||
if itself.as_ref().is_some_and(|itself| {
|
||||
std::path::absolute(&entrypoint.install_path).is_ok_and(|target| *itself == target)
|
||||
}) {
|
||||
self_replace::self_delete()?;
|
||||
continue;
|
||||
}
|
||||
|
||||
match fs_err::tokio::remove_file(&entrypoint.install_path).await {
|
||||
Ok(()) => {}
|
||||
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
|
||||
|
|
|
|||
Loading…
Reference in New Issue