Skip to content

fix(env): preserve user-configured PATH entries from env._.path#6835

Merged
jdx merged 5 commits intomainfrom
fix-env-path-ordering
Oct 31, 2025
Merged

fix(env): preserve user-configured PATH entries from env._.path#6835
jdx merged 5 commits intomainfrom
fix-env-path-ordering

Conversation

@jdx
Copy link
Copy Markdown
Owner

@jdx jdx commented Oct 31, 2025

Summary

Fixes a regression introduced in f4dca1b where user-configured paths from env._.path were being filtered out if they already existed in __MISE_ORIG_PATH. This caused incorrect PATH ordering when users explicitly configured paths to ensure precedence.

Problem

When a user configured env._.path to prepend a directory that was already in their original PATH (e.g., /opt/homebrew/bin), the filtering logic would remove it, breaking the user's intended PATH ordering.

Example:

  • Original PATH: /usr/bin:/opt/homebrew/bin
  • User configures: env._.path = ["/opt/homebrew/bin"]
  • Expected: /opt/homebrew/bin comes first
  • Actual (bug): /opt/homebrew/bin stays in original position (after /usr/bin)

Reproduction:

# User has /opt/homebrew/bin in their shell PATH
export PATH="/usr/bin:/bin:/opt/homebrew/bin"

# User creates mise config to ensure homebrew comes first
cat > mise.macos.toml <<EOF
[env]
_.path = ["/opt/homebrew/bin"]
EOF

# After activating mise, /opt/homebrew/bin should be first
# Bug: it stays in its original position
which rsync  # Returns /usr/bin/rsync (wrong, should be /opt/homebrew/bin/rsync)

Solution

Separate path handling into two categories:

  1. User-configured paths (from env._.path via config.path_dirs()) - filtered only against pre (user manual additions after mise activation), never against post (__MISE_ORIG_PATH)
  2. Tool installation paths - filtered if they duplicate original PATH entries (preserves the fix from f4dca1b)

The key insight: We need to avoid duplicates when users manually add paths to PATH after mise activation, but we should NOT filter against the original PATH before mise activation. This preserves the user's intended ordering from configuration while preventing unbounded PATH growth.

Added list_final_paths_split() to return paths separately, and updated build_path_operations() to only filter tool paths against the original PATH, while user-configured paths are only filtered against user manual additions.

Changes

  • src/toolset/mod.rs: Add list_final_paths_split() method to separate user vs tool paths
  • src/cli/hook_env.rs:
    • Update build_path_operations() to accept separate user/tool paths
    • Filter tool paths against original PATH (as before)
    • Filter user paths only against pre (user manual additions), NOT against post (__MISE_ORIG_PATH)
  • e2e/config/test_env_path_ordering: Add regression test that verifies user-configured paths take precedence
  • e2e/cli/test_deactivate: Update test assertion to reflect correct behavior where user-configured paths are always added even if they exist elsewhere in PATH

Test Plan

# Test the original bug fix
mise run test:e2e config/test_env_path_ordering

# Test reactivation behavior
mise run test:e2e cli/test_deactivate

The tests verify:

  1. User can configure paths via env._.path
  2. Those paths take precedence even if already in original PATH before mise activation
  3. User-specified ordering is preserved
  4. On reactivation after deactivation, user-configured paths are added from config while user manual additions are preserved

🤖 Generated with Claude Code


Note

Separates user-configured and tool PATH entries to keep env._.path precedence, updates PATH/DIRENV_DIFF logic, and adds a regression test with adjusted deactivate assertions.

  • Env/Path handling:
    • Introduce Toolset::list_final_paths_split() to return (user_paths, tool_paths).
    • Update hook_env to use split paths for __MISE_DIFF and PATH construction.
    • Rework build_path_operations(...) to:
      • Filter tool_paths against original PATH and user-added pre paths.
      • Filter user_paths only against pre (not original PATH) to preserve configured precedence.
      • Build PATH as: pre -> user_paths -> tool_paths -> post and update DIRENV_DIFF accordingly.
  • Tests:
    • Add e2e/config/test_env_path_ordering to verify env._.path precedence over existing PATH.
    • Update e2e/cli/test_deactivate to expect shared_entry count of 3 after reactivation and clarify behavior comments.

