diff --git a/e2e/cli/test_upgrade b/e2e/cli/test_upgrade index 5bed74816e..aca9846e4b 100644 --- a/e2e/cli/test_upgrade +++ b/e2e/cli/test_upgrade @@ -70,6 +70,44 @@ assert "ls $MISE_DATA_DIR/installs/dummy/1.0.0" mise up assert_contains "mise up --dry-run-code 2>&1" "All tools are up to date" +# Test: upgrade should not re-download a version that is already installed +# This verifies the fix for https://github.com/jdx/mise/discussions/8260 +cat <mise.toml +[tools] +dummy = "1" +EOF +mise uninstall dummy --all +# Install 1.0.0 first (old version), then also install 1.1.0 (the latest 1.x) +mise install dummy@1.0.0 +mise install dummy@1.1.0 +assert_contains "mise ls --installed dummy" "1.0.0" +assert_contains "mise ls --installed dummy" "1.1.0" +# upgrade should report "All tools are up to date" since 1.1.0 is already installed +assert_contains "mise up 2>&1" "All tools are up to date" + +# Test: upgrade with lockfile should update the lockfile to the new version +cat <mise.toml +[tools] +dummy = "1" +EOF +mise uninstall dummy --all +mise install dummy@1.0.0 +# Write a lockfile pinning to 1.0.0 +cat <mise.lock +[[tools.dummy]] +version = "1.0.0" +backend = "asdf:dummy" +EOF +# Verify the lockfile pins to 1.0.0 +assert "mise ls dummy" "dummy 1.0.0 ~/workdir/mise.toml 1" +# Upgrade should install 1.1.0 and update the lockfile +mise up +assert "mise ls dummy" "dummy 1.1.0 ~/workdir/mise.toml 1" +# Verify the lockfile was updated to 1.1.0 +assert_contains "cat mise.lock" "1.1.0" +assert_not_contains "cat mise.lock" "1.0.0" +rm -f mise.lock + # Test that upgrading a specific tool doesn't check other tools # This verifies the fix for https://github.com/jdx/mise/discussions/7328 cat <mise.toml diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 8089e5f040..18b9019aa9 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -563,6 +563,17 @@ pub trait Backend: Debug + Send + Sync { if let Some(install_path) = tv.request.install_path(config) && check_path(&install_path, true) { + // For Prefix requests, install_path finds any installed dir + // matching the prefix (e.g., "1.0.0" for prefix "1"), but if + // the ToolVersion resolved to a different version (e.g., "1.1.0"), + // we must not treat it as installed. + if let ToolRequest::Prefix { .. } = &tv.request + && install_path + .file_name() + .is_some_and(|f| f.to_string_lossy() != tv.version) + { + return check_path(&tv.install_path(), check_symlink); + } return true; } check_path(&tv.install_path(), check_symlink) diff --git a/src/cli/upgrade.rs b/src/cli/upgrade.rs index 463c4bd3e3..fcb0130a2a 100644 --- a/src/cli/upgrade.rs +++ b/src/cli/upgrade.rs @@ -210,8 +210,7 @@ impl Upgrade { let opts = InstallOptions { reason: "upgrade".to_string(), - // TODO: can we remove this without breaking e2e/cli/test_upgrade? it may be causing tools to re-install - force: true, + force: false, jobs: self.jobs, raw: self.raw, resolve_options: ResolveOptions {