Skip to content

fix(task): execute monorepo subdirectory prepare steps from root#8291

Merged
jdx merged 3 commits intomainfrom
fix/monorepo-prepare-subdirectory
Feb 21, 2026
Merged

fix(task): execute monorepo subdirectory prepare steps from root#8291
jdx merged 3 commits intomainfrom
fix/monorepo-prepare-subdirectory

Conversation

@jdx
Copy link
Owner

@jdx jdx commented Feb 21, 2026

Summary

  • Fixes monorepo subdirectory prepare steps being skipped when running mise run //subdir:task from the root (discussions/6564#discussioncomment-15853930)
  • Moves prepare execution to after task resolution so subdirectory config files discovered during get_task_lists() are passed to PrepareEngine
  • Extracts build_provider() helper in PrepareEngine and adds add_config_files() method for injecting additional config file providers

Test plan

  • mise run build compiles successfully
  • mise run test:e2e test_prepare passes (all existing + new test)
  • New e2e test: sets up monorepo with subdirectory prepare provider, runs mise run //subapp:check from root, asserts prepare step executed before task

🤖 Generated with Claude Code


Note

Medium Risk
Changes task execution ordering and expands which prepare providers can run for a mise run, which could affect dependency prep behavior in multi-config/monorepo setups.

Overview
Fixes mise run //subdir:task in monorepos so auto-enabled prepare steps from the referenced subdirectory config are executed.

This moves auto-prepare execution in run to after task resolution and injects the resolved tasks’ config files into PrepareEngine via a new add_config_files() path (with provider construction refactored into build_provider()). Adds an e2e regression test that asserts subdirectory prepare runs when invoking //subapp:check from the repo root.

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

When running `mise run //subdir:task` from the monorepo root, prepare
steps defined in the subdirectory config were skipped because prepare
ran before task resolution. Move prepare to run after `get_task_lists()`
so subdirectory config files discovered during task resolution can be
passed to the PrepareEngine.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings February 21, 2026 18:37
@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 prepare steps for monorepo subdirectories were not being executed when tasks were run from the root. The solution involves reordering the prepare step execution to occur after task resolution, enabling the system to correctly identify and apply prepare configurations from subdirectories. Additionally, the PrepareEngine has been refactored to support this new workflow, improving its ability to handle dynamic configuration sources.

Highlights

  • Monorepo Prepare Step Fix: Resolved an issue where mise run //subdir:task from the root of a monorepo would skip prepare steps defined in the subdirectory's configuration.
  • Prepare Execution Timing: Relocated the execution of auto-enabled prepare steps to occur after task resolution, allowing the system to discover and incorporate prepare providers from monorepo subdirectory configurations.
  • PrepareEngine Enhancements: Introduced a new build_provider() helper function to centralize provider instantiation logic and added an add_config_files() method to PrepareEngine for dynamically injecting additional configuration files, specifically for monorepo subdirectories.
Changelog
  • e2e/cli/test_prepare
    • Added a new end-to-end test case to verify that monorepo subdirectory prepare steps are correctly executed when running tasks from the root.
  • src/cli/run.rs
    • Moved the logic for running auto-enabled prepare steps to a later stage in the run command's execution flow.
    • Integrated the PrepareEngine::add_config_files method to include prepare configurations from resolved monorepo subdirectory tasks.
  • src/prepare/engine.rs
    • Extracted the provider instantiation logic into a new private helper function build_provider.
    • Implemented a public method add_config_files to allow external injection of additional ConfigFile instances for prepare provider discovery.
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

@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 addresses an issue where prepare steps in monorepo subdirectories were skipped when running tasks from the root. The solution involves moving the prepare execution phase to after task resolution and injecting the configuration files of the resolved tasks into the PrepareEngine. Overall, the changes are well-structured and include a comprehensive end-to-end test. My feedback focuses on minor performance optimizations regarding the deduplication of configuration files and the use of more efficient data structures for tracking disabled providers.

