From 5498a86b2a4ee10f90af159178cb17792d027efa Mon Sep 17 00:00:00 2001 From: JamBalaya56562 Date: Sat, 13 Jun 2026 22:38:43 +0900 Subject: [PATCH] fix(activate): don't reorder PATH for the mise dir in --shims mode `mise activate --shims` prepended both the shims dir and the directory containing the mise executable unconditionally (PR #8757 dropped the "already in PATH" guard so the shims dir reliably moves to the front on re-source). For a deb/rpm install the mise binary lives at /usr/bin, so activation emitted `export PATH="/usr/bin:$PATH"`, duplicating /usr/bin and moving it ahead of /usr/local/bin -- changing command resolution (#10264). Keep always-prepending the shims dir (preserving #8757's re-source behavior), but emit the mise executable's own dir through the guarded prepend_path, which skips dirs already in PATH. The exe dir only needs to be present so `mise` is callable, never at the front. For a deb install, activation now emits only the shims line. Addresses discussion #10264. Co-Authored-By: Claude Opus 4.8 (1M context) --- e2e/shell/test_shims_activate_prepend | 18 ++++++++++++++++++ src/cli/activate.rs | 19 ++++++++++++------- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/e2e/shell/test_shims_activate_prepend b/e2e/shell/test_shims_activate_prepend index 236d9f6694..afac8c9723 100644 --- a/e2e/shell/test_shims_activate_prepend +++ b/e2e/shell/test_shims_activate_prepend @@ -14,3 +14,21 @@ assert_contains "env PATH='$CUSTOM_PATH' $MISE_BIN activate bash --shims" "expor # Shims not in PATH: should also be emitted as a prepend with shims first assert_contains "env PATH='/some/other/bin:/usr/bin' $MISE_BIN activate bash --shims" "export PATH=\"$SHIMS_DIR:" + +# Regression #10264: when the dir containing the mise executable is already in +# PATH (e.g. a deb install at /usr/bin), --shims must NOT re-prepend it, which +# would reorder PATH (moving /usr/bin ahead of /usr/local/bin). Only the shims +# dir should be prepended, so exactly one `export PATH=` line is emitted. +EXE_DIR="$(dirname "$MISE_BIN")" +if ! out="$(env PATH="$EXE_DIR:/usr/local/bin:/usr/bin" "$MISE_BIN" activate bash --shims 2>&1)"; then + echo "FAIL: 'mise activate bash --shims' exited non-zero:" + printf '%s\n' "$out" + exit 1 +fi +assert_contains_text "$out" "export PATH=\"$SHIMS_DIR:" +path_lines="$(printf '%s\n' "$out" | grep -c 'export PATH=' || true)" +if [[ $path_lines != "1" ]]; then + echo "FAIL: expected exactly one 'export PATH=' line (shims only) since the mise dir is already in PATH, got $path_lines:" + printf '%s\n' "$out" + exit 1 +fi diff --git a/src/cli/activate.rs b/src/cli/activate.rs index 6b2b6c0ff0..924afc7353 100644 --- a/src/cli/activate.rs +++ b/src/cli/activate.rs @@ -102,10 +102,14 @@ impl Activate { fn activate_shims(&self, shell: &dyn Shell, mise_bin: &Path) -> std::io::Result<()> { let exe_dir = mise_bin.parent().unwrap(); let mut prelude = vec![]; - // For shells with native path dedup/reorder (fish), always emit path commands - // using MovePrependEnv so entries get moved to front on re-source (e.g. VS Code). - // For other shells, keep the is_dir_in_path guard to avoid PATH growth on re-source. - if let Some(p) = self.shims_prepend_path(shell, exe_dir) { + // The shims dir is always (move-)prepended so it stays at the front of PATH + // even when activation is re-sourced (e.g. VS Code terminals) — see #8757. + // The mise executable's own dir only needs to be present so `mise` is + // callable, so it uses the guarded prepend: this avoids re-prepending (and + // thereby reordering) a system dir such as /usr/bin that is already in PATH + // for deb/rpm installs, which would otherwise move it ahead of + // /usr/local/bin (#10264). + if let Some(p) = self.prepend_path(exe_dir) { prelude.push(p); } if let Some(p) = self.shims_prepend_path(shell, &dirs::SHIMS) { @@ -165,9 +169,10 @@ impl Activate { } } - /// Used by activate_shims. Always prepends the path to the front, even if - /// already present (accepting a duplicate entry). For shells with native path - /// dedup (fish), uses MovePrependEnv to reorder without duplicating. + /// Used by activate_shims for the shims directory. Always prepends the path to + /// the front, even if already present (accepting a duplicate entry), so the + /// shims dir wins on re-source. For shells with native path dedup (fish), uses + /// MovePrependEnv to reorder without duplicating. fn shims_prepend_path(&self, shell: &dyn Shell, p: &Path) -> Option { if !is_dir_not_in_nix(p) || p.is_relative() { return None;