Skip to content

fix(install): don't warn for configured tools when version is passed via CLI#9522

Merged
jdx merged 6 commits intomainfrom
claude/gracious-ishizaka-fe51c6
May 1, 2026
Merged

fix(install): don't warn for configured tools when version is passed via CLI#9522
jdx merged 6 commits intomainfrom
claude/gracious-ishizaka-fe51c6

Conversation

@jdx
Copy link
Copy Markdown
Owner

@jdx jdx commented May 1, 2026

Summary

  • Fixes #9501: mise install ruby@latest warns "ruby installed but not activated — it is not in any config file" even when ruby is in the user's mise config
  • Determines "configured" by walking config.config_files directly, instead of reading trs.sources after CLI args have been merged in

Why

Install::run writes CLI args into env::TOOL_ARGS before the tool request set is built. ToolRequestSetBuilder::load_runtime_args then merges those args back in with ToolSource::Argument, and merge (src/toolset/tool_request_set.rs:279) gives the right-hand-side precedence — overwriting the MiseToml source for the tool. The inactive-tool check then sees ToolSource::Argument and falsely fires for any TOOL@VERSION form, regardless of whether the tool is configured.

The bug only triggers when an explicit version is passed (ruby@latest, [email protected]); mise install ruby (no version) is unaffected because the merge skips that branch — which is why the existing e2e test missed it.

Test plan

  • Added regression cases to e2e/cli/test_install_inactive_hint covering both [email protected] and dummy@latest against a configured tool
  • mise run test:e2e test_install_inactive_hint passes
  • mise run test:e2e test_install_dry_run still passes
  • cargo build and cargo clippy --all-targets -- -D warnings clean

🤖 Generated with Claude Code


Note

Low Risk
Low risk: changes only the logic that decides when to emit the “installed but not activated” warning, plus adds focused unit/e2e regression tests.

Overview
Fixes false “not activated / not in any config file” warnings from mise install TOOL@VERSION by determining “configured” tools via config files and MISE_<TOOL>_VERSION env vars instead of ToolRequestSet.sources (which gets overwritten by CLI arg merging).

Introduces a reusable tool_env_vars() helper (with backend unaliasing and skip rules) and adds regression coverage in both e2e (test_install_inactive_hint) and unit tests for env-var parsing.

Reviewed by Cursor Bugbot for commit 516d513. Bugbot is set up for automated code reviews on this repo. Configure here.

…via CLI

`mise install ruby@latest` was incorrectly showing
"ruby installed but not activated — it is not in any config file"
even when ruby is in a mise config file.

`Install::run` writes CLI args into `env::TOOL_ARGS` before the tool
request set is built. `ToolRequestSetBuilder::load_runtime_args` then
merges those args back in with `ToolSource::Argument`, and `merge` gives
the right-hand-side precedence — overwriting the `MiseToml` source for
the tool. The inactive-tool check then sees `ToolSource::Argument` and
fires for any `TOOL@VERSION` form, regardless of whether the tool is
actually configured.

Determine "configured" by walking `config.config_files` directly instead
of reading the merged source map, and add regression coverage for both
`[email protected]` and `dummy@latest` against a configured tool.

Fixes #9501

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 1, 2026

Greptile Summary

Fixes a false-positive "not activated" warning in mise install TOOL@VERSION when the tool is already in the user's config. The root cause was that load_runtime_args overwrites the ToolSource for any TOOL@VERSION CLI arg with ToolSource::Argument, making config-backed tools indistinguishable from CLI-only ones when reading trs.sources. The fix builds a configured_tools set by scanning config.config_files and MISE_<TOOL>_VERSION env vars directly (via the new public tool_env_vars() helper), bypassing the corrupted source map entirely.

Confidence Score: 5/5

Safe to merge — targeted bug fix with regression tests; only affects when the inactive-tool hint fires, no behavioral change for the install itself.

No P0 or P1 issues found. The fix correctly addresses the root cause by bypassing the corrupted trs.sources and reading config directly. The new tool_env_vars() helper is well-tested (alias resolution and hook-var exclusion both covered). The single P2 note is a minor performance observation that does not affect correctness.

No files require special attention.

Important Files Changed