Comment on lines +313 to +316
let subdir_configs: Vec<_> = task_list
.iter()
.filter_map(|task| task.cf.clone())
.collect();
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

Resolved tasks in a monorepo often share the same configuration file. Deduplicating subdir_configs before passing them to the PrepareEngine prevents redundant processing of the same prepare configuration.

Suggested change
let subdir_configs: Vec<_> = task_list
.iter()
.filter_map(|task| task.cf.clone())
.collect();
let subdir_configs: Vec<_> = task_list
.iter() .filter_map(|task| task.cf.clone())
.unique_by(|cf| cf.get_path().to_path_buf())
.collect();

self.providers.iter().map(|p| p.id().to_string()).collect();
let mut disabled: Vec<String> = vec![];

for cf in config_files {
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

Using a HashSet for the disabled list is more efficient than a Vec for the subsequent filtering step, as it provides $O(1)$ lookup complexity.

Suggested change
for cf in config_files {
let mut disabled: HashSet<String> = HashSet::new();

}
}

/// List all discovered providers
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

With disabled as a HashSet, we can perform an efficient lookup using the provider ID directly, avoiding the allocation of a new String for every comparison in the retain closure.

Suggested change
/// List all discovered providers
.retain(|p| !disabled.contains(p.id()));

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

This PR fixes an issue where monorepo subdirectory prepare steps were being skipped when running tasks from the root directory using the //subdir:task syntax. The fix moves prepare step execution to occur after task resolution, allowing the PrepareEngine to discover and include prepare providers from subdirectory config files.

Changes:

  • Refactored PrepareEngine to extract provider building logic into a helper method and added add_config_files() for injecting additional config file providers
  • Moved prepare step execution in Run command to after task resolution so subdirectory configs are available
  • Added e2e test validating monorepo subdirectory prepare steps execute correctly

Reviewed changes

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

File Description
src/prepare/engine.rs Extracts build_provider() helper and adds add_config_files() method to support injecting subdirectory config providers
src/cli/run.rs Moves prepare execution after task resolution and injects subdirectory config files into PrepareEngine
e2e/cli/test_prepare Adds test case for monorepo subdirectory prepare steps when running //subdir:task from root

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

Comment on lines +220 to +221
let mut seen_ids: HashSet<String> =
self.providers.iter().map(|p| p.id().to_string()).collect();
Copy link

Copilot AI Feb 21, 2026

Choose a reason for hiding this comment

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

The seen_ids HashSet is initialized from existing providers but doesn't check for duplicates within the new config files being added. If the same provider ID appears in multiple config files passed to this method, only the first occurrence will be added. Consider documenting this behavior or adding validation to warn about duplicate provider IDs within the input config files.

Copilot uses AI. Check for mistakes.
Comment on lines +313 to +316
let subdir_configs: Vec<_> = task_list
.iter()
.filter_map(|task| task.cf.clone())
.collect();
Copy link

Copilot AI Feb 21, 2026

Choose a reason for hiding this comment

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

The cloning of task.cf values and collection into subdir_configs could benefit from a comment explaining why we're collecting config files from tasks. While the prepare execution comment above provides context, documenting this specific collection step would clarify why we need these config files and what they represent (subdirectory configs from resolved tasks).

Copilot uses AI. Check for mistakes.
@jdx jdx changed the title fix(run): execute monorepo subdirectory prepare steps from root fix(task): execute monorepo subdirectory prepare steps from root Feb 21, 2026
@jdx
Copy link
Owner Author

jdx commented Feb 21, 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 enabled auto-merge (squash) February 21, 2026 19:00
@jdx jdx merged commit 76a5fb3 into main Feb 21, 2026
36 checks passed
@jdx jdx deleted the fix/monorepo-prepare-subdirectory branch February 21, 2026 19:01
@github-actions
Copy link

Hyperfine Performance

