From a0febffc72abb58ad8aabd7177bc19ddfd9ffb85 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Thu, 15 Aug 2024 23:19:59 -0400 Subject: [PATCH] Warn when --upgrade is passed to tool run --- crates/uv-cache/src/lib.rs | 5 ++ crates/uv/src/commands/project/environment.rs | 2 +- crates/uv/src/commands/tool/run.rs | 6 +- crates/uv/src/lib.rs | 2 +- crates/uv/src/settings.rs | 17 +++++- crates/uv/tests/tool_run.rs | 58 +++++++++++-------- 6 files changed, 59 insertions(+), 31 deletions(-) diff --git a/crates/uv-cache/src/lib.rs b/crates/uv-cache/src/lib.rs index e5dc874114fc..5d76ad73815d 100644 --- a/crates/uv-cache/src/lib.rs +++ b/crates/uv-cache/src/lib.rs @@ -154,6 +154,11 @@ impl Cache { &self.root } + /// Return the [`Refresh`] policy for the cache. + pub fn refresh(&self) -> &Refresh { + &self.refresh + } + /// The folder for a specific cache bucket pub fn bucket(&self, cache_bucket: CacheBucket) -> PathBuf { self.root.join(cache_bucket.to_str()) diff --git a/crates/uv/src/commands/project/environment.rs b/crates/uv/src/commands/project/environment.rs index a06bae10fabf..ef47530897eb 100644 --- a/crates/uv/src/commands/project/environment.rs +++ b/crates/uv/src/commands/project/environment.rs @@ -89,7 +89,7 @@ impl CachedEnvironment { // Search in the content-addressed cache. let cache_entry = cache.entry(CacheBucket::Environments, interpreter_hash, resolution_hash); - if settings.reinstall.is_none() { + if cache.refresh().is_none() { if let Ok(root) = fs_err::read_link(cache_entry.path()) { if let Ok(environment) = PythonEnvironment::from_root(root, cache) { return Ok(Self(environment)); diff --git a/crates/uv/src/commands/tool/run.rs b/crates/uv/src/commands/tool/run.rs index 83afce385677..6880feac20ff 100644 --- a/crates/uv/src/commands/tool/run.rs +++ b/crates/uv/src/commands/tool/run.rs @@ -499,11 +499,7 @@ async fn get_or_create_environment( }; // Check if the tool is already installed in a compatible environment. - if !isolated - && !target.is_latest() - && settings.reinstall.is_none() - && settings.upgrade.is_none() - { + if !isolated && !target.is_latest() { let installed_tools = InstalledTools::from_settings()?.init()?; let _lock = installed_tools.acquire_lock()?; diff --git a/crates/uv/src/lib.rs b/crates/uv/src/lib.rs index c3f12e6071ad..f25e4008a229 100644 --- a/crates/uv/src/lib.rs +++ b/crates/uv/src/lib.rs @@ -738,7 +738,7 @@ async fn run(cli: Cli) -> Result { }; // Resolve the settings from the command-line arguments and workspace configuration. - let args = settings::ToolRunSettings::resolve(args, filesystem); + let args = settings::ToolRunSettings::resolve(args, filesystem, invocation_source); show_settings!(args); // Initialize the cache. diff --git a/crates/uv/src/settings.rs b/crates/uv/src/settings.rs index 0f290a47b73b..900ec63e0d95 100644 --- a/crates/uv/src/settings.rs +++ b/crates/uv/src/settings.rs @@ -36,6 +36,7 @@ use uv_warnings::warn_user_once; use uv_workspace::pyproject::DependencyType; use crate::commands::pip::operations::Modifications; +use crate::commands::ToolRunCommand; /// The resolved global settings to use for any invocation of the CLI. #[allow(clippy::struct_excessive_bools)] @@ -275,7 +276,11 @@ pub(crate) struct ToolRunSettings { impl ToolRunSettings { /// Resolve the [`ToolRunSettings`] from the CLI and filesystem configuration. #[allow(clippy::needless_pass_by_value)] - pub(crate) fn resolve(args: ToolRunArgs, filesystem: Option) -> Self { + pub(crate) fn resolve( + args: ToolRunArgs, + filesystem: Option, + invocation_source: ToolRunCommand, + ) -> Self { let ToolRunArgs { command, from, @@ -289,6 +294,16 @@ impl ToolRunSettings { python, } = args; + // If `--upgrade` was passed explicitly, warn. + if installer.upgrade || !installer.upgrade_package.is_empty() { + warn_user_once!("Tools cannot be upgraded via `{invocation_source}`; use `uv tool upgrade --all` to upgrade all installed tools, or `{invocation_source} package@latest` to run the latest version of a tool"); + } + + // If `--reinstall` was passed explicitly, warn. + if installer.reinstall || !installer.reinstall_package.is_empty() { + warn_user_once!("Tools cannot be reinstalled via `{invocation_source}`; use `uv tool upgrade --reinstall` to reinstall all installed tools, or `{invocation_source} package@latest` to run the latest version of a tool"); + } + Self { command, from, diff --git a/crates/uv/tests/tool_run.rs b/crates/uv/tests/tool_run.rs index 6d1d3f7ca8c1..87b24a04e760 100644 --- a/crates/uv/tests/tool_run.rs +++ b/crates/uv/tests/tool_run.rs @@ -370,25 +370,6 @@ fn tool_run_from_install() { + platformdirs==4.2.0 "###); - // Verify that `--upgrade` resolves a fresh isolated environment (but reuses the cached - // environment resolved in the previous step). - uv_snapshot!(context.filters(), context.tool_run() - .arg("--upgrade") - .arg("black") - .arg("--version") - .env("UV_TOOL_DIR", tool_dir.as_os_str()) - .env("XDG_BIN_HOME", bin_dir.as_os_str()), @r###" - success: true - exit_code: 0 - ----- stdout ----- - black, 24.3.0 (compiled: yes) - Python (CPython) 3.12.[X] - - ----- stderr ----- - warning: `uv tool run` is experimental and may change without warning - Resolved [N] packages in [TIME] - "###); - // Verify that `tool run black` at a different version installs the new version. uv_snapshot!(context.filters(), context.tool_run() .arg("black@24.1.1") @@ -524,11 +505,11 @@ fn tool_run_cache() { Resolved [N] packages in [TIME] "###); - // Verify that `--reinstall` reinstalls everything. + // Verify that `--refresh` recreates everything. uv_snapshot!(context.filters(), context.tool_run() .arg("-p") .arg("3.12") - .arg("--reinstall") + .arg("--refresh") .arg("black") .arg("--version") .env("UV_TOOL_DIR", tool_dir.as_os_str()) @@ -552,11 +533,11 @@ fn tool_run_cache() { + platformdirs==4.2.0 "###); - // Verify that `--reinstall-package` reinstalls everything. We may want to change this. + // Verify that `--refresh-package` recreates everything. We may want to change this. uv_snapshot!(context.filters(), context.tool_run() .arg("-p") .arg("3.12") - .arg("--reinstall-package") + .arg("--refresh-package") .arg("packaging") .arg("black") .arg("--version") @@ -900,6 +881,37 @@ fn warn_no_executables_found() { "###); } +/// Warn when a user passes `--upgrade` to `uv tool run`. +#[test] +fn tool_run_upgrade_warn() { + let context = TestContext::new("3.12").with_filtered_counts(); + let tool_dir = context.temp_dir.child("tools"); + let bin_dir = context.temp_dir.child("bin"); + + uv_snapshot!(context.filters(), context.tool_run() + .arg("--upgrade") + .arg("pytest") + .arg("--version") + .env("UV_TOOL_DIR", tool_dir.as_os_str()) + .env("XDG_BIN_HOME", bin_dir.as_os_str()), @r###" + success: true + exit_code: 0 + ----- stdout ----- + pytest 8.1.1 + + ----- stderr ----- + warning: Tools cannot be upgraded via `uv tool run`; use `uv tool upgrade --all` to upgrade all installed tools, or `uv tool run package@latest` to run the latest version of a tool + warning: `uv tool run` is experimental and may change without warning + Resolved [N] packages in [TIME] + Prepared [N] packages in [TIME] + Installed [N] packages in [TIME] + + iniconfig==2.0.0 + + packaging==24.0 + + pluggy==1.4.0 + + pytest==8.1.1 + "###); +} + /// If we fail to resolve the tool, we should include "tool" in the error message. #[test] fn tool_run_resolution_error() {