Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Close #9871: add uv self uninstall command #11613

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -731,6 +731,7 @@ jobs:
./uvx -v ruff --version
eval "$(./uv generate-shell-completion bash)"
eval "$(./uvx --generate-shell-completion bash)"
./uv self uninstall --remove-data

smoke-test-macos:
timeout-minutes: 10
Expand All @@ -755,6 +756,7 @@ jobs:
./uvx -v ruff --version
eval "$(./uv generate-shell-completion bash)"
eval "$(./uvx --generate-shell-completion bash)"
./uv self uninstall --remove-data

smoke-test-windows-x86_64:
timeout-minutes: 10
Expand All @@ -775,6 +777,7 @@ jobs:
./uvx -v ruff --version
(& ./uv generate-shell-completion powershell) | Out-String | Invoke-Expression
(& ./uvx --generate-shell-completion powershell) | Out-String | Invoke-Expression
./uv self uninstall --remove-data

smoke-test-windows-aarch64:
timeout-minutes: 10
Expand All @@ -798,6 +801,7 @@ jobs:
./uvx -v ruff --version
(& ./uv generate-shell-completion powershell) | Out-String | Invoke-Expression
(& ./uvx --generate-shell-completion powershell) | Out-String | Invoke-Expression
./uv self uninstall --remove-data

integration-test-conda:
timeout-minutes: 10
Expand Down
10 changes: 10 additions & 0 deletions crates/uv-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,8 @@ pub struct SelfNamespace {
pub enum SelfCommand {
/// Update uv.
Update(SelfUpdateArgs),
/// Uninstall uv.
Uninstall(SelfUninstallArgs),
}

#[derive(Args, Debug)]
Expand All @@ -523,6 +525,14 @@ pub struct SelfUpdateArgs {
pub token: Option<String>,
}

#[derive(Args, Debug)]
pub struct SelfUninstallArgs {
/// true iff should run `uv cache clean` and remove directories pointed to by `uv python dir`
/// and `uv tool dir`
#[arg(long)]
pub remove_data: bool,
}

#[derive(Args)]
#[allow(clippy::struct_excessive_bools)]
pub struct CacheNamespace {
Expand Down
4 changes: 4 additions & 0 deletions crates/uv/src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ pub(crate) use python::list::list as python_list;
pub(crate) use python::pin::pin as python_pin;
pub(crate) use python::uninstall::uninstall as python_uninstall;
#[cfg(feature = "self-update")]
pub(crate) use self_uninstall::self_uninstall;
#[cfg(feature = "self-update")]
pub(crate) use self_update::self_update;
pub(crate) use tool::dir::dir as tool_dir;
pub(crate) use tool::install::install as tool_install;
Expand Down Expand Up @@ -71,6 +73,8 @@ mod python;
pub(crate) mod reporters;
mod run;
#[cfg(feature = "self-update")]
mod self_uninstall;
#[cfg(feature = "self-update")]
mod self_update;
mod tool;
mod venv;
Expand Down
45 changes: 45 additions & 0 deletions crates/uv/src/commands/self_uninstall.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use anyhow::Result;

use crate::commands::cache_clean;
use crate::commands::ExitStatus;
use crate::printer::Printer;
use std::env;
use uv_cache::rm_rf;
use uv_cache::Cache;
use uv_python::managed::ManagedPythonInstallations;
use uv_tool::InstalledTools;

pub(crate) fn self_uninstall(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should require the uv installer to have been used to enable this? Like we do in self_update

If so, that'd give us another way to determine the canonical install path.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. Is it fair to say that the uv installer was used iff uv was built with the self-update feature? In other words, is it reasonable to enable the uv self uninstall command iff the uv self update command is enabled?

Thanks.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can check for a receipt like the update command does.

is it reasonable to enable the uv self uninstall command iff the uv self update command is enabled?

This also makes sense to me.

cache: &Cache,
printer: Printer,
remove_data: bool,
) -> Result<ExitStatus> {
if remove_data {
// uv cache clean
cache_clean(&[], cache, printer)?;

// rm -r "$(uv python dir)"
let installed_toolchains = ManagedPythonInstallations::from_settings(None)?;
let python_directory = installed_toolchains.root();
rm_rf(python_directory)?;

// rm -r "$(uv tool dir)"
let installed_tools = InstalledTools::from_settings()?;
let tools_path = installed_tools.root();
rm_rf(tools_path)?;
}

// Remove uv and uvx binaries
// rm ~/.local/bin/uv ~/.local/bin/uvx

let uv_executable = env::current_exe().unwrap();
let uv_path = uv_executable.as_path();
// We assume uvx executable is in same directory as uv executable
let uvx_executable = uv_path.with_file_name(format!("uvx{}", std::env::consts::EXE_SUFFIX));
let uvx_path = uvx_executable.as_path();

rm_rf(uv_path)?;
rm_rf(uvx_path)?;

Ok(ExitStatus::Success)
}
8 changes: 6 additions & 2 deletions crates/uv/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use uv_cli::{
};
use uv_cli::{PythonCommand, PythonNamespace, ToolCommand, ToolNamespace, TopLevelArgs};
#[cfg(feature = "self-update")]
use uv_cli::{SelfCommand, SelfNamespace, SelfUpdateArgs};
use uv_cli::{SelfCommand, SelfNamespace, SelfUninstallArgs, SelfUpdateArgs};
use uv_fs::{Simplified, CWD};
use uv_requirements::RequirementsSource;
use uv_scripts::{Pep723Error, Pep723Item, Pep723Metadata, Pep723Script};
Expand Down Expand Up @@ -910,10 +910,14 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
token,
}),
}) => commands::self_update(target_version, token, printer).await,
#[cfg(feature = "self-update")]
Commands::Self_(SelfNamespace {
command: SelfCommand::Uninstall(SelfUninstallArgs { remove_data }),
}) => commands::self_uninstall(&cache, printer, remove_data),
#[cfg(not(feature = "self-update"))]
Commands::Self_(_) => {
anyhow::bail!(
"uv was installed through an external package manager, and self-update \
"uv was installed through an external package manager, and self {{update,uninstall}} \
is not available. Please use your package manager to update uv."
);
}
Expand Down
121 changes: 121 additions & 0 deletions docs/reference/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -9102,6 +9102,8 @@ uv self [OPTIONS] <COMMAND>

