Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions docs/tasks/templates.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,18 +53,18 @@ Templates use colon (`:`) separators for namespacing, similar to task naming con

When a task extends a template, fields are merged according to these rules:

| Field | Behavior |
| --------------------------------------- | ----------------------------------------------------------- |
| `run`, `run_windows` | Local overrides completely |
| `tools` | Deep merge (local tools added/override template) |
| `env` | Deep merge (local env added/override template) |
| `depends`, `depends_post`, `wait_for` | Local overrides completely (not merged) |
| `dir` | Local overrides; defaults to config_root if not in template |
| `sources`, `outputs` | Local overrides completely |
| Sandbox deny fields | Compose with task-local settings |
| Sandbox allow fields | Template and task-local values are combined |
| `description`, `shell`, `timeout`, etc. | Local overrides template (if set) |
| `quiet`, `hide`, `raw` | Not carried over (must be set explicitly in task) |
| Field | Behavior |
| ------------------------------------------------- | ----------------------------------------------------------- |
| `run`, `run_windows` | Local overrides completely |
| `tools` | Deep merge (local tools added/override template) |
| `env` | Deep merge (local env added/override template) |
| `depends`, `depends_post`, `wait_for` | Local overrides completely (not merged) |
| `dir` | Local overrides; defaults to config_root if not in template |
| `sources`, `outputs` | Local overrides completely |
| Sandbox deny fields | Compose with task-local settings |
| Sandbox allow fields | Template and task-local values are combined |
| `description`, `shell`, `timeout`, etc. | Local overrides template (if set) |
| `quiet`, `hide`, `raw`, `interactive`, `raw_args` | Not supported on templates (set explicitly on each task) |

### Example: Deep Merge for Tools

Expand Down
13 changes: 10 additions & 3 deletions e2e/config/test_schema_tombi
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ run = "cargo fmt"
shell = "bash -c"

[task_templates.base]
quiet = true
dir = "{{config_root}}"
deny_net = true
allow_env = ["CI"]
Expand Down Expand Up @@ -124,7 +123,7 @@ strict = true

[[schemas]]
path = "file://$SCHEMA_PATH"
include = ["mise-bad.toml", "mise-bad-age.toml", "mise-bad-env-directive.toml", "mise-bad-tmpl.toml", "mise-bad-os.toml", "mise-bad-watch-files.toml"]
include = ["mise-bad.toml", "mise-bad-age.toml", "mise-bad-env-directive.toml", "mise-bad-tmpl.toml", "mise-bad-tmpl-output-flag.toml", "mise-bad-os.toml", "mise-bad-watch-files.toml"]

[[schemas]]
path = "file://$TASK_SCHEMA_PATH"
Expand Down Expand Up @@ -153,11 +152,19 @@ assert_fail "$TOMBI_LINT mise-bad-env-directive.toml"
cat >"$HOME/workdir/mise-bad-tmpl.toml" <<'TOML'
[task_templates.derived]
extends = "base"
quiet = true
TOML

assert_fail "$TOMBI_LINT mise-bad-tmpl.toml"

# Verify that quiet/hide/raw/interactive/raw_args are rejected on task_templates
for flag in quiet hide raw interactive raw_args; do
cat >"$HOME/workdir/mise-bad-tmpl-output-flag.toml" <<TOML
[task_templates.base]
$flag = true
TOML
assert_fail "$TOMBI_LINT mise-bad-tmpl-output-flag.toml"
done

cat >"$HOME/workdir/mise-bad-os.toml" <<'TOML'
[tools]
node = { version = "latest", os = true }
Expand Down
9 changes: 9 additions & 0 deletions schema/mise.json
Original file line number Diff line number Diff line change
Expand Up @@ -2653,6 +2653,15 @@
"allOf": [
{
"$ref": "#/$defs/task_props"
},
{
"properties": {
"hide": false,
"interactive": false,
"quiet": false,
"raw": false,
"raw_args": false
}
}
Comment thread
risu729 marked this conversation as resolved.
],
"unevaluatedProperties": false,
Expand Down
17 changes: 6 additions & 11 deletions src/task/task_template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,12 @@ pub struct TaskTemplate {
#[serde(default)]
pub dir: Option<String>,
#[serde(default)]
pub hide: Option<bool>,
#[serde(default)]
pub raw: Option<bool>,
#[serde(default)]
pub sources: Vec<String>,
#[serde(default)]
pub outputs: TaskOutputs,
#[serde(default)]
pub shell: Option<String>,
#[serde(default)]
pub quiet: Option<bool>,
#[serde(default)]
pub silent: Option<Silent>,
#[serde(default)]
pub tools: IndexMap<String, TaskToolValue>,
Expand Down Expand Up @@ -173,11 +167,12 @@ impl Task {
self.shell = template.shell.clone();
}

// Note: quiet, hide, and raw are `bool` in Task (not Option<bool>), so we cannot
// distinguish between "not set" (defaults to false) and "explicitly set to false".
// Therefore, we do NOT merge these boolean fields from templates to avoid the case
// where a task explicitly sets `quiet = false` but gets overridden by a template's
// `quiet = true`. Users must explicitly set these in their task if needed.
// Note: quiet, hide, raw, interactive, and raw_args are `bool` in Task (not
// Option<bool>), so we cannot distinguish between "not set" (defaults to false)
// and "explicitly set to false". Therefore, we do NOT merge these boolean
// fields from templates to avoid the case where a task explicitly sets
// `quiet = false` but gets overridden by a template's `quiet = true`. Users
// must explicitly set these in their task if needed.

// silent: use template only if local is Off (Silent is an enum, so we can distinguish)
if matches!(self.silent, Silent::Off)
Expand Down
Loading