Written by Cursor Bugbot for commit 9862ca8. This will update automatically on new commits. Configure here.

Fixes a regression introduced in f4dca1b where user-configured paths
from env._.path were being filtered out if they already existed in
__MISE_ORIG_PATH. This caused incorrect PATH ordering when users
explicitly configured paths to ensure precedence.

## Problem

When a user configured env._.path to prepend a directory that was
already in their original PATH (e.g., /opt/homebrew/bin), the filtering
logic would remove it, breaking the user's intended PATH ordering.

Example:
- Original PATH: /usr/bin:/opt/homebrew/bin
- User configures: env._.path = ["/opt/homebrew/bin"]
- Expected: /opt/homebrew/bin:/usr/bin:/opt/homebrew/bin
- Actual (bug): /usr/bin:/opt/homebrew/bin

## Solution

Separate path handling into two categories:
1. User-configured paths (from env._.path) - never filtered, always prepended
2. Tool installation paths - filtered if they duplicate original PATH entries

Added list_final_paths_split() to return paths separately, and updated
build_path_operations() to only filter tool paths while preserving
user-configured paths.

## Changes

- src/toolset/mod.rs: Add list_final_paths_split() method
- src/cli/hook_env.rs: Update build_path_operations() to handle paths separately
- e2e/config/test_env_path_ordering: Add regression test

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings October 31, 2025 22:57
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR fixes a regression where user-configured paths from env._.path were being incorrectly filtered out when they already existed in the original PATH, breaking intended PATH ordering for explicit user configurations.

  • Separates path handling into user-configured paths (never filtered) and tool installation paths (filtered for duplicates)
  • Updates path building logic to preserve user-specified ordering while maintaining the existing duplicate filtering for tool paths
  • Adds comprehensive regression test to verify PATH ordering behavior

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
src/toolset/mod.rs Adds list_final_paths_split() method to separate user-configured paths from tool paths
src/cli/hook_env.rs Updates path operations to handle user and tool paths separately, only filtering tool paths
e2e/config/test_env_path_ordering Adds regression test verifying user-configured PATH ordering takes precedence

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/cli/hook_env.rs
Comment on lines +226 to 227
// Filter out tool paths that are already in the original PATH (post) or
// in the pre paths (user additions). This prevents mise from claiming ownership
Copy link

Copilot AI Oct 31, 2025

Choose a reason for hiding this comment

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

The comment should be updated to reflect that this filtering now only applies to tool paths, not all paths as it previously did.

Suggested change
// Filter out tool paths that are already in the original PATH (post) or
// in the pre paths (user additions). This prevents mise from claiming ownership
// Filter out tool paths (not all paths) that are already in the original PATH (post) or
// in the pre paths (user additions). This filtering now only applies to tool paths,
// not all paths as it previously did. This prevents mise from claiming ownership

Copilot uses AI. Check for mistakes.
eval "$(mise hook-env)"

echo "DEBUG: Final PATH=$PATH"
echo "DEBUG: __MISE_ORIG_PATH=${__MISE_ORIG_PATH:-not set}"
Copy link

Copilot AI Oct 31, 2025

Choose a reason for hiding this comment

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

Debug output statements should be removed from the test file as they add noise to test execution and aren't needed for the test assertion logic.

Suggested change
echo "DEBUG: __MISE_ORIG_PATH=${__MISE_ORIG_PATH:-not set}"

Copilot uses AI. Check for mistakes.

# Verify the PATH order by checking which comes first
WHICH_OUTPUT=$(which test-tool)
echo "DEBUG: which test-tool = $WHICH_OUTPUT"
Copy link

Copilot AI Oct 31, 2025

Choose a reason for hiding this comment

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

Debug output statement should be removed from the test file as it adds noise to test execution and isn't needed for the test assertion logic.

Suggested change
echo "DEBUG: which test-tool = $WHICH_OUTPUT"

Copilot uses AI. Check for mistakes.
@jdx jdx enabled auto-merge (squash) October 31, 2025 22:59
cursor[bot]

This comment was marked as outdated.

env_results.env_paths (tool-stage _.path) must come BEFORE config.path_dirs()
to maintain the original behavior from list_final_paths() where env_results.env_paths
had highest precedence (was prepended at the front).