<dl class="cli-reference"><dt><a href="#uv-self-update"><code>uv self update</code></a></dt><dd><p>Update uv</p>
</dd>
<dt><a href="#uv-self-uninstall"><code>uv self uninstall</code></a></dt><dd><p>Uninstall uv</p>
</dd>
</dl>

### uv self update
Expand Down Expand Up @@ -9230,6 +9232,125 @@ uv self update [OPTIONS] [TARGET_VERSION]

</dd></dl>

### uv self uninstall

Uninstall uv

<h3 class="cli-reference">Usage</h3>

```
uv self uninstall [OPTIONS]
```

<h3 class="cli-reference">Options</h3>

<dl class="cli-reference"><dt><code>--allow-insecure-host</code> <i>allow-insecure-host</i></dt><dd><p>Allow insecure connections to a host.</p>

<p>Can be provided multiple times.</p>

<p>Expects to receive either a hostname (e.g., <code>localhost</code>), a host-port pair (e.g., <code>localhost:8080</code>), or a URL (e.g., <code>https://localhost</code>).</p>

<p>WARNING: Hosts included in this list will not be verified against the system&#8217;s certificate store. Only use <code>--allow-insecure-host</code> in a secure network with verified sources, as it bypasses SSL verification and could expose you to MITM attacks.</p>

<p>May also be set with the <code>UV_INSECURE_HOST</code> environment variable.</p>
</dd><dt><code>--cache-dir</code> <i>cache-dir</i></dt><dd><p>Path to the cache directory.</p>

<p>Defaults to <code>$XDG_CACHE_HOME/uv</code> or <code>$HOME/.cache/uv</code> on macOS and Linux, and <code>%LOCALAPPDATA%\uv\cache</code> on Windows.</p>

<p>To view the location of the cache directory, run <code>uv cache dir</code>.</p>

<p>May also be set with the <code>UV_CACHE_DIR</code> environment variable.</p>
</dd><dt><code>--color</code> <i>color-choice</i></dt><dd><p>Control the use of color in output.</p>

<p>By default, uv will automatically detect support for colors when writing to a terminal.</p>

<p>Possible values:</p>

<ul>
<li><code>auto</code>: Enables colored output only when the output is going to a terminal or TTY with support</li>

<li><code>always</code>: Enables colored output regardless of the detected environment</li>

<li><code>never</code>: Disables colored output</li>
</ul>
</dd><dt><code>--config-file</code> <i>config-file</i></dt><dd><p>The path to a <code>uv.toml</code> file to use for configuration.</p>

<p>While uv configuration can be included in a <code>pyproject.toml</code> file, it is not allowed in this context.</p>

<p>May also be set with the <code>UV_CONFIG_FILE</code> environment variable.</p>
</dd><dt><code>--directory</code> <i>directory</i></dt><dd><p>Change to the given directory prior to running the command.</p>

<p>Relative paths are resolved with the given directory as the base.</p>

<p>See <code>--project</code> to only change the project root directory.</p>

</dd><dt><code>--help</code>, <code>-h</code></dt><dd><p>Display the concise help for this command</p>

</dd><dt><code>--native-tls</code></dt><dd><p>Whether to load TLS certificates from the platform&#8217;s native certificate store.</p>

<p>By default, uv loads certificates from the bundled <code>webpki-roots</code> crate. The <code>webpki-roots</code> are a reliable set of trust roots from Mozilla, and including them in uv improves portability and performance (especially on macOS).</p>

<p>However, in some cases, you may want to use the platform&#8217;s native certificate store, especially if you&#8217;re relying on a corporate trust root (e.g., for a mandatory proxy) that&#8217;s included in your system&#8217;s certificate store.</p>

<p>May also be set with the <code>UV_NATIVE_TLS</code> environment variable.</p>
</dd><dt><code>--no-cache</code>, <code>-n</code></dt><dd><p>Avoid reading from or writing to the cache, instead using a temporary directory for the duration of the operation</p>

<p>May also be set with the <code>UV_NO_CACHE</code> environment variable.</p>
</dd><dt><code>--no-config</code></dt><dd><p>Avoid discovering configuration files (<code>pyproject.toml</code>, <code>uv.toml</code>).</p>

<p>Normally, configuration files are discovered in the current directory, parent directories, or user configuration directories.</p>

<p>May also be set with the <code>UV_NO_CONFIG</code> environment variable.</p>
</dd><dt><code>--no-progress</code></dt><dd><p>Hide all progress outputs.</p>

<p>For example, spinners or progress bars.</p>

<p>May also be set with the <code>UV_NO_PROGRESS</code> environment variable.</p>
</dd><dt><code>--no-python-downloads</code></dt><dd><p>Disable automatic downloads of Python.</p>

</dd><dt><code>--offline</code></dt><dd><p>Disable network access.</p>

<p>When disabled, uv will only use locally cached data and locally available files.</p>

<p>May also be set with the <code>UV_OFFLINE</code> environment variable.</p>
</dd><dt><code>--project</code> <i>project</i></dt><dd><p>Run the command within the given project directory.</p>

<p>All <code>pyproject.toml</code>, <code>uv.toml</code>, and <code>.python-version</code> files will be discovered by walking up the directory tree from the project root, as will the project&#8217;s virtual environment (<code>.venv</code>).</p>

<p>Other command-line arguments (such as relative paths) will be resolved relative to the current working directory.</p>

<p>See <code>--directory</code> to change the working directory entirely.</p>

<p>This setting has no effect when used in the <code>uv pip</code> interface.</p>

</dd><dt><code>--python-preference</code> <i>python-preference</i></dt><dd><p>Whether to prefer uv-managed or system Python installations.</p>

<p>By default, uv prefers using Python versions it manages. However, it will use system Python installations if a uv-managed Python is not installed. This option allows prioritizing or ignoring system Python installations.</p>

<p>May also be set with the <code>UV_PYTHON_PREFERENCE</code> environment variable.</p>
<p>Possible values:</p>

<ul>
<li><code>only-managed</code>: Only use managed Python installations; never use system Python installations</li>

<li><code>managed</code>: Prefer managed Python installations over system Python installations</li>

<li><code>system</code>: Prefer system Python installations over managed Python installations</li>

<li><code>only-system</code>: Only use system Python installations; never use managed Python installations</li>
</ul>
</dd><dt><code>--quiet</code>, <code>-q</code></dt><dd><p>Do not print any output</p>

</dd><dt><code>--remove-data</code></dt><dd><p>true iff should run <code>uv cache clean</code> and remove directories pointed to by <code>uv python dir</code> and <code>uv tool dir</code></p>

</dd><dt><code>--verbose</code>, <code>-v</code></dt><dd><p>Use verbose output.</p>

<p>You can configure fine-grained logging using the <code>RUST_LOG</code> environment variable. (&lt;https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives&gt;)</p>

</dd><dt><code>--version</code>, <code>-V</code></dt><dd><p>Display the uv version</p>

</dd></dl>

## uv version

Display uv's version
Expand Down
Loading