Skip to content

fix(prepare): install config tools before running prepare steps#8582

Merged
jdx merged 7 commits intomainfrom
fix/prepare-install-tools-first
Mar 13, 2026
Merged

fix(prepare): install config tools before running prepare steps#8582
jdx merged 7 commits intomainfrom
fix/prepare-install-tools-first

Conversation

@jdx
Copy link
Copy Markdown
Owner

@jdx jdx commented Mar 13, 2026

Summary

  • InstallOptions::default() sets missing_args_only: true, which only installs tools passed as CLI arguments — not tools from [tools] in config files
  • This caused prepare steps to run before their required tools were installed, failing on clean machines where tools like uv aren't available system-wide
  • Set missing_args_only: false in both mise run and mise prepare so config tools are installed before prepare steps execute

Fixes #8567

Test plan

  • Verified with a test project using [tools] uv = "0.9.25" and a custom prepare step that runs uv pip install
  • Before fix: prepare runs before uv is installed (uses system uv if available, fails on clean machines)
  • After fix: uv is installed first, then prepare runs successfully
  • CI passes

🤖 Generated with Claude Code


Note

Medium Risk
Changes tool auto-install behavior for mise prepare/mise run and alters how prepare commands are executed (now via an inline shell), which could affect projects relying on previous argument parsing or auto-install settings.

Overview
Ensures mise prepare installs all configured [tools] before executing prepare rules by setting InstallOptions.missing_args_only to false, preventing prepare steps from failing on clean machines.

Aligns prepare command execution with task behavior by wrapping PrepareCommand::from_string in the configured inline shell (enabling pipes/redirects/&&) and updates --explain output to show the command description. Adds an e2e regression test (test_prepare_tool_install) that verifies tool installation occurs before the prepare step runs.

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

`InstallOptions::default()` sets `missing_args_only: true`, which skips
tools from `[tools]` config and only installs CLI-argument tools. This
caused prepare steps to run before their required tools were installed,
failing on clean machines where tools like `uv` aren't available
system-wide.

Fixes #8567

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, 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 issue where mise's prepare steps could fail on environments without pre-installed tools. By modifying the tool installation logic, it ensures that all necessary tools specified in configuration files are installed before any prepare steps are executed, preventing failures and improving the reliability of mise in new or clean development environments.

Highlights

  • InstallOptions::default() behavior: InstallOptions::default() previously set missing_args_only: true, which meant only tools explicitly passed as CLI arguments were installed, not those specified in [tools] in config files.
  • Impact on prepare steps: This behavior caused prepare steps to fail on clean machines because required tools from config files were not installed before the prepare steps attempted to use them.
  • Solution implemented: The missing_args_only flag is now explicitly set to false in both mise run and mise prepare commands, ensuring that all tools defined in config files are installed before any prepare steps are executed.
Activity
  • Verified the fix with a test project using [tools] uv = "0.9.25" and a custom prepare step that runs uv pip install.
  • Confirmed that before the fix, prepare steps would run before uv was installed, potentially using a system uv or failing on clean machines.
  • Confirmed that after the fix, uv is installed first, and then the prepare steps run successfully.
  • CI passes.
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.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 13, 2026

Greptile Summary

This PR fixes a race condition where mise prepare and mise run would execute prepare steps before installing tools declared in [tools] config, causing failures on clean machines where tools like uv or tiny aren't available system-wide.

Key changes:

  • src/cli/prepare.rs: Sets missing_args_only: false in InstallOptions so all [tools] entries are installed before prepare providers execute. Note: skip_auto_install is still not set here (see discussion in previous review threads about auto_install = false not being respected).
  • src/cli/run.rs: Adds missing_args_only and skip_auto_install fields driven by Settings::get().task.run_auto_install and Settings::get().auto_install, matching the established pattern in task_tool_installer.rs.
  • src/prepare/mod.rs: PrepareCommand::from_string now wraps the run string with the platform default inline shell (sh -c on Unix), enabling shell features like pipes, redirects, and && in custom prepare commands. The empty-shell edge case is now handled with a proper error (ok_or_else) instead of a panic.
  • e2e/cli/test_prepare_tool_install: Adds a regression test that removes the tiny installation and then calls mise prepare, verifying that the tool is installed before the prepare step runs.

The --explain output in prepare.rs now uses cmd.description (the human-readable run string or explicit description) instead of cmd.program + " " + cmd.args.join(" ") which would have emitted the raw sh -c <run> form after the shell-wrapping change.

