Skip to content

fix(install): preserve config options and registry defaults#8044

Merged
jdx merged 8 commits intomainfrom
fix/install-cli-config-options-merge
Feb 7, 2026
Merged

fix(install): preserve config options and registry defaults#8044
jdx merged 8 commits intomainfrom
fix/install-cli-config-options-merge

Conversation

@jdx
Copy link
Owner

@jdx jdx commented Feb 7, 2026

Summary

  • fix(install): preserve config options when installing with explicit CLI version — When running mise install tool@version with an explicit version, tool-level config options (e.g. postinstall) from mise.toml were being lost. The CLI args merge in load_runtime_args() replaced config entries entirely, and init_request_options() overwrote options with empty backend arg opts. Fixed by merging config options into CLI requests and preferring config request options in init_request_options().
  • fix(config): preserve registry defaults when using table syntax (pipx/ansible breaks with brace version syntax #8039) — When using table syntax like ansible = { version = "latest" }, install-time registry defaults (e.g. uvx=false, pipx_args=--include-deps) were stripped. The install-time key filtering correctly removes stale install-state cache, but also removed registry defaults. Fixed by re-applying registry defaults after filtering for any keys not explicitly set by the user.

Test plan

  • New e2e test test_install_postinstall_cli_version verifies postinstall fires with both mise install dummy@latest and mise install dummy
  • New unit test test_table_syntax_preserves_registry_defaults verifies registry defaults (uvx, pipx_args) are preserved with table syntax and that user overrides take precedence
  • All 447 unit tests pass
  • All lints pass

🤖 Generated with Claude Code


Note

Medium Risk
Changes tool option resolution/merging during installs and config parsing, which can subtly affect install behavior across many backends. Coverage is improved via new e2e/unit tests, but option precedence changes may have edge cases.

Overview
Ensures tool installs invoked with an explicit CLI version (e.g. mise install dummy@latest) still inherit tool options defined in mise.toml (notably postinstall) by merging config options into argument-sourced ToolRequests and preferring config request options during install request initialization.

Fixes a regression in mise.toml table syntax tool definitions where filtering install-time keys also removed registry defaults; registry default options are now re-applied for keys the user didn’t override. Adds an e2e regression test for postinstall behavior and a unit test verifying registry defaults are preserved and user overrides win.

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

…LI version

When running `mise install tool@version` for a tool that has options
(e.g. postinstall) in mise.toml, the config options were lost because:

1. CLI args replaced config entries in the ToolRequestSet merge without
   preserving config options like postinstall
2. init_request_options() used backend arg opts (empty for CLI tools)
   instead of config request options, overwriting any merged options

This fix:
- Merges config options into CLI tool requests in load_runtime_args()
  when the CLI request has no options of its own
- Updates init_request_options() to prefer config request options over
  backend arg opts

Fixes #7979

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings February 7, 2026 16:31
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @jdx, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses a critical bug in mise where tool configuration options, such as postinstall scripts, were being ignored when users installed tools using an explicit version via the command line (e.g., mise install node@latest). The changes ensure that mise now correctly preserves and applies these configuration options, leading to more predictable and reliable tool installations that respect the user's mise.toml settings.

Highlights

  • Configuration Option Preservation: Resolved an issue where mise install tool@version would silently drop configuration options (e.g., postinstall) defined in mise.toml.
  • Tool Request Merging Logic: Updated ToolRequestSetBuilder to correctly merge config-defined options into CLI-specified tool requests, ensuring they are not lost.
  • Option Prioritization: Modified init_request_options logic to prioritize configuration options, ensuring they are preserved and applied during the installation process.
  • New End-to-End Test: Added a new end-to-end test (test_install_postinstall_cli_version) to specifically verify that postinstall hooks execute as expected when installing tools with explicit CLI versions.
Changelog
  • e2e/cli/test_install_postinstall_cli_version
    • Added a new end-to-end test to confirm that postinstall hooks are correctly triggered when installing tools with explicit versions via the CLI.
  • src/toolset/tool_request_set.rs
    • Implemented logic to merge configuration options from mise.toml into tool requests originating from CLI arguments, preventing their loss.
  • src/toolset/toolset_install.rs
    • Adjusted the init_request_options function to prioritize and utilize configuration-defined options, ensuring they are applied during the installation process.
Activity
  • A new e2e test, test_install_postinstall_cli_version, was introduced to validate the fix for postinstall hooks with explicit CLI versions.
  • All existing relevant tests, including test_hooks_postinstall_env and test_use_retain_opts, continue to pass.
  • This PR directly addresses and fixes the issue reported in https://github.com/jdx/mise/discussions/7979.
  • The changes were generated with the assistance of Claude Code.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
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

Fixes mise install tool@version dropping tool options from mise.toml by preserving/merging request options when a CLI version is explicitly provided.

Changes:

  • Prefer config-derived tool options (e.g., postinstall) during install option initialization.
  • Merge config options into CLI tool requests when CLI specifies a version but omits options.
  • Add an e2e regression test ensuring postinstall runs for mise install dummy@latest.

Reviewed changes

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

File Description
src/toolset/toolset_install.rs Pulls options from config requests (when present) instead of only backend arg opts.
src/toolset/tool_request_set.rs Merges config options into CLI-provided version requests to retain tool config.
e2e/cli/test_install_postinstall_cli_version Adds regression test validating postinstall runs with explicit CLI version.

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

Comment on lines +84 to +91
let config_opts = tvl.requests.first().map(|r| r.options());
let options = if let Some(ref opts) = config_opts
&& !opts.is_empty()
{
opts.clone()
} else {
tvl.backend.opts()
};
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

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

The comment says 'Use config request options if available', but the implementation reads tvl.requests.first() without ensuring that the first request is actually the config-sourced request. If request ordering changes (or the first request is the CLI/argument request), this can still skip config options. Consider selecting the request by source (e.g., find the config ToolRequest in tvl.requests) rather than relying on .first().

Copilot uses AI. Check for mistakes.
Comment on lines +84 to +88
let config_opts = tvl.requests.first().map(|r| r.options());
let options = if let Some(ref opts) = config_opts
&& !opts.is_empty()
{
opts.clone()
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

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

This clones opts after already materializing options into config_opts. You can avoid the extra clone by taking ownership of the options (e.g., using an iterator/filter pattern to directly produce an owned ToolVersionOptions when non-empty). This reduces allocations/copies when options are present.

Suggested change
let config_opts = tvl.requests.first().map(|r| r.options());
let options = if let Some(ref opts) = config_opts
&& !opts.is_empty()
{
opts.clone()
let options = if let Some(opts) = tvl
.requests
.first()
.map(|r| r.options())
.filter(|opts| !opts.is_empty())
{
opts

Copilot uses AI. Check for mistakes.
Comment on lines +12 to +13
# Remove any existing dummy installation to force reinstall
rm -rf ~/.local/share/mise/installs/dummy
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

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

The test hard-codes the install location under ~/.local/share/mise, which may not match the test environment if $HOME, XDG_DATA_HOME, or a tool-specific MISE_DATA_DIR is set by the harness. Use the configured mise data dir (env var if available) or a mise command/API used elsewhere in the test suite to resolve the installs path before deleting.

Copilot uses AI. Check for mistakes.
@@ -0,0 +1,37 @@
#!/usr/bin/env bash
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

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

Consider adding set -euo pipefail near the top so unexpected command failures or unset variables fail the test immediately. This tends to make e2e failures more deterministic and easier to diagnose.

Suggested change
#!/usr/bin/env bash
#!/usr/bin/env bash
set -euo pipefail

Copilot uses AI. Check for mistakes.
Copy link
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 effectively addresses an issue where configuration options were being dropped when installing a tool with an explicit version from the command line. The changes in tool_request_set.rs and toolset_install.rs correctly preserve these options by merging them from the configuration. The addition of a new end-to-end test provides solid verification for this fix. The logic is sound, and I have a couple of suggestions to make the Rust code more idiomatic and maintainable.

Comment on lines +257 to +264
if let Some(config_versions) = trs.tools.get(&arg.ba) {
if let Some(config_tvr) = config_versions.first() {
let config_opts = config_tvr.options();
if !config_opts.is_empty() {
tvr.set_options(config_opts);
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The nested if let statements can be simplified by using and_then to make the code more concise and less nested. This improves readability.

                    if let Some(config_tvr) = trs.tools.get(&arg.ba).and_then(|v| v.first()) {
                        let config_opts = config_tvr.options();
                        if !config_opts.is_empty() {
                            tvr.set_options(config_opts);
                        }
                    }

Comment on lines +85 to +91
let options = if let Some(ref opts) = config_opts
&& !opts.is_empty()
{
opts.clone()
} else {
tvl.backend.opts()
};
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The if let ... && ... construct can be expressed more idiomatically using Option::filter and Option::unwrap_or_else. This functional style is often preferred for its conciseness and clarity.

                let options = config_opts
                    .filter(|opts| !opts.is_empty())
                    .unwrap_or_else(|| tvl.backend.opts());

autofix-ci bot and others added 3 commits February 7, 2026 16:35
When using table syntax like `ansible = { version = "latest" }`, install-time
registry defaults (e.g. uvx=false, pipx_args=--include-deps) were being
stripped. The install-time key filtering in mise_toml.rs correctly removes
stale install-state cache but also removed registry defaults. After filtering
and merging user options, re-apply registry defaults for any keys not
explicitly set by the user.

Fixes #8039

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@jdx jdx changed the title fix(install): preserve config options when installing with explicit CLI version fix(install): preserve config options and registry defaults Feb 7, 2026
autofix-ci bot and others added 4 commits February 7, 2026 16:47
- Simplify nested if-let chains with and_then and Option::filter
- Use $MISE_DATA_DIR instead of hardcoded path in e2e test

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

github-actions bot commented Feb 7, 2026

Hyperfine Performance

mise x -- echo

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.2.6 x -- echo 21.6 ± 0.3 21.0 24.3 1.00
mise x -- echo 22.6 ± 0.6 21.8 26.1 1.05 ± 0.03

mise env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.2.6 env 21.1 ± 0.6 20.4 27.6 1.00
mise env 22.0 ± 0.6 20.8 25.6 1.04 ± 0.04

mise hook-env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.2.6 hook-env 22.0 ± 0.3 21.3 23.8 1.00
mise hook-env 22.6 ± 0.4 21.9 25.5 1.03 ± 0.02

mise ls

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.2.6 ls 20.0 ± 0.5 19.3 25.0 1.00
mise ls 20.5 ± 0.4 19.8 22.1 1.02 ± 0.03

xtasks/test/perf

Command mise-2026.2.6 mise Variance
install (cached) 115ms 117ms -1%
ls (cached) 73ms 74ms -1%
bin-paths (cached) 77ms 78ms -1%
task-ls (cached) 554ms 555ms +0%

@jdx
Copy link
Owner Author

jdx commented Feb 7, 2026

bugbot run

Copy link

@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.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

@jdx jdx merged commit 596a757 into main Feb 7, 2026
35 checks passed
@jdx jdx deleted the fix/install-cli-config-options-merge branch February 7, 2026 17:48
mise-en-dev added a commit that referenced this pull request Feb 8, 2026
### 🚀 Features

- **(shim)** add native .exe shim mode for Windows by @jdx in
[#8045](#8045)

### 🐛 Bug Fixes

- **(install)** preserve config options and registry defaults by @jdx in
[#8044](#8044)
- **(link)** linked versions override lockfile during resolution by @jdx
in [#8050](#8050)
- **(release)** preserve aqua-registry sections in changelog across
releases by @jdx in [#8047](#8047)
- ls --all-sources shows duplicate entries by @roele in
[#8042](#8042)

### 📚 Documentation

- replace "inherit" terminology with config layering by @jdx in
[#8046](#8046)

### 📦 Registry

- switch oxlint to npm backend by default by @risu729 in
[#8038](#8038)
- add orval (npm:orval) by @zdunecki in
[#8051](#8051)

### New Contributors

- @zdunecki made their first contribution in
[#8051](#8051)
lucasew pushed a commit to lucasew/CONTRIB-mise that referenced this pull request Feb 18, 2026
## Summary

- **fix(install): preserve config options when installing with explicit
CLI version** — When running `mise install tool@version` with an
explicit version, tool-level config options (e.g. `postinstall`) from
`mise.toml` were being lost. The CLI args merge in `load_runtime_args()`
replaced config entries entirely, and `init_request_options()` overwrote
options with empty backend arg opts. Fixed by merging config options
into CLI requests and preferring config request options in
`init_request_options()`.
- **fix(config): preserve registry defaults when using table syntax
(jdx#8039)** — When using table syntax like `ansible = { version = "latest"
}`, install-time registry defaults (e.g. `uvx=false`,
`pipx_args=--include-deps`) were stripped. The install-time key
filtering correctly removes stale install-state cache, but also removed
registry defaults. Fixed by re-applying registry defaults after
filtering for any keys not explicitly set by the user.

## Test plan

- [x] New e2e test `test_install_postinstall_cli_version` verifies
postinstall fires with both `mise install dummy@latest` and `mise
install dummy`
- [x] New unit test `test_table_syntax_preserves_registry_defaults`
verifies registry defaults (uvx, pipx_args) are preserved with table
syntax and that user overrides take precedence
- [x] All 447 unit tests pass
- [x] All lints pass

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes tool option resolution/merging during installs and config
parsing, which can subtly affect install behavior across many backends.
Coverage is improved via new e2e/unit tests, but option precedence
changes may have edge cases.
> 
> **Overview**
> Ensures tool installs invoked with an explicit CLI version (e.g. `mise
install dummy@latest`) still inherit tool options defined in `mise.toml`
(notably `postinstall`) by merging config options into argument-sourced
`ToolRequest`s and preferring config request options during install
request initialization.
> 
> Fixes a regression in `mise.toml` *table syntax* tool definitions
where filtering install-time keys also removed registry defaults;
registry default options are now re-applied for keys the user didn’t
override. Adds an e2e regression test for `postinstall` behavior and a
unit test verifying registry defaults are preserved and user overrides
win.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
039ab54. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
lucasew pushed a commit to lucasew/CONTRIB-mise that referenced this pull request Feb 18, 2026
### 🚀 Features

- **(shim)** add native .exe shim mode for Windows by @jdx in
[jdx#8045](jdx#8045)

### 🐛 Bug Fixes

- **(install)** preserve config options and registry defaults by @jdx in
[jdx#8044](jdx#8044)
- **(link)** linked versions override lockfile during resolution by @jdx
in [jdx#8050](jdx#8050)
- **(release)** preserve aqua-registry sections in changelog across
releases by @jdx in [jdx#8047](jdx#8047)
- ls --all-sources shows duplicate entries by @roele in
[jdx#8042](jdx#8042)

### 📚 Documentation

- replace "inherit" terminology with config layering by @jdx in
[jdx#8046](jdx#8046)

### 📦 Registry

- switch oxlint to npm backend by default by @risu729 in
[jdx#8038](jdx#8038)
- add orval (npm:orval) by @zdunecki in
[jdx#8051](jdx#8051)

### New Contributors

- @zdunecki made their first contribution in
[jdx#8051](jdx#8051)
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