Skip to content

fix(task): support usage refs in dependency template tags#9424

Merged
jdx merged 2 commits intomainfrom
codex/task-usage-template-tags
Apr 27, 2026
Merged

fix(task): support usage refs in dependency template tags#9424
jdx merged 2 commits intomainfrom
codex/task-usage-template-tags

Conversation

@jdx
Copy link
Copy Markdown
Owner

@jdx jdx commented Apr 27, 2026

Summary

  • Detect usage references inside Tera statement/comment/output tags for dependency templates
  • Preserve typed usage values when rendering dependency templates so boolean flags work in {% if usage.* %} branches
  • Add e2e coverage for conditional depends_post rendering with a boolean usage flag

Validation

  • mise run format
  • cargo test -q task::tests::test_tera_template_has_usage_ref
  • mise run test:e2e e2e/tasks/test_task_dep_args

This PR was generated by an AI coding assistant.


Note

Medium Risk
Changes how dependency templates detect and render usage references, including preserving boolean/array types for Tera evaluation, which can affect task dependency resolution and conditional depends_post behavior.

Overview
Dependency templates can now reference usage inside 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_task now returns typed tera::Values (booleans/arrays/counts) instead of strings, and render_depends_with_usage consumes those typed values so dependency templates can branch on flags reliably.

Adds unit coverage for usage reference detection and an e2e test that toggles depends_post via a boolean usage flag.

Reviewed by Cursor Bugbot for commit 483d702. Bugbot is set up for automated code reviews on this repo. Configure here.

@jdx jdx marked this pull request as ready for review April 27, 2026 13:07
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 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.

Comment thread src/task/mod.rs
tag[..idx]
.chars()
.next_back()
.is_none_or(|c| !c.is_ascii_alphanumeric() && c != '_' && c != '.')
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

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.

Suggested change
.is_none_or(|c| !c.is_ascii_alphanumeric() && c != '_' && c != '.')
.map_or(true, |c| !c.is_ascii_alphanumeric() && c != '_' && c != '.')

Comment thread src/task/mod.rs Outdated
for (flag, val) in &po.flags {
values.insert(flag.name.to_snake_case(), to_tera_value(val));
}
if po.cmds.len() > 1 {
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

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).

Suggested change
if po.cmds.len() > 1 {
if !spec.cmd.subcommands.is_empty() {

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 27, 2026

Greptile Summary

This PR extends dependency template rendering to detect usage references inside Tera statement tags ({%) in addition to output tags ({{}}), and changes parse_usage_values_from_task to return typed tera::Value (booleans, arrays, counts) instead of raw strings so that {% if usage.flag %} branches evaluate correctly.

The previous comment about {# ... #} comment false-positives has been addressed: TAGS now only includes {{ and {% (not {#), confirmed by an explicit unit test.

Confidence Score: 5/5

Safe 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 {{ / {%, not {#), the typed-value conversion maps each ParseValue variant correctly (Bool→Bool, String→String, MultiBool→Number count, MultiString→Array), and the render/re-render cycle is preserved correctly. Tests cover the boundary conditions and the end-to-end conditional scenario.

No files require special attention.

Important Files Changed

Filename Overview
src/task/mod.rs Core logic change: tera_template_has_usage_ref now parses {{ and {% Tera tags (excluding {# comments); parse_usage_values_from_task returns typed tera::Value preserving booleans/arrays/counts; render_depends_with_usage signature updated accordingly. Unit tests added for both new helpers.
e2e/tasks/test_task_dep_args New e2e scenario verifies that a boolean usage flag (--run-post) correctly gates a conditional depends_post template, covering both the positive and negative branches.

Sequence Diagram

sequenceDiagram
    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
Loading

Reviews (3): Last reviewed commit: "[autofix.ci] apply automated fixes" | Re-trigger Greptile

Comment thread src/task/mod.rs Outdated
@jdx jdx force-pushed the codex/task-usage-template-tags branch from f6f56b4 to 483d702 Compare April 27, 2026 13:14
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 2 potential issues.

Fix All in Cursor

❌ 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.

Comment thread src/task/mod.rs
}
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));
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 483d702. Configure here.

Comment thread src/task/mod.rs
}
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));
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 483d702. Configure here.

@github-actions
Copy link
Copy Markdown

Hyperfine Performance

mise x -- echo

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%

@jdx jdx merged commit d213604 into main Apr 27, 2026
34 checks passed
@jdx jdx deleted the codex/task-usage-template-tags branch April 27, 2026 15:08
jdx added a commit that referenced this pull request Apr 27, 2026
…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]>
mise-en-dev added a commit that referenced this pull request Apr 28, 2026
### 🚀 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>
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