Filename Overview
src/cli/install.rs Replaces trs.sources-based inactive-tool detection with a config-file + env-var scan, correctly fixing the false-positive warning when TOOL@VERSION is passed for a configured tool.
src/toolset/tool_request_set.rs Extracts tool_env_vars() public helper that unaliases backend names (e.g. "nodejs" → "node"); load_runtime_env now delegates to it, and new unit tests verify alias resolution and hook-var skipping.
src/toolset/mod.rs Re-exports tool_env_vars from the toolset public API; trivial one-line change.
e2e/cli/test_install_inactive_hint Adds regression cases for TOOL@VERSION and TOOL@latest for configured tools, and env-var-configured tools; tee-to-file pattern for env-var tests is slightly indirect but functional.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["mise install TOOL@VERSION"] --> B["Write CLI args to env::TOOL_ARGS"]
    B --> C["Build ToolRequestSet via ToolRequestSetBuilder"]
    C --> D["load_runtime_args merges CLI args\nwith ToolSource::Argument\n(overwrites MiseToml source)"]
    D --> E["install_runtimes called"]
    E --> F["Build configured_tools set (NEW)"]
    F --> G["Scan config.config_files via to_tool_request_set()"]
    F --> H["Scan MISE_<TOOL>_VERSION env vars via tool_env_vars()"]
    G --> I["configured_tools HashSet"]
    H --> I
    E --> J["Collect tools from expanded_runtimes"]
    J --> K["Filter: tools NOT in configured_tools"]
    K --> L{Any inactive tools?}
    L -- Yes --> M["warn: not activated — not in config"]
    L -- No --> N["Install proceeds silently"]
Loading

Fix All in Claude Code

Reviews (6): Last reviewed commit: "test(install): cover env-var unaliasing ..." | Re-trigger Greptile

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request fixes a regression where installing a tool with a specific version (e.g., TOOL@VERSION) incorrectly triggered an inactive hint, even if the tool was already configured. The fix involves explicitly checking configuration files to identify configured tools. Review feedback suggests optimizing the logic by removing an unnecessary intermediate vector allocation and leveraging the existing tools set to simplify the calculation of inactive tools and handle potential duplicates.

Comment thread src/cli/install.rs Outdated
Comment on lines +157 to +163
.flat_map(|cf_trs| {
cf_trs
.tools
.keys()
.map(|ba| ba.short.clone())
.collect::<Vec<_>>()
})
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The intermediate Vec allocation inside flat_map is unnecessary. You can chain the iterator directly to improve efficiency and readability.

            .flat_map(|cf_trs| cf_trs.tools.keys().map(|ba| ba.short.clone()))

Comment thread src/cli/install.rs Outdated
Comment on lines 165 to 169
let inactive_tools: Vec<String> = expanded_runtimes
.iter()
.filter(|ta| {
trs.sources
.get(ta.ba.as_ref())
.is_none_or(|s| s.is_argument())
})
.filter(|ta| !configured_tools.contains(&ta.ba.short))
.map(|ta| ta.ba.short.clone())
.collect();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Since the tools set (containing the names of tools being installed) is already computed at line 146, you can use it directly to calculate inactive_tools. This avoids re-iterating over expanded_runtimes and automatically handles duplicate tool names in the warning message (e.g., if a user runs mise install node@20 node@22).

Suggested change
let inactive_tools: Vec<String> = expanded_runtimes
.iter()
.filter(|ta| {
trs.sources
.get(ta.ba.as_ref())
.is_none_or(|s| s.is_argument())
})
.filter(|ta| !configured_tools.contains(&ta.ba.short))
.map(|ta| ta.ba.short.clone())
.collect();
let inactive_tools: Vec<String> = tools
.iter()
.filter(|t| !configured_tools.contains(*t))
.cloned()
.collect();

Comment thread src/cli/install.rs
jdx and others added 2 commits May 1, 2026 10:14
Address gemini review feedback on #9522:
- use into_keys() to avoid the intermediate Vec allocation in flat_map
- compute inactive_tools from the existing tools HashSet, which dedupes
  duplicate args (e.g. `mise install node@20 node@22`) for free

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Cursor Bugbot caught that the config-files-only check on #9522 regressed
the env-var case: tools activated only via MISE_<TOOL>_VERSION (e.g.
MISE_NODE_VERSION=20) would now incorrectly emit the "not activated"
warning, because they don't appear in config.config_files. The previous
trs.sources-based check happened to handle them via ToolSource::Environment.

We can't go back to trs.sources because load_runtime_args still overrides
Environment with Argument the same way it overrides MiseToml. Mirror the
MISE_<TOOL>_VERSION parsing from ToolRequestSetBuilder::load_runtime_env
and merge those names into configured_tools.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Comment thread src/cli/install.rs Outdated
jdx and others added 2 commits May 1, 2026 10:26
Hoist the MISE_<TOOL>_VERSION parsing out of install.rs (where it had
been duplicated from ToolRequestSetBuilder::load_runtime_env) into a
shared tool_env_vars() helper in tool_request_set.rs. Both call sites
now share one definition of "what counts as a tool env var".

No behavior change.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Cursor Bugbot caught that tool_env_vars yielded the raw plugin name
extracted from the env var key, so MISE_NODEJS_VERSION produced
"nodejs" but the tools set (built from ToolArg.ba.short, which is
already unaliased) contained "node" — and the inactive-tool check
falsely flagged node as not activated.