Confidence Score: 3/5

  • Mostly safe to merge — the core bug fix is correct and well-tested, but prepare.rs still does not respect auto_install = false (an outstanding concern from previous review threads).
  • The fix correctly addresses the root cause (missing_args_only: true skipping config tools) and the run.rs change properly mirrors the task_tool_installer.rs pattern including skip_auto_install. The shell-wrapping change in mod.rs is intentional and safe. Score is 3 rather than 4 because prepare.rs still omits skip_auto_install, silently ignoring users' auto_install = false global setting — an issue raised in prior review threads that remains unresolved in this PR.
  • Pay close attention to src/cli/prepare.rs — the skip_auto_install field should be set consistently with exec.rs and task_tool_installer.rs to honor the global auto_install setting.

Important Files Changed

Filename Overview
src/cli/prepare.rs Core fix: sets missing_args_only: false so config tools are installed before prepare runs. However, skip_auto_install is still not set, meaning auto_install = false in global settings is silently ignored for mise prepare. The --explain output now correctly uses cmd.description instead of reconstructing the shell-wrapped command.
src/cli/run.rs Correctly mirrors the task_tool_installer.rs pattern by adding missing_args_only and skip_auto_install based on task.run_auto_install and auto_install settings, ensuring config tools are installed before prepare steps run in mise run.
src/prepare/mod.rs Changes PrepareCommand::from_string to shell-wrap custom run commands using default_inline_shell(), enabling shell features like pipes and redirects. Correctly handles empty shell configuration with an error instead of a panic (ok_or_else instead of unwrap).
e2e/cli/test_prepare_tool_install New regression test that installs tiny = "1" via [tools] and uses it in a prepare step, verifying the install-before-prepare ordering. Properly removes the tiny installation before running to exercise the install path. Uses an isolated temp directory via the test framework, so state from previous runs doesn't interfere.

Sequence Diagram

sequenceDiagram
    participant User
    participant prepare_rs as mise prepare / mise run
    participant Toolset
    participant PrepareEngine
    participant PrepareCommand

    User->>prepare_rs: invoke command
    prepare_rs->>Toolset: build toolset (ToolsetBuilder)
    prepare_rs->>Toolset: install_missing_versions(missing_args_only: false)
    Note over Toolset: Installs ALL config [tools] (e.g. uv, tiny)<br/>Before fix: missing_args_only was true,<br/>skipping config tools
    Toolset-->>prepare_rs: tools installed, shims rebuilt
    prepare_rs->>Toolset: env_with_path() → PATH includes new tools
    prepare_rs->>PrepareEngine: run(PrepareOptions { env: toolset_env })
    PrepareEngine->>PrepareCommand: from_string(run, config)
    Note over PrepareCommand: Wraps in default_inline_shell()<br/>(sh -c) to support pipes/redirects
    PrepareCommand-->>PrepareEngine: PrepareCommand { program: "sh", args: ["-c", run] }
    PrepareEngine->>PrepareEngine: execute_prepare_static(cmd, toolset_env)
    Note over PrepareEngine: Runs with PATH containing installed tools
    PrepareEngine-->>prepare_rs: PrepareStepResult::Ran(id)
    prepare_rs-->>User: "Prepared: {id}"
Loading

Last reviewed commit: 288b0c4

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 correctly fixes an issue where tools from config files were not installed before prepare steps in mise run and mise prepare. The change involves setting missing_args_only: false when installing tools. My review includes a couple of minor suggestions to improve code conciseness by inlining the InstallOptions struct where it's used only once.

Comment on lines +70 to 75
let install_opts = InstallOptions {
missing_args_only: false,
..Default::default()
};
ts.install_missing_versions(&mut config, &install_opts)
.await?;
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

For conciseness, you can construct InstallOptions inline within the install_missing_versions call, since the install_opts variable is not used elsewhere.

Suggested change
let install_opts = InstallOptions {
missing_args_only: false,
..Default::default()
};
ts.install_missing_versions(&mut config, &install_opts)
.await?;
ts.install_missing_versions(&mut config, &InstallOptions {
missing_args_only: false,
..Default::default()
})
.await?;

Comment on lines 275 to 281
let opts = InstallOptions {
jobs: self.jobs,
raw: self.raw,
missing_args_only: false,
..Default::default()
};
let _ = ts.install_missing_versions(&mut config, &opts).await?;
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

For conciseness, you can construct InstallOptions inline within the install_missing_versions call, since the opts variable is not used elsewhere.

Suggested change
let opts = InstallOptions {
jobs: self.jobs,
raw: self.raw,
missing_args_only: false,
..Default::default()
};
let _ = ts.install_missing_versions(&mut config, &opts).await?;
let _ = ts.install_missing_versions(&mut config, &InstallOptions {
jobs: self.jobs,
raw: self.raw,
missing_args_only: false,
..Default::default()
}).await?;

