fix(task): support usage refs in dependency template tags#9424
Conversation
There was a problem hiding this comment.
Code Review
This pull request enhances task dependency rendering by improving the detection of Tera usage references and preserving data types (such as booleans and arrays) when passing usage values to templates. It replaces simple string matching with a more robust logic that handles various Tera tags and converts usage values into tera::Value objects. Feedback suggests replacing is_none_or with map_or to maintain compatibility with older Rust versions and adjusting the condition for the cmd variable to ensure consistency when subcommands are defined in the specification.
| tag[..idx] | ||
| .chars() | ||
| .next_back() | ||
| .is_none_or(|c| !c.is_ascii_alphanumeric() && c != '_' && c != '.') |
There was a problem hiding this comment.
is_none_or was stabilized in Rust 1.82.0. To maintain better compatibility with older compiler versions (MSRV), consider using map_or(true, ...) instead.
| .is_none_or(|c| !c.is_ascii_alphanumeric() && c != '_' && c != '.') | |
| .map_or(true, |c| !c.is_ascii_alphanumeric() && c != '_' && c != '.') |
| for (flag, val) in &po.flags { | ||
| values.insert(flag.name.to_snake_case(), to_tera_value(val)); | ||
| } | ||
| if po.cmds.len() > 1 { |
There was a problem hiding this comment.
To maintain consistency with make_usage_ctx_from_spec_defaults, usage.cmd should be inserted into the map whenever subcommands are defined in the spec, even if no subcommand was selected (in which case it will be an empty string).
| if po.cmds.len() > 1 { | |
| if !spec.cmd.subcommands.is_empty() { |
Greptile SummaryThis PR extends dependency template rendering to detect The previous comment about Confidence Score: 5/5Safe to merge — logic is correct, comment-tag false-positive is fixed, and both unit and e2e tests cover the new behaviour. No P0 or P1 issues found. The detection change is conservative (only No files require special attention. Important Files Changed
Sequence DiagramsequenceDiagram
participant CLI as CLI / deps.rs
participant Task as Task::render
participant DHU as dep_has_usage_ref
participant TH as tera_template_has_usage_ref
participant PU as parse_usage_values_from_task
participant RDU as render_depends_with_usage
CLI->>Task: render(tera, ctx)
Task->>Task: save depends_raw / depends_post_raw / wait_for_raw
loop each TaskDep
Task->>DHU: dep_has_usage_ref(dep)
DHU->>TH: tera_template_has_usage_ref(dep.task)
TH-->>DHU: scans {{ and {% tags only
DHU-->>Task: has_ref (bool)
alt has_ref == false
Task->>Task: dep.render(tera, ctx)
else has_ref == true
Task->>Task: skip (deferred)
end
end
CLI->>PU: parse_usage_values_from_task(config, task)
PU-->>CLI: IndexMap<String, tera::Value>
CLI->>RDU: render_depends_with_usage(config, usage_values)
RDU->>RDU: tera_ctx.insert(usage, usage_values)
RDU->>RDU: restore raw deps and re-render all with usage context
Reviews (3): Last reviewed commit: "[autofix.ci] apply automated fixes" | Re-trigger Greptile |
f6f56b4 to
483d702
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 483d702. Configure here.
| } | ||
| if !spec.cmd.subcommands.is_empty() { | ||
| let cmd = po.cmds.iter().skip(1).map(|c| c.name.clone()).join(" "); | ||
| values.insert("cmd".to_string(), tera::Value::String(cmd)); |
There was a problem hiding this comment.
Subcommand cmd value unreachable in some tasks
Low Severity
The new subcommand handling that inserts a cmd value is placed after the early-return that triggers when both spec.cmd.args and spec.cmd.flags are empty. For tasks defined only with subcommands (no top-level args/flags), the function returns early and never reaches the new insertion, leaving usage.cmd undefined in dependency template rendering and silently dropping any dep that references it.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 483d702. Configure here.
| } | ||
| if !spec.cmd.subcommands.is_empty() { | ||
| let cmd = po.cmds.iter().skip(1).map(|c| c.name.clone()).join(" "); | ||
| values.insert("cmd".to_string(), tera::Value::String(cmd)); |
There was a problem hiding this comment.
Duplicated usage value conversion logic
Low Severity
The new to_tera_value closure and the args/flags-to-snake-case iteration in parse_usage_values_from_task duplicate the logic already implemented in TaskScriptParser::make_usage_ctx. Future changes to usage::parse::ParseValue variants or naming conventions will need to be made in two places, and the two implementations have already drifted slightly in how usage.cmd is exposed when no subcommand is selected.
Reviewed by Cursor Bugbot for commit 483d702. Configure here.
Hyperfine Performance
|
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.4.24 x -- echo |
24.7 ± 0.4 | 23.7 | 26.5 | 1.00 |
mise x -- echo |
25.7 ± 0.8 | 24.4 | 33.1 | 1.04 ± 0.04 |
mise env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.4.24 env |
24.2 ± 0.6 | 23.1 | 30.8 | 1.00 |
mise env |
25.1 ± 0.5 | 23.8 | 27.3 | 1.04 ± 0.04 |
mise hook-env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.4.24 hook-env |
25.1 ± 0.6 | 24.0 | 29.9 | 1.00 |
mise hook-env |
25.8 ± 0.5 | 24.7 | 30.5 | 1.03 ± 0.03 |
mise ls
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.4.24 ls |
25.1 ± 0.4 | 24.1 | 26.5 | 1.00 |
mise ls |
26.3 ± 0.9 | 25.0 | 39.3 | 1.05 ± 0.04 |
xtasks/test/perf
| Command | mise-2026.4.24 | mise | Variance |
|---|---|---|---|
| install (cached) | 165ms | 170ms | -2% |
| ls (cached) | 83ms | 85ms | -2% |
| bin-paths (cached) | 84ms | 88ms | -4% |
| task-ls (cached) | 792ms | 795ms | +0% |
…sage_ctx (#9431) ## Summary Follow-up to [#9424](#9424) addressing two issues raised in review (Cursor Bugbot): 1. **Early-return bug** — `parse_usage_values_from_task` returned an empty map when both `spec.cmd.args` and `spec.cmd.flags` were empty, *before* reaching the new subcommand handling. Tasks defined with only subcommands (no top-level args/flags) that referenced `{{ usage.cmd }}` in deps got an empty `IndexMap`, so the value never landed in the Tera context. Fixed by including `spec.cmd.subcommands.is_empty()` in the early-return condition. 2. **Duplicated conversion logic** — `to_tera_value` and the args/flags-to-snake-case iteration in `parse_usage_values_from_task` duplicated `TaskScriptParser::make_usage_ctx`, and the two had already drifted slightly. Promoted `make_usage_ctx` to `pub(crate)` and reused it. Preserved the defensive empty-`cmd` insertion when subcommands are defined but none was selected. ## Test plan - [x] `mise run format` - [x] `mise run lint` - [x] `cargo test --bin mise task::tests` (34 tests pass) - [x] `mise run test:e2e e2e/tasks/test_task_dep_args` (includes new regression test for subcommand-only deps via `{{ usage.cmd }}`) - [x] `mise run test:e2e e2e/tasks/test_task_usage_cmd` (no regression in pre-existing subcommand behavior) 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Low risk: small, localized change to usage-context extraction plus an added e2e regression test; behavior only differs for tasks whose `usage` defines subcommands without root args/flags. > > **Overview** > Fixes dependency-template rendering for subcommand-only tasks by ensuring `parse_usage_values_from_task` no longer early-returns before considering subcommands, and by reusing `TaskScriptParser::make_usage_ctx` to build the `usage` map. > > When a task defines subcommands but none is selected, the code now guarantees `usage.cmd` is present (as an empty string) so `{{ usage.cmd }}` references in dependency templates consistently resolve. > > Adds an e2e regression test covering `depends` templates that branch on `usage.cmd` for subcommand-only `usage` specs. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 97fa3ff. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
### 🚀 Features - **(task)** add --name-only flag to mise tasks ls by @jdx in [#9435](#9435) ### 🐛 Bug Fixes - **(Dockerfile)** install copr-cli via dnf for better dependency management by @bestagi in [#9421](#9421) - **(aqua)** drop empty-releases fallback to tags by @jdx in [#9443](#9443) - **(docs)** fix theme flicker on docs by @vhespanha in [#9427](#9427) - **(lockfile)** update global lockfile on upgrade by @jdx in [#9442](#9442) - **(ls-remote)** omit rolling/prerelease from JSON when false by @jdx in [#9439](#9439) - **(task)** support usage refs in dependency template tags by @jdx in [#9424](#9424) - **(task)** populate usage.cmd for subcommand-only tasks; share make_usage_ctx by @jdx in [#9431](#9431) - **(task)** resolve sandbox allow_read/allow_write against task dir by @jdx in [#9428](#9428) ### 📚 Documentation - **(site)** add self-hosted page tracker via Cloudflare Worker, drop GoatCounter by @jdx in [#9430](#9430) ### New Contributors - @vhespanha made their first contribution in [#9427](#9427) --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>


Summary
usagereferences inside Tera statement/comment/output tags for dependency templates{% if usage.* %}branchesdepends_postrendering with a boolean usage flagValidation
mise run formatcargo test -q task::tests::test_tera_template_has_usage_refmise run test:e2e e2e/tasks/test_task_dep_argsThis PR was generated by an AI coding assistant.
Note
Medium Risk
Changes how dependency templates detect and render
usagereferences, including preserving boolean/array types for Tera evaluation, which can affect task dependency resolution and conditionaldepends_postbehavior.Overview
Dependency templates can now reference
usageinside Tera statement tags (e.g.{% if usage.flag %}), not just in{{ ... }}output expressions, so conditional dependency blocks are deferred and re-rendered correctly.parse_usage_values_from_tasknow returns typedtera::Values (booleans/arrays/counts) instead of strings, andrender_depends_with_usageconsumes those typed values so dependency templates can branch on flags reliably.Adds unit coverage for
usagereference detection and an e2e test that togglesdepends_postvia a boolean usage flag.Reviewed by Cursor Bugbot for commit 483d702. Bugbot is set up for automated code reviews on this repo. Configure here.