Run the extracted name through unalias_backend so callers get the
same canonical short the rest of the toolset already uses.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 4d54b4d. Configure here.

Comment thread e2e/cli/test_install_inactive_hint Outdated
…check

Cursor Bugbot pointed out the --dry-run e2e case for MISE_NODEJS_VERSION
never reached the inactive-tool warning (Install::install_runtimes
returns early in dry-run mode), so it asserted nothing about the
unaliasing behavior it claimed to test.

Replace it with a unit test on tool_env_vars() that directly verifies
MISE_NODEJS_VERSION → "node" and MISE_GOLANG_VERSION → "go", plus a
companion test that the helper skips MISE_VERSION /
MISE_INSTALL_VERSION / MISE_TOOL_VERSION.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 1, 2026

Hyperfine Performance

mise x -- echo

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.4.28 x -- echo 21.6 ± 0.4 21.0 27.2 1.00
mise x -- echo 22.2 ± 0.4 21.4 23.9 1.03 ± 0.03

mise env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.4.28 env 21.3 ± 0.6 20.6 27.5 1.00
mise env 21.6 ± 0.4 20.9 22.8 1.01 ± 0.03

mise hook-env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.4.28 hook-env 22.0 ± 0.4 21.3 23.8 1.00
mise hook-env 22.3 ± 0.4 21.5 27.2 1.01 ± 0.03

mise ls

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.4.28 ls 19.1 ± 0.3 18.6 21.1 1.00
mise ls 19.7 ± 0.4 19.1 21.2 1.03 ± 0.03

xtasks/test/perf

Command mise-2026.4.28 mise Variance
install (cached) 150ms 154ms -2%
ls (cached) 76ms 77ms -1%
bin-paths (cached) 80ms 81ms -1%
task-ls (cached) 829ms 803ms +3%

@jdx jdx merged commit 62997db into main May 1, 2026
36 checks passed
@jdx jdx deleted the claude/gracious-ishizaka-fe51c6 branch May 1, 2026 17:38
mise-en-dev added a commit that referenced this pull request May 3, 2026
### 🚀 Features