Ensures tools from [tools] config are installed before prepare steps
that depend on them, preventing regression of the ordering fix.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
jdx and others added 2 commits March 13, 2026 14:40
Custom prepare commands were executed directly (splitting into
program + args) which meant shell features like pipes, redirects,
and && didn't work. Now wraps commands with the default inline shell
(sh -c) to match task execution behavior.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Match the pattern from task_tool_installer.rs: honor task.run_auto_install
and auto_install settings so users who set auto_install = false don't get
config tools force-installed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use cmd.description (the original run string) instead of the
shell-wrapped program+args in --explain output. Restores precise
assertion in e2e test.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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.

mise prepare is an explicit user command like mise install, so it
should always install config tools regardless of auto_install setting.
The auto_install guard is only appropriate for implicit installs
(shims, mise x, mise run).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace unwrap() with a proper error message if the user has
configured an empty shell string.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@jdx jdx enabled auto-merge (squash) March 13, 2026 14:17
@jdx jdx merged commit 1cda78e into main Mar 13, 2026
36 checks passed
@jdx jdx deleted the fix/prepare-install-tools-first branch March 13, 2026 14:20
@github-actions
Copy link
Copy Markdown

Hyperfine Performance

mise x -- echo

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.3.8 x -- echo 23.7 ± 0.4 22.9 28.9 1.00
mise x -- echo 23.7 ± 0.6 22.8 29.2 1.00 ± 0.03

mise env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.3.8 env 23.8 ± 0.6 22.5 25.3 1.00
mise env 24.3 ± 0.5 23.0 26.6 1.02 ± 0.03

mise hook-env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.3.8 hook-env 24.9 ± 0.6 23.7 32.2 1.00
mise hook-env 25.0 ± 0.8 23.2 27.6 1.00 ± 0.04

mise ls

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.3.8 ls 24.4 ± 0.7 22.9 29.9 1.00 ± 0.04
mise ls 24.3 ± 0.7 22.5 27.2 1.00

xtasks/test/perf

Command mise-2026.3.8 mise Variance
install (cached) 152ms 153ms +0%
ls (cached) 84ms 84ms +0%
bin-paths (cached) 86ms 86ms +0%
task-ls (cached) 849ms 824ms +3%

mise-en-dev added a commit that referenced this pull request Mar 13, 2026
### 🚀 Features

- **(github)** use release latest endpoint to get latest release by
@roele in [#8516](#8516)
- **(install)** add shared and system install directories by @jdx in
[#8581](#8581)
- **(vfox)** add provenance metadata to lockfile for tool plugins by
@malept in [#8544](#8544)

### 🐛 Bug Fixes

- **(aqua)** expose main binary when files field is empty and
symlink_bins is enabled by @AlexanderTheGrey in
[#8550](#8550)
- **(env)** redact secrets in `mise set` listing and task-specific env
by @jdx in [#8583](#8583)
- **(prepare)** install config tools before running prepare steps by
@jdx in [#8582](#8582)
- **(task)** allow ctrl-c to interrupt tool downloads during `mise run`
by @jdx in [#8571](#8571)
- **(tasks)** add file task header parser support for spaces around = by
@roele in [#8574](#8574)

### 📚 Documentation

- **(task)** add property description for interactive by @roele in
[#8562](#8562)
- add missing `</bold>` closing tag by @muzimuzhi in
[#8564](#8564)
- rebrand site with new chef logo and warm culinary palette by @jdx in
[#8587](#8587)

### 📦️ Dependency Updates

- update ghcr.io/jdx/mise:alpine docker digest to de4657e by
@renovate[bot] in [#8577](#8577)
- update ghcr.io/jdx/mise:copr docker digest to eef29a2 by
@renovate[bot] in [#8578](#8578)
- update ghcr.io/jdx/mise:rpm docker digest to 5a96587 by @renovate[bot]
in [#8580](#8580)
- update ghcr.io/jdx/mise:deb docker digest to 464cf7c by @renovate[bot]
in [#8579](#8579)

### 📦 Registry

- fix flatc version test mismatch by @jdx in
[#8588](#8588)

### Chore

- **(registry)** skip spark test-tool by @jdx in
[#8572](#8572)

### New Contributors

- @AlexanderTheGrey made their first contribution in
[#8550](#8550)

## 📦 Aqua Registry Updates

#### New Packages (6)

- [`bahdotsh/mdterm`](https://github.com/bahdotsh/mdterm)
-
[`callumalpass/mdbase-lsp`](https://github.com/callumalpass/mdbase-lsp)
- [`facebook/ktfmt`](https://github.com/facebook/ktfmt)
- [`gurgeous/tennis`](https://github.com/gurgeous/tennis)
-
[`tektoncd/pipelines-as-code`](https://github.com/tektoncd/pipelines-as-code)
- [`weedonandscott/trolley`](https://github.com/weedonandscott/trolley)

#### Updated Packages (2)

- [`apple/container`](https://github.com/apple/container)
- [`cocogitto/cocogitto`](https://github.com/cocogitto/cocogitto)
fragon10 pushed a commit to fragon10/mise that referenced this pull request Mar 27, 2026
…8582)

## Summary
- `InstallOptions::default()` sets `missing_args_only: true`, which only
installs tools passed as CLI arguments — not tools from `[tools]` in
config files
- This caused prepare steps to run before their required tools were
installed, failing on clean machines where tools like `uv` aren't
available system-wide
- Set `missing_args_only: false` in both `mise run` and `mise prepare`
so config tools are installed before prepare steps execute

Fixes jdx#8567

## Test plan
- [x] Verified with a test project using `[tools] uv = "0.9.25"` and a
custom prepare step that runs `uv pip install`
- [x] Before fix: prepare runs before uv is installed (uses system uv if
available, fails on clean machines)
- [x] After fix: uv is installed first, then prepare runs successfully
- [ ] CI passes

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes tool auto-install behavior for `mise prepare`/`mise run` and
alters how prepare commands are executed (now via an inline shell),
which could affect projects relying on previous argument parsing or
auto-install settings.
> 
> **Overview**
> Ensures `mise prepare` installs all configured `[tools]` before
executing prepare rules by setting `InstallOptions.missing_args_only` to
`false`, preventing prepare steps from failing on clean machines.
> 
> Aligns prepare command execution with task behavior by wrapping
`PrepareCommand::from_string` in the configured inline shell (enabling
pipes/redirects/`&&`) and updates `--explain` output to show the command
description. Adds an e2e regression test (`test_prepare_tool_install`)
that verifies tool installation occurs before the prepare step runs.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
288b0c4. 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>
fragon10 pushed a commit to fragon10/mise that referenced this pull request Mar 27, 2026
### 🚀 Features

- **(github)** use release latest endpoint to get latest release by
@roele in [jdx#8516](jdx#8516)
- **(install)** add shared and system install directories by @jdx in
[jdx#8581](jdx#8581)
- **(vfox)** add provenance metadata to lockfile for tool plugins by
@malept in [jdx#8544](jdx#8544)

### 🐛 Bug Fixes

- **(aqua)** expose main binary when files field is empty and
symlink_bins is enabled by @AlexanderTheGrey in
[jdx#8550](jdx#8550)
- **(env)** redact secrets in `mise set` listing and task-specific env
by @jdx in [jdx#8583](jdx#8583)
- **(prepare)** install config tools before running prepare steps by
@jdx in [jdx#8582](jdx#8582)
- **(task)** allow ctrl-c to interrupt tool downloads during `mise run`
by @jdx in [jdx#8571](jdx#8571)
- **(tasks)** add file task header parser support for spaces around = by
@roele in [jdx#8574](jdx#8574)

### 📚 Documentation

- **(task)** add property description for interactive by @roele in
[jdx#8562](jdx#8562)
- add missing `</bold>` closing tag by @muzimuzhi in
[jdx#8564](jdx#8564)
- rebrand site with new chef logo and warm culinary palette by @jdx in
[jdx#8587](jdx#8587)

### 📦️ Dependency Updates

- update ghcr.io/jdx/mise:alpine docker digest to de4657e by
@renovate[bot] in [jdx#8577](jdx#8577)
- update ghcr.io/jdx/mise:copr docker digest to eef29a2 by
@renovate[bot] in [jdx#8578](jdx#8578)
- update ghcr.io/jdx/mise:rpm docker digest to 5a96587 by @renovate[bot]
in [jdx#8580](jdx#8580)
- update ghcr.io/jdx/mise:deb docker digest to 464cf7c by @renovate[bot]
in [jdx#8579](jdx#8579)

### 📦 Registry

- fix flatc version test mismatch by @jdx in
[jdx#8588](jdx#8588)

### Chore

- **(registry)** skip spark test-tool by @jdx in
[jdx#8572](jdx#8572)

### New Contributors

- @AlexanderTheGrey made their first contribution in
[jdx#8550](jdx#8550)

## 📦 Aqua Registry Updates

#### New Packages (6)

- [`bahdotsh/mdterm`](https://github.com/bahdotsh/mdterm)
-
[`callumalpass/mdbase-lsp`](https://github.com/callumalpass/mdbase-lsp)
- [`facebook/ktfmt`](https://github.com/facebook/ktfmt)
- [`gurgeous/tennis`](https://github.com/gurgeous/tennis)
-
[`tektoncd/pipelines-as-code`](https://github.com/tektoncd/pipelines-as-code)
- [`weedonandscott/trolley`](https://github.com/weedonandscott/trolley)

#### Updated Packages (2)

- [`apple/container`](https://github.com/apple/container)
- [`cocogitto/cocogitto`](https://github.com/cocogitto/cocogitto)
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