mise x -- echo

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.2.17 x -- echo 21.9 ± 0.3 21.4 24.3 1.00
mise x -- echo 22.8 ± 1.9 22.1 61.6 1.04 ± 0.09

mise env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.2.17 env 21.3 ± 0.5 20.8 31.9 1.00
mise env 21.5 ± 0.4 20.8 27.2 1.01 ± 0.03

mise hook-env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.2.17 hook-env 22.0 ± 0.5 21.4 27.9 1.00
mise hook-env 22.1 ± 0.2 21.6 24.1 1.01 ± 0.03

mise ls

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.2.17 ls 19.6 ± 0.2 19.2 21.0 1.00
mise ls 19.7 ± 0.4 19.2 26.8 1.00 ± 0.03

xtasks/test/perf

Command mise-2026.2.17 mise Variance
install (cached) 123ms 122ms +0%
ls (cached) 74ms 74ms +0%
bin-paths (cached) 79ms 79ms +0%
task-ls (cached) 802ms 796ms +0%

jdx pushed a commit that referenced this pull request Feb 21, 2026
### 🚀 Features

- **(install)** auto-lock all platforms after tool installation by @jdx
in [#8277](#8277)

### 🐛 Bug Fixes

- **(config)** respect --yes flag for config trust prompts by @jdx in
[#8288](#8288)
- **(exec)** strip shims from PATH on Unix to prevent infinite recursion
by @jdx in [#8276](#8276)
- **(install)** validate --locked before --dry-run short-circuit by
@altendky in [#8290](#8290)
- **(release)** refresh PATH after mise up in release-plz by @jdx in
[#8292](#8292)
- **(schema)** replace unevaluatedProperties with additionalProperties
by @jdx in [#8285](#8285)
- **(task)** avoid duplicated stderr on task failure in replacing mode
by @jdx in [#8275](#8275)
- **(task)** use process groups to kill child process trees on Unix by
@jdx in [#8279](#8279)
- **(task)** run depends_post tasks even when parent task fails by @jdx
in [#8274](#8274)
- **(task)** suggest similar commands when mistyping a CLI subcommand by
@jdx in [#8286](#8286)
- **(task)** execute monorepo subdirectory prepare steps from root by
@jdx in [#8291](#8291)
- **(upgrade)** don't force-reinstall already installed versions by @jdx
in [#8282](#8282)
- **(watch)** restore terminal state after watchexec exits by @jdx in
[#8273](#8273)

### 📚 Documentation

- clarify that MISE_CEILING_PATHS excludes the ceiling directory itself
by @jdx in [#8283](#8283)

### Chore

- replace gen-release-notes script with communique by @jdx in
[#8289](#8289)

### New Contributors

- @altendky made their first contribution in
[#8290](#8290)

## 📦 Aqua Registry Updates

#### New Packages (4)

-
[`Skarlso/crd-to-sample-yaml`](https://github.com/Skarlso/crd-to-sample-yaml)
-
[`kunobi-ninja/kunobi-releases`](https://github.com/kunobi-ninja/kunobi-releases)
-
[`swanysimon/markdownlint-rs`](https://github.com/swanysimon/markdownlint-rs)
- [`tmux/tmux-builds`](https://github.com/tmux/tmux-builds)

#### Updated Packages (2)

-
[`firecow/gitlab-ci-local`](https://github.com/firecow/gitlab-ci-local)
- [`k1LoW/runn`](https://github.com/k1LoW/runn)
netbsd-srcmastr pushed a commit to NetBSD/pkgsrc that referenced this pull request Feb 22, 2026
## [2026.2.18](https://github.com/jdx/mise/compare/v2026.2.17..v2026.2.18) - 2026-02-21

### 🚀 Features

- **(install)** auto-lock all platforms after tool installation by @jdx in [#8277](jdx/mise#8277)

### 🐛 Bug Fixes

- **(config)** respect --yes flag for config trust prompts by @jdx in [#8288](jdx/mise#8288)
- **(exec)** strip shims from PATH on Unix to prevent infinite recursion by @jdx in [#8276](jdx/mise#8276)
- **(install)** validate --locked before --dry-run short-circuit by @altendky in [#8290](jdx/mise#8290)
- **(release)** refresh PATH after mise up in release-plz by @jdx in [#8292](jdx/mise#8292)
- **(schema)** replace unevaluatedProperties with additionalProperties by @jdx in [#8285](jdx/mise#8285)
- **(task)** avoid duplicated stderr on task failure in replacing mode by @jdx in [#8275](jdx/mise#8275)
- **(task)** use process groups to kill child process trees on Unix by @jdx in [#8279](jdx/mise#8279)
- **(task)** run depends_post tasks even when parent task fails by @jdx in [#8274](jdx/mise#8274)
- **(task)** suggest similar commands when mistyping a CLI subcommand by @jdx in [#8286](jdx/mise#8286)
- **(task)** execute monorepo subdirectory prepare steps from root by @jdx in [#8291](jdx/mise#8291)
- **(upgrade)** don't force-reinstall already installed versions by @jdx in [#8282](jdx/mise#8282)
- **(watch)** restore terminal state after watchexec exits by @jdx in [#8273](jdx/mise#8273)

### 📚 Documentation

- clarify that MISE_CEILING_PATHS excludes the ceiling directory itself by @jdx in [#8283](jdx/mise#8283)

### Chore

- replace gen-release-notes script with communique by @jdx in [#8289](jdx/mise#8289)

### New Contributors

- @altendky made their first contribution in [#8290](jdx/mise#8290)

### 📦 Aqua Registry Updates

#### New Packages (4)

- [`Skarlso/crd-to-sample-yaml`](https://github.com/Skarlso/crd-to-sample-yaml)
- [`kunobi-ninja/kunobi-releases`](https://github.com/kunobi-ninja/kunobi-releases)
- [`swanysimon/markdownlint-rs`](https://github.com/swanysimon/markdownlint-rs)
- [`tmux/tmux-builds`](https://github.com/tmux/tmux-builds)

#### Updated Packages (2)

- [`firecow/gitlab-ci-local`](https://github.com/firecow/gitlab-ci-local)
- [`k1LoW/runn`](https://github.com/k1LoW/runn)

## [2026.2.17](https://github.com/jdx/mise/compare/v2026.2.16..v2026.2.17) - 2026-02-19

### 🚀 Features

- **(prepare)** update mtime of outputs after command is run by @halms in [#8243](jdx/mise#8243)

### 🐛 Bug Fixes

- **(install)** use backend bin paths for per-tool postinstall hooks by @jdx in [#8234](jdx/mise#8234)
- **(use)** write to config.toml instead of config.local.toml by @jdx in [#8240](jdx/mise#8240)
- default legacy .mise.backend installs to non-explicit by @jean-humann in [#8245](jdx/mise#8245)

### 🚜 Refactor

- **(config)** consolidate flat task_* settings into nested task.* by @jdx in [#8239](jdx/mise#8239)

### Chore

- **(prepare)** refactor common code into ProviderBase by @halms in [#8246](jdx/mise#8246)

### 📦 Aqua Registry Updates

#### Updated Packages (1)

- [`namespacelabs/foundation/nsc`](https://github.com/namespacelabs/foundation/nsc)
NfNitLoop added a commit to NfNitLoop/mise that referenced this pull request Feb 25, 2026
This updates the tests introduced in:
jdx#8291
to demonstrate a case where prepare steps are not properly run.
risu729 pushed a commit to risu729/mise that referenced this pull request Feb 27, 2026
…#8291)

## Summary
- **Fixes** monorepo subdirectory prepare steps being skipped when
running `mise run //subdir:task` from the root
([discussions/6564#discussioncomment-15853930](jdx#6564 (comment)))
- Moves prepare execution to after task resolution so subdirectory
config files discovered during `get_task_lists()` are passed to
`PrepareEngine`
- Extracts `build_provider()` helper in `PrepareEngine` and adds
`add_config_files()` method for injecting additional config file
providers

## Test plan
- [x] `mise run build` compiles successfully
- [x] `mise run test:e2e test_prepare` passes (all existing + new test)
- [x] New e2e test: sets up monorepo with subdirectory prepare provider,
runs `mise run //subapp:check` from root, asserts prepare step executed
before task

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes task execution ordering and expands which `prepare` providers
can run for a `mise run`, which could affect dependency prep behavior in
multi-config/monorepo setups.
> 
> **Overview**
> Fixes `mise run //subdir:task` in monorepos so auto-enabled `prepare`
steps from the referenced subdirectory config are executed.
> 
> This moves auto-`prepare` execution in `run` to *after* task
resolution and injects the resolved tasks’ config files into
`PrepareEngine` via a new `add_config_files()` path (with provider
construction refactored into `build_provider()`). Adds an e2e regression
test that asserts subdirectory `prepare` runs when invoking
`//subapp:check` from the repo root.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
f13905e. 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>
risu729 pushed a commit to risu729/mise that referenced this pull request Feb 27, 2026
### 🚀 Features

- **(install)** auto-lock all platforms after tool installation by @jdx
in [jdx#8277](jdx#8277)

### 🐛 Bug Fixes

- **(config)** respect --yes flag for config trust prompts by @jdx in
[jdx#8288](jdx#8288)
- **(exec)** strip shims from PATH on Unix to prevent infinite recursion
by @jdx in [jdx#8276](jdx#8276)
- **(install)** validate --locked before --dry-run short-circuit by
@altendky in [jdx#8290](jdx#8290)
- **(release)** refresh PATH after mise up in release-plz by @jdx in
[jdx#8292](jdx#8292)
- **(schema)** replace unevaluatedProperties with additionalProperties
by @jdx in [jdx#8285](jdx#8285)
- **(task)** avoid duplicated stderr on task failure in replacing mode
by @jdx in [jdx#8275](jdx#8275)
- **(task)** use process groups to kill child process trees on Unix by
@jdx in [jdx#8279](jdx#8279)
- **(task)** run depends_post tasks even when parent task fails by @jdx
in [jdx#8274](jdx#8274)
- **(task)** suggest similar commands when mistyping a CLI subcommand by
@jdx in [jdx#8286](jdx#8286)
- **(task)** execute monorepo subdirectory prepare steps from root by
@jdx in [jdx#8291](jdx#8291)
- **(upgrade)** don't force-reinstall already installed versions by @jdx
in [jdx#8282](jdx#8282)
- **(watch)** restore terminal state after watchexec exits by @jdx in
[jdx#8273](jdx#8273)

### 📚 Documentation

- clarify that MISE_CEILING_PATHS excludes the ceiling directory itself
by @jdx in [jdx#8283](jdx#8283)

### Chore

- replace gen-release-notes script with communique by @jdx in
[jdx#8289](jdx#8289)

### New Contributors

- @altendky made their first contribution in
[jdx#8290](jdx#8290)

## 📦 Aqua Registry Updates

#### New Packages (4)

-
[`Skarlso/crd-to-sample-yaml`](https://github.com/Skarlso/crd-to-sample-yaml)
-
[`kunobi-ninja/kunobi-releases`](https://github.com/kunobi-ninja/kunobi-releases)
-
[`swanysimon/markdownlint-rs`](https://github.com/swanysimon/markdownlint-rs)
- [`tmux/tmux-builds`](https://github.com/tmux/tmux-builds)

#### Updated Packages (2)

-
[`firecow/gitlab-ci-local`](https://github.com/firecow/gitlab-ci-local)
- [`k1LoW/runn`](https://github.com/k1LoW/runn)
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