- **(conda)** graduate conda backend out of experimental by @jdx in
[#9544](#9544)
- **(deps)** Add dart and flutter providers by @tjarvstrand in
[#9505](#9505)
- **(registry)** add neo4j by @mnm364 in
[#9525](#9525)
- **(registry)** add rustfs by @mnm364 in
[#9530](#9530)
- **(task)** support exclusion patterns in task sources by
@jlarmstrongiv in [#9496](#9496)
- **(vfox)** add stat function to lua file module by @esteve in
[#9497](#9497)

### 🐛 Bug Fixes

- **(backend)** flag regex prerelease versions by @jdx in
[#9500](#9500)
- **(backend)** mark -nightly/-canary/-experimental as prereleases by
@jdx in [#9523](#9523)
- **(backend)** suppress no-versions warning for unresolved-latest
backends by @jdx in [#9548](#9548)
- **(backend)** include dotnet prereleases from package flags by @jdx in
[#9551](#9551)
- **(backend)** scope PEP 440 prerelease detection to Python backends by
@jdx in [#9558](#9558)
- **(cargo)** Apply install_env during cargo install by @c22 in
[#9502](#9502)
- **(copr)** drop epel-9 chroots since rust >= 1.91 is unavailable by
@jdx in [#9484](#9484)
- **(github)** skip attestations on non-default api_url by @jdx in
[#9486](#9486)
- **(github)** retry ip allow list errors without auth by @risu729 in
[#9506](#9506)
- **(http)** update versions host tracking endpoint by @jdx in
[#9527](#9527)
- **(install)** don't warn for configured tools when version is passed
via CLI by @jdx in [#9522](#9522)
- **(install)** refresh latest before installing missing tools by @jdx
in [#9545](#9545)
- **(install)** don't cache nonexistent install paths by @jdx in
[#9553](#9553)
- **(lockfile)** don't propagate ad-hoc CLI overrides into the project
lockfile by @jdx in [#9562](#9562)
- **(plugin)** detect plugin types after cloning by @risu729 in
[#9540](#9540)
- **(release)** pass --no-git-checks to aube publish by @jdx in
[#9483](#9483)
- **(task)** convert PATH to MSYS Unix form when spawning POSIX shells
on Windows by @JamBalaya56562 in
[#9547](#9547)

### 📚 Documentation

- **(contributing)** require popularity check for registry PRs by @jdx
in
[7bbeebe](7bbeebe)
- **(watch)** update pitchfork domain to en.dev by @risu729 in
[#9536](#9536)
- document ghtkn GitHub token setup by @jdx in
[#9546](#9546)
- clarify registry backend acceptance policy by @jdx in
[#9543](#9543)
- Change exec command to use bash for variable echo by @kuboon in
[#9567](#9567)

### 🧪 Testing

- **(e2e)** run test-tool targets in parallel by @jdx in
[#9564](#9564)
- **(e2e)** run tests in parallel by @jdx in
[#9563](#9563)
- **(e2e)** bind-mount /tmp on disk and surface failed tests in CI
summary by @jdx in [#9570](#9570)
- **(tasks)** migrate test_task_help atask to usage field by @jdx in
[#9549](#9549)

### 📦️ Dependency Updates

- update fedora:45 docker digest to 8b838b3 by @renovate[bot] in
[#9507](#9507)
- update ghcr.io/jdx/mise:deb docker digest to f02194c by @renovate[bot]
in [#9509](#9509)
- update taiki-e/install-action digest to 7769b73 by @renovate[bot] in
[#9512](#9512)
- update ghcr.io/jdx/mise:alpine docker digest to 581f8a8 by
@renovate[bot] in [#9508](#9508)
- update rust crate ctor to v0.10.1 by @renovate[bot] in
[#9515](#9515)
- update ghcr.io/jdx/mise:rpm docker digest to a5c9655 by @renovate[bot]
in [#9510](#9510)
- update rust docker digest to a9cfb75 by @renovate[bot] in
[#9511](#9511)
- update rust crate age to v0.11.3 by @renovate[bot] in
[#9514](#9514)
- update rust crate jiff to v0.2.24 by @renovate[bot] in
[#9516](#9516)
- update dependency vitepress-plugin-tabs to ^0.9.0 by @renovate[bot] in
[#9518](#9518)
- update autofix-ci/action action to v1.3.4 by @renovate[bot] in
[#9513](#9513)
- update rust crate usage-lib to v3.2.1 by @renovate[bot] in
[#9517](#9517)
- update apple-actions/import-codesign-certs action to v7 by
@renovate[bot] in [#9519](#9519)
- update taiki-e/install-action digest to 51cd0b8 by @renovate[bot] in
[#9531](#9531)
- exclude taiki-e/install-action from renovate by @jdx in
[#9532](#9532)
- update rust crate blake3 to v1.8.5 by @renovate[bot] in
[#9533](#9533)

### 📦 Registry

- enable shellcheck on windows by @zeitlinger in
[#9487](#9487)
- add google-java-format by @zeitlinger in
[#9488](#9488)
- add expert
([aqua:expert-lsp/expert](https://github.com/expert-lsp/expert)) by
@AlternateRT in [#9498](#9498)
- update entry for checkmake by @eread in
[#9504](#9504)
- add systemctl-tui
([aqua:rgwood/systemctl-tui](https://github.com/rgwood/systemctl-tui))
by @2xdevv in [#9521](#9521)
- add codon by @3w36zj6 in
[#9538](#9538)
- add tool yr (backend:github:VirusTotal/yara-x) by @adam-moss in
[#9542](#9542)
- add tool betterleaks (backend:aqua/betterleaks/betterleaks) by
@adam-moss in [#9541](#9541)
- add `git-filter-repo` by @garysassano in
[#9550](#9550)
- add umoci
([aqua:opencontainers/umoci](https://github.com/opencontainers/umoci))
by @2xdevv in [#9555](#9555)
- add aqua backend for elixir-ls by @AlternateRT in
[#9557](#9557)
- deny inline backend options by @risu729 in
[#9565](#9565)

### Chore

- **(ci)** fail registry tests without summary by @jdx in
[#9559](#9559)
- **(ci)** use !cancelled() instead of always() for test-ci aggregator
by @jdx in [#9569](#9569)
- **(ci)** use namespace runners for ci jobs by @jdx in
[#9561](#9561)
- **(config)** deprecate shorthands_file setting by @risu729 in
[#9534](#9534)
- **(docs)** remove shrill.en.dev analytics script by @jdx in
[#9539](#9539)
- **(release)** replace bc with awk in release-plz star formatting by
@jdx in
[d7f177f](d7f177f)
- bump hk to 1.44.3 by @jdx in
[#9493](#9493)
- invert CLAUDE.md/AGENTS.md so AGENTS.md is canonical by @jdx in
[#9560](#9560)
- set dev profile debug to 1 by @jdx in
[#9572](#9572)

### New Contributors

- @kuboon made their first contribution in
[#9567](#9567)
- @AlternateRT made their first contribution in
[#9557](#9557)
- @2xdevv made their first contribution in
[#9555](#9555)
- @adam-moss made their first contribution in
[#9541](#9541)
- @jlarmstrongiv made their first contribution in
[#9496](#9496)
- @tjarvstrand made their first contribution in
[#9505](#9505)
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.

1 participant