Repository navigation
fix(task): resolve monorepo task names from subdirectories - #11941
Conversation
Bare and `:`-prefixed task names only resolved when the cwd was exactly a config root: expand_colon_task_syntax turned the cwd into a task-path prefix verbatim, while the loader names tasks after the defining config's directory. Scope the name to Config::project_root (the nearest config root above the cwd) instead, falling back to the cwd when it lies outside the selected monorepo. Absolute //-forms, depends resolution, and non-monorepo mode are unaffected. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository YAML (base), Central YAML (inherited), Organization UI (inherited) Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughTask expansion now resolves names from nested directories against the nearest enclosing configuration root. It falls back to the current directory when no matching root exists. Documentation and end-to-end tests cover both task syntaxes and resolution cases. ChangesMonorepo task scope
Estimated code review effort: 2 (Simple) | ~10 minutes Mergeability Score: ⚪ Minimal · up to This change fixes task-name resolution from nested monorepo directories while preserving existing absolute and non-monorepo behavior, with targeted regression coverage and validation reported. No actionable merge-blocking risk remains beyond normal checks and review. Sequence Diagram(s)sequenceDiagram
participant WorkingDirectory
participant TaskExpansion
participant ConfigRoots
participant TaskLoader
WorkingDirectory->>TaskExpansion: request bare or colon-prefixed task
TaskExpansion->>ConfigRoots: collect enclosing project and monorepo roots
ConfigRoots-->>TaskExpansion: return nearest matching root
TaskExpansion->>TaskLoader: expand task within selected root
TaskLoader-->>TaskExpansion: return resolved task
Possibly related PRs
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Greptile SummaryThe PR updates monorepo task-name expansion so bare and colon-prefixed names use the nearest enclosing task namespace rather than the current directory itself.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains. Important Files Changed
Reviews (3): Last reviewed commit: "fix(task): include declared config_roots..." | Re-trigger Greptile |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
e2e/tasks/test_task_monorepo_optional_colon (1)
178-188: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winAdd an ancestor conflict to prove nearest-root precedence.
The fixture defines
nested-onlyonly inservices/nested/mise.toml. This proves nested-config discovery, but it does not prove that the nested task wins over a same-named task inservices/mise.toml.Add a conflicting ancestor task and assert that its output is absent. This makes the “nearest enclosing config root wins” claim executable.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@e2e/tasks/test_task_monorepo_optional_colon` around lines 178 - 188, Add a same-named conflicting task to the ancestor services/mise.toml fixture, then extend Test 12’s assertions from services/nested/inner to verify both task invocations execute the nested task output and exclude the ancestor task output, preserving nearest-root precedence.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@e2e/tasks/test_task_monorepo_optional_colon`:
- Around line 178-188: Add a same-named conflicting task to the ancestor
services/mise.toml fixture, then extend Test 12’s assertions from
services/nested/inner to verify both task invocations execute the nested task
output and exclude the ancestor task output, preserving nearest-root precedence.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository YAML (base), Central YAML (inherited), Organization UI (inherited)
Review profile: CHILL
Plan: Pro Plus
Run ID: ddf2cc11-dc8f-49f8-bc07-836a389d2d74
📒 Files selected for processing (4)
docs/tasks/monorepo.mde2e/tasks/test_task_monorepo_optional_colone2e/tasks/test_task_shorthand_monoreposrc/task/task_load_context.rs
Addresses review: the nested-config case now defines the same task in an ancestor config and asserts the nearest one runs and the ancestor's does not. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A project listed in [monorepo].config_roots may hold only file tasks and no config file (e2e: test_task_non_executable_error), so Config::project_root alone misses it. Resolve bare and `:`-prefixed names against the nearest cwd ancestor that is either a config-file root or a declared config root, bounded by the monorepo root. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Problem
In monorepo mode, running a task by its bare or
:-prefixed name only works when the cwd is exactly a config root. From any directory below one it fails:This contradicts non-monorepo mode (which has always walked up to the nearest mise.toml), monorepo config/env discovery (which walks up correctly —
mise envin the same directory shows the project's[env]), and the docs ("The leading:is optional when running tasks from subdirectories"). Absolute forms (//proj:task,//.../proj:task,//...:task) already work from any depth.Root cause
expand_colon_task_syntax(src/task/task_load_context.rs) converted the cwd to a task-path prefix verbatim viamonorepo_scope(&monorepo_root, cwd)— a purestrip_prefix, no upward walk. The loader names tasks on a different basis:load_local_tasks_with_contextwalksall_dirs()upward from the cwd and prefixes each config's tasks with the defining directory's scope. The two only agree when the cwd is itself the defining directory — the one case that worked. The error message is self-diagnosing: mise had already loaded//proj:hello-monowhile asking for//proj/deep/deeper:hello-mono.Fix
Scope bare and
:-prefixed names to the nearest project root at or above the cwd, instead of to the cwd itself:A "project root" here is the union of two sources, and both are load-bearing:
mise.tomlin a subdirectory not listed in[monorepo].config_rootsstill gets its own namespace from the loader (//proj/nested:nested-task) — a name typed inproj/nested/innermust resolve to//proj/nested, not the outer project.[monorepo].config_roots(resolved viamonorepo_config_root_dirs(None), which matches any existing directory). Needed because a declared project may contain only file tasks (mise-tasks/) and no config file at all — the loader still names its tasks//projects/frontend:build(covered bye2e/tasks/test_task_non_executable_error).The walk is bounded by the monorepo root, which counts as a root itself, and a cwd outside the monorepo is left as-is, preserving every existing error path.
Alternatives considered
Config::project_rootalone: one line, and correct for every config-file case — but it misses declared config roots that hold only file tasks (caught bytest_task_non_executable_errorin CI).[monorepo].config_rootsalone: wrong in the other direction — unlisted nested configs resolve to the outer project, which lacks the task.//a/b:tsilently resolve to//a:t, weakening the absolute forms, which must stay exact.{{cwd}}, relative script paths); far wider blast radius.Risk and compatibility
expand_colon_task_syntaxchanged; all its callers (mise run, bare shorthand,tasks info,tasks deps,cache task) gain the same resolution consistently.depends(incl../...) is structurally unaffected: dependency patterns resolve viaresolve_task_patternfrom the declaring task's name, never the cwd. Its e2e coverage passes.//forms return early before this code; non-monorepo mode returns early onmonorepo_root.is_none().project_root == cwd, so the scope is byte-identical to before (verified — identical task names and cwds in all control cases).//scope. An error case starts working; no previously-working command changes meaning.Tests
e2e/tasks/test_task_shorthand_monorepo: deep-subdir bare +:name; a directory outside all configured projects resolving to root tasks.e2e/tasks/test_task_monorepo_optional_colon: deep-subdir bare +:nameincluding dependency execution; absolute//app:buildand//...:buildfrom a deep cwd; a monorepo-root subdir below no config root; nearest-root-wins for an unlisted nested config, including precedence over an ancestor config defining the same task name. These fail on the pre-fix binary (at the first new assertion, withno task //app/deep/deeper:build found), so they are regression tests rather than tautologies.e2e/tasks/test_task_non_executable_error, which runs:buildfrom a project that hasmise-tasks/but no config file.docs/tasks/monorepo.mdnow states:taskworks from any directory below a config_root and resolves to the nearest enclosing one.Verified additionally: 13-case repro matrix (classic-mode control, config-root/monorepo-root controls, deep-cwd cases, absolute forms from deep cwd, below-no-config-root, unlisted-nested-config) — all pass;
cargo clippy --locked --bin mise -- -D warningsclean;cargo fmt --checkclean; targeted monorepo e2e suite incl. the./...depends-scoping tests passes.🤖 Generated with Claude Code