The previous implementation reversed this by appending env_results.env_paths
after config.path_dirs(), breaking the expected PATH ordering.

Co-Authored-By: Claude <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Oct 31, 2025

Hyperfine Performance

mise x -- echo

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2025.10.21 x -- echo 18.5 ± 0.4 17.9 20.4 1.00
mise x -- echo 18.9 ± 2.1 18.0 61.3 1.02 ± 0.11

mise env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2025.10.21 env 18.0 ± 0.4 17.5 19.6 1.00
mise env 18.2 ± 0.5 17.5 21.9 1.01 ± 0.03

mise hook-env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2025.10.21 hook-env 18.2 ± 0.5 17.6 21.4 1.00
mise hook-env 18.3 ± 0.4 17.6 21.4 1.00 ± 0.04

mise ls

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2025.10.21 ls 15.8 ± 0.4 15.1 17.2 1.00 ± 0.03
mise ls 15.8 ± 0.2 15.3 17.5 1.00

xtasks/test/perf

Command mise-2025.10.21 mise Variance
install (cached) 105ms 105ms +0%
ls (cached) 64ms 64ms +0%
bin-paths (cached) 70ms 70ms +0%
task-ls (cached) 426ms ⚠️ 481ms -11%

⚠️ Warning: task-ls cached performance variance is -11%

On reactivation, user additions may be in __MISE_ORIG_PATH (post), so we need
to filter user_paths against the full current PATH (pre + post) to avoid duplicates.

This is a trade-off: it may filter out some user-configured paths that are in
__MISE_ORIG_PATH during initial activation, but it avoids creating duplicate
paths on reactivation.

Co-Authored-By: Claude <noreply@anthropic.com>
@jdx jdx disabled auto-merge October 31, 2025 23:20
cursor[bot]

This comment was marked as outdated.

jdx and others added 2 commits October 31, 2025 18:23
The previous implementation filtered user_paths against the full current PATH
(pre + post), which included __MISE_ORIG_PATH. This broke the intended fix
where user-configured paths from env._.path should take precedence even if
they already exist in the original PATH.

The correct behavior is:
- Filter user_paths against `pre` (user manual additions after mise activation)
  to avoid duplicates when users manually modify PATH
- Do NOT filter against `post` (__MISE_ORIG_PATH) to preserve user's intended
  PATH ordering from configuration

Also updated cli/test_deactivate to reflect correct behavior: on reactivation,
shared_entry should appear 3 times (1 from mise config + 2 from user additions),
not 2 as the old buggy implementation enforced.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Fixed 3 misleading comments:
1. Line 82: Order doesn't matter for __MISE_DIFF (only used for set membership)
2. Line 182: user_paths ARE filtered (against pre, not post)
3. Lines 235-237: Clarified that tool_paths are filtered against post, and
   user_paths are filtered separately against pre

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@jdx jdx enabled auto-merge (squash) October 31, 2025 23:33
@jdx jdx merged commit 0aa6ac0 into main Oct 31, 2025
29 checks passed
@jdx jdx deleted the fix-env-path-ordering branch October 31, 2025 23:41
jdx pushed a commit that referenced this pull request Nov 1, 2025
### 🐛 Bug Fixes

- **(activate)** reset PATH when activate is called multiple times by
@jdx in [#6829](#6829)
- **(env)** preserve user-configured PATH entries from env._.path by
@jdx in [#6835](#6835)
- store tool options for all backends in metadata by @roele in
[#6807](#6807)

### 📚 Documentation

- fix usage spec syntax from 'option' to 'flag' by @jdx in
[#6834](#6834)

### 📦️ Dependency Updates

- update ghcr.io/jdx/mise:alpine docker digest to 7351bbe by
@renovate[bot] in [#6826](#6826)
- update ghcr.io/jdx/mise:deb docker digest to 3a847f2 by @renovate[bot]
in [#6828](#6828)
- update ghcr.io/jdx/mise:copr docker digest to 546dffb by
@renovate[bot] in [#6827](#6827)
- pin jdx/mise-action action to e3d7b8d by @renovate[bot] in
[#6825](#6825)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants