Skip to content

fix(task): resolve vars from subdirectory configs for monorepo tasks#8343

Merged
jdx merged 3 commits intomainfrom
fix/monorepo-task-vars-resolution
Feb 25, 2026
Merged

fix(task): resolve vars from subdirectory configs for monorepo tasks#8343
jdx merged 3 commits intomainfrom
fix/monorepo-task-vars-resolution

Conversation

@jdx
Copy link
Owner

@jdx jdx commented Feb 25, 2026

Summary

  • Fix [vars] not being loaded from subdirectory configs when running monorepo tasks (e.g., mise run //infra/stacks/gcp:greet)
  • Resolve vars from the task's config hierarchy in build_tera_context and thread them through the script rendering chain via TaskScriptParser.extra_vars
  • Fix parse_usage_spec_and_init_env re-parsing scripts without vars, which caused rendering failures even when render_run_scripts_with_args had correct vars

Closes #6564

Test plan

  • New e2e test test_task_monorepo_vars covers: root vars, subdirectory vars, inheritance, overriding, and MISE_ENV-specific vars
  • Existing test_task_monorepo_mise_env still passes
  • Existing test_task_monorepo_tera_templates, test_task_monorepo_config_context, test_task_monorepo_usage_env still pass
  • All 481 unit tests pass
  • Lint passes

🤖 Generated with Claude Code


Note

Medium Risk
Touches core task execution/rendering paths and changes how vars are injected into Tera contexts, which could affect template rendering for monorepo tasks; behavior is covered by a new end-to-end test.

Overview
Monorepo tasks now resolve [vars] from the task’s subdirectory config hierarchy (and mise.<env>.toml overlays), merging them with root vars so subdir values can override while still inheriting unspecified root vars.

This threads the resolved vars through task execution and script/usage parsing (TaskContextBuilderTaskExecutorTask::parse_usage_spec_with_vars/render_run_scripts_with_argsTaskScriptParser) to prevent re-parsing/rendering without the correct vars context.

Adds an e2e test test_task_monorepo_vars covering root vs subdir vars, overrides/inheritance, and env-specific vars for monorepo task runs.

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

When running a monorepo task like `mise run //infra/stacks/gcp:greet`
from the repo root, `[vars]` defined in the task's subdirectory configs
were not loaded, causing "Variable `vars.MY_VAR` not found in context"
errors.

Root cause: The tera context used for task script rendering only
contained global vars from `config.vars`, not vars from the task's
config hierarchy. Additionally, `parse_usage_spec_and_init_env`
re-parsed scripts without vars, causing rendering failures.

Fix: Resolve vars from the task's config hierarchy in
`build_tera_context` and thread them through the script rendering chain
via `TaskScriptParser.extra_vars`.

Closes #6564

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@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 resolves a critical issue where environment variables defined in [vars] sections of subdirectory configuration files were not being applied to monorepo tasks. The changes introduce a robust system to correctly identify, resolve, and inject these variables into the task's execution context and script rendering process. This ensures that tasks within a monorepo environment behave predictably with respect to their local and inherited configurations, enhancing the reliability and functionality of mise for complex project structures.

Highlights

  • Monorepo Task Variable Resolution: Fixed a bug where [vars] defined in subdirectory mise.toml files were not correctly loaded when running monorepo tasks, ensuring proper inheritance and overriding of variables.
  • Variable Threading Mechanism: Introduced an extra_vars mechanism to explicitly thread resolved variables from the task's configuration hierarchy through the script rendering chain, preventing re-parsing issues.
  • Code Refactoring: Refactored Task, TaskScriptParser, TaskContextBuilder, and TaskExecutor to support the new extra_vars parameter, ensuring variables are available at all necessary stages of task execution and script parsing.
  • Comprehensive Testing: Added a new end-to-end test (test_task_monorepo_vars) to thoroughly validate the correct loading, inheritance, and overriding of root, subdirectory, and MISE_ENV-specific variables in monorepo tasks.
Changelog
  • e2e/tasks/test_task_monorepo_vars
    • Added a new end-to-end test to verify monorepo task variable resolution, including root, subdirectory, inheritance, overriding, and MISE_ENV-specific vars.
  • src/task/mod.rs
    • Introduced parse_usage_spec_with_vars to allow passing extra_vars during usage spec parsing.
    • Updated render_run_scripts_with_args to accept and utilize extra_vars for script rendering.
  • src/task/task_context_builder.rs
    • Modified resolve_task_env_with_config to return an Option<IndexMap<String, String>> containing resolved variables from the task's config hierarchy.
    • Updated build_tera_context to accept task_config_files and resolve [vars] from them, merging them into the Tera context and returning them as resolved_vars.
  • src/task/task_executor.rs
    • Updated execute method to retrieve extra_vars from resolve_task_env_with_config.
    • Passed extra_vars to render_run_scripts_with_args and parse_usage_spec_and_init_env to ensure correct variable propagation.
  • src/task/task_script_parser.rs
    • Added an extra_vars field to TaskScriptParser to store variables resolved from the task's config hierarchy.
    • Implemented with_extra_vars and inject_extra_vars methods to integrate these variables into the Tera context during script parsing and rendering.
Activity
  • The pull request author, jdx, has implemented the changes and provided a detailed summary of the fixes.
  • A comprehensive test plan has been executed, including a new e2e test for monorepo vars and verification that existing tests pass.
  • The changes were generated with Claude Code, indicating AI assistance in development.
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

greptile-apps bot commented Feb 25, 2026

Greptile Summary

Fixes monorepo tasks (mise run //path:task) so that {{ vars.* }} template references are resolved from the task's subdirectory config hierarchy, not just the root config.

Key Changes:

  • TaskContextBuilder.build_tera_context() now resolves vars from task's full config hierarchy (including MISE_ENV-specific files) and merges them into the tera context
  • Vars are threaded as extra_vars through TaskExecutorTaskTaskScriptParser to ensure consistent rendering across multiple parse passes
  • TaskScriptParser.inject_extra_vars() injects resolved vars into the tera context before rendering scripts and usage specs
  • Fixes issue where parse_usage_spec_and_init_env was re-parsing scripts without vars, causing render failures

Impact:

  • Subdirectory vars now correctly override root vars for monorepo tasks
  • MISE_ENV-specific vars (e.g., mise.dev.toml) properly resolved for subdirectory tasks
  • Consistent var resolution across initial script render and usage-spec reparse paths

Confidence Score: 4/5

  • Safe to merge with moderate risk - changes affect core task execution pipeline but are well-tested
  • The PR touches critical task rendering/parsing paths that affect how vars and scripts are resolved across the codebase. However, the changes are well-scoped with clear separation of concerns (extra_vars parameter threading), comprehensive e2e test coverage for various monorepo scenarios, and all existing tests pass. The implementation properly handles edge cases like MISE_ENV-specific configs and var inheritance/overrides.
  • Pay close attention to src/task/task_context_builder.rs which contains the core vars resolution logic for config hierarchies

Important Files Changed

Filename Overview
e2e/tasks/test_task_monorepo_vars New e2e test covering vars resolution for monorepo tasks with comprehensive scenarios
src/task/task_context_builder.rs Core logic to resolve vars from task config hierarchy, merge into tera context, and thread through env resolution chain
src/task/mod.rs Adds extra_vars parameter to usage parsing methods to pass resolved vars to script parser
src/task/task_executor.rs Threads extra_vars from env resolution through to script rendering and usage parsing
src/task/task_script_parser.rs Adds extra_vars field and inject_extra_vars method to inject task-specific vars into tera context during rendering

Sequence Diagram

sequenceDiagram
    participant Executor as TaskExecutor
    participant ContextBuilder as TaskContextBuilder
    participant Task as Task
    participant Parser as TaskScriptParser
    
    Note over Executor: Execute monorepo task //path:task
    
    Executor->>ContextBuilder: resolve_task_env_with_config()
    activate ContextBuilder
    
    Note over ContextBuilder: Load task config hierarchy<br/>(includes MISE_ENV files)
    ContextBuilder->>ContextBuilder: load_config_hierarchy_from_dir()
    
    ContextBuilder->>ContextBuilder: build_tera_context(task_config_files)
    Note over ContextBuilder: Resolve vars from hierarchy<br/>Merge with global config.vars
    
    ContextBuilder-->>Executor: (env, task_env, extra_vars)
    deactivate ContextBuilder
    
    Executor->>Task: render_run_scripts_with_args(extra_vars)
    activate Task
    
    Task->>Task: parse_usage_spec_with_vars(extra_vars)
    Task->>Parser: new().with_extra_vars(extra_vars)
    activate Parser
    
    Parser->>Parser: inject_extra_vars(tera_ctx)
    Note over Parser: Insert vars into {{ vars.* }}
    
    Parser->>Parser: render scripts with vars
    Parser-->>Task: (rendered_scripts, spec)
    deactivate Parser
    
    Task-->>Executor: rendered scripts
    deactivate Task
    
    Executor->>Task: parse_usage_spec_and_init_env(extra_vars)
    Note over Executor: Parse again with args<br/>using same extra_vars
    
    Note over Executor: Execute rendered scripts
Loading

Last reviewed commit: c45dd9e

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 with variable resolution for monorepo tasks by correctly loading [vars] from subdirectory configs. The approach of threading these variables through the task execution pipeline is sound. I've identified a critical bug where file-based monorepo tasks would still not have access to these variables. Additionally, I've suggested some improvements for the new e2e test and a small refactoring to reduce code duplication.

let args = task.args.iter().cloned().collect_vec();
let get_args = || once(command.clone()).chain(args.clone()).collect_vec();
self.parse_usage_spec_and_init_env(config, task, &mut env, get_args)
self.parse_usage_spec_and_init_env(config, task, &mut env, get_args, None)
Copy link
Contributor

Choose a reason for hiding this comment

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

critical

The extra_vars are not passed down to exec_file, so it's always None here. This will prevent this fix from working for file-based monorepo tasks. The exec_file function should be updated to accept extra_vars and pass it through to this function call.

Comment on lines +46 to +67
# Test 2: Subdirectory task sees subdirectory vars (the core bug fix)
echo "=== Test 2: Subdirectory task sees subdirectory vars ==="
unset MISE_ENV
output=$(mise run '//infra/stacks/gcp:greet')
echo "$output"
assert_contains "echo '$output'" "GCP_VAR=gcp_value"
assert_contains "echo '$output'" "SHARED_VAR=from_gcp"

# Test 3: Subdirectory task still inherits root vars it doesn't override
echo "=== Test 3: Subdirectory task inherits root vars ==="
unset MISE_ENV
output=$(mise run '//infra/stacks/gcp:greet')
echo "$output"
assert_contains "echo '$output'" "ROOT_VAR=root_value"

# Test 4: Subdirectory vars override root vars of the same name
echo "=== Test 4: Subdirectory vars override root vars ==="
unset MISE_ENV
output=$(mise run '//infra/stacks/gcp:greet')
echo "$output"
# SHARED_VAR should be from_gcp, not from_root
assert_contains "echo '$output'" "SHARED_VAR=from_gcp"
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

Tests 2, 3, and 4 each run the same mise run command. For better efficiency and clarity, you could combine these into a single test case that runs the command once and then performs all related assertions.

# Test 2, 3, 4: Subdirectory task var resolution
echo "=== Test 2, 3, 4: Subdirectory task var resolution ==="
unset MISE_ENV
output=$(mise run '//infra/stacks/gcp:greet')
echo "$output"
# Test 2: Subdirectory task sees subdirectory vars (the core bug fix)
assert_contains "echo '$output'" "GCP_VAR=gcp_value"
# Test 4: Subdirectory vars override root vars of the same name
assert_contains "echo '$output'" "SHARED_VAR=from_gcp"
# Test 3: Subdirectory task still inherits root vars it doesn't override
assert_contains "echo '$output'" "ROOT_VAR=root_value"

Comment on lines +72 to +82
cat <<EOF >infra/stacks/gcp/mise.toml
[vars]
GCP_VAR = "gcp_value"
SHARED_VAR = "from_gcp"

[tasks.greet]
run = 'echo "GCP_VAR={{vars.GCP_VAR}} SHARED_VAR={{vars.SHARED_VAR}} ROOT_VAR={{vars.ROOT_VAR}}"'

[tasks.greet-dev]
run = 'echo "DEV_VAR={{vars.DEV_VAR}} SHARED_VAR={{vars.SHARED_VAR}}"'
EOF
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

Instead of overwriting the entire mise.toml file, you could append just the new task definition. This would make the test's intent clearer and avoid re-defining existing configuration.

cat <<EOF >>infra/stacks/gcp/mise.toml

[tasks.greet-dev]
run = 'echo "DEV_VAR={{vars.DEV_VAR}} SHARED_VAR={{vars.SHARED_VAR}}"'
EOF

src/task/mod.rs Outdated
Comment on lines +766 to +769
let mut parser = TaskScriptParser::new(cwd);
if let Some(vars) = extra_vars {
parser = parser.with_extra_vars(vars);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

This logic for creating and configuring the TaskScriptParser is duplicated from parse_usage_spec_with_vars. Consider extracting this into a private helper method on Task to reduce duplication and improve maintainability.

- Pass extra_vars through exec_file for file-based monorepo tasks
- Cache resolved_vars in env_resolution_cache so subsequent tasks
  sharing the same config path still get vars
- Consolidate e2e tests 2/3/4 into a single test case
- Extract make_script_parser helper to reduce duplication

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

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

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

@github-actions
Copy link

github-actions bot commented Feb 25, 2026

Hyperfine Performance

mise x -- echo

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.2.20 x -- echo 26.5 ± 0.6 25.3 32.1 1.02 ± 0.05
mise x -- echo 25.9 ± 1.1 24.5 38.3 1.00

mise env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.2.20 env 25.0 ± 0.8 23.7 34.2 1.00
mise env 25.5 ± 0.7 23.9 27.8 1.02 ± 0.04

mise hook-env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.2.20 hook-env 25.8 ± 0.5 24.8 28.8 1.00
mise hook-env 26.2 ± 0.9 24.9 39.2 1.02 ± 0.04

mise ls

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.2.20 ls 23.3 ± 0.7 22.2 27.6 1.00
mise ls 24.0 ± 0.5 22.6 25.8 1.03 ± 0.04

xtasks/test/perf

Command mise-2026.2.20 mise Variance
install (cached) 158ms 159ms +0%
ls (cached) 89ms 89ms +0%
bin-paths (cached) 94ms 93ms +1%
task-ls (cached) 853ms 843ms +1%

All callers now use parse_usage_spec_with_vars directly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@jdx jdx merged commit c29d272 into main Feb 25, 2026
35 checks passed
@jdx jdx deleted the fix/monorepo-task-vars-resolution branch February 25, 2026 21:31
mise-en-dev added a commit that referenced this pull request Feb 26, 2026
### 🐛 Bug Fixes

- **(exec)** respect PATH order for virtualenv resolution in mise x by
@jdx in [#8342](#8342)
- **(task)** revert process group changes that cause hangs with nested
mise tasks by @jdx in [#8347](#8347)
- **(task)** resolve vars from subdirectory configs for monorepo tasks
by @jdx in [#8343](#8343)
- **(task)** resolve dependencies before prepare to fix monorepo glob
deps by @jdx in [#8353](#8353)
- python noarch with Conda backend by @wolfv in
[#8349](#8349)

### New Contributors

- @wolfv made their first contribution in
[#8349](#8349)

## 📦 Aqua Registry Updates

#### New Packages (3)

- [`alexhallam/tv`](https://github.com/alexhallam/tv)
- [`arcanist-sh/hx`](https://github.com/arcanist-sh/hx)
- [`dathere/qsv`](https://github.com/dathere/qsv)

#### Updated Packages (3)

- [`astral-sh/ruff`](https://github.com/astral-sh/ruff)
- [`caarlos0/fork-cleaner`](https://github.com/caarlos0/fork-cleaner)
- [`rhysd/actionlint`](https://github.com/rhysd/actionlint)
risu729 pushed a commit to risu729/mise that referenced this pull request Feb 27, 2026
…dx#8343)

## Summary
- Fix `[vars]` not being loaded from subdirectory configs when running
monorepo tasks (e.g., `mise run //infra/stacks/gcp:greet`)
- Resolve vars from the task's config hierarchy in `build_tera_context`
and thread them through the script rendering chain via
`TaskScriptParser.extra_vars`
- Fix `parse_usage_spec_and_init_env` re-parsing scripts without vars,
which caused rendering failures even when `render_run_scripts_with_args`
had correct vars

Closes jdx#6564

## Test plan
- [x] New e2e test `test_task_monorepo_vars` covers: root vars,
subdirectory vars, inheritance, overriding, and MISE_ENV-specific vars
- [x] Existing `test_task_monorepo_mise_env` still passes
- [x] Existing `test_task_monorepo_tera_templates`,
`test_task_monorepo_config_context`, `test_task_monorepo_usage_env`
still pass
- [x] All 481 unit tests pass
- [x] Lint passes

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches core task execution/rendering paths and changes how vars are
injected into Tera contexts, which could affect template rendering for
monorepo tasks; behavior is covered by a new end-to-end test.
> 
> **Overview**
> Monorepo tasks now resolve `[vars]` from the task’s subdirectory
config hierarchy (and `mise.<env>.toml` overlays), merging them with
root vars so subdir values can override while still inheriting
unspecified root vars.
> 
> This threads the resolved vars through task execution and script/usage
parsing (`TaskContextBuilder` → `TaskExecutor` →
`Task::parse_usage_spec_with_vars`/`render_run_scripts_with_args` →
`TaskScriptParser`) to prevent re-parsing/rendering without the correct
vars context.
> 
> Adds an e2e test `test_task_monorepo_vars` covering root vs subdir
vars, overrides/inheritance, and env-specific vars for monorepo task
runs.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
1f08f3e. 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>
risu729 pushed a commit to risu729/mise that referenced this pull request Feb 27, 2026
### 🐛 Bug Fixes

- **(exec)** respect PATH order for virtualenv resolution in mise x by
@jdx in [jdx#8342](jdx#8342)
- **(task)** revert process group changes that cause hangs with nested
mise tasks by @jdx in [jdx#8347](jdx#8347)
- **(task)** resolve vars from subdirectory configs for monorepo tasks
by @jdx in [jdx#8343](jdx#8343)
- **(task)** resolve dependencies before prepare to fix monorepo glob
deps by @jdx in [jdx#8353](jdx#8353)
- python noarch with Conda backend by @wolfv in
[jdx#8349](jdx#8349)

### New Contributors

- @wolfv made their first contribution in
[jdx#8349](jdx#8349)

## 📦 Aqua Registry Updates

#### New Packages (3)

- [`alexhallam/tv`](https://github.com/alexhallam/tv)
- [`arcanist-sh/hx`](https://github.com/arcanist-sh/hx)
- [`dathere/qsv`](https://github.com/dathere/qsv)

#### Updated Packages (3)

- [`astral-sh/ruff`](https://github.com/astral-sh/ruff)
- [`caarlos0/fork-cleaner`](https://github.com/caarlos0/fork-cleaner)
- [`rhysd/actionlint`](https://github.com/rhysd/actionlint)
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