fix: support .MATCH in wildcard include vars

This commit is contained in:
Valentin Maerten
2026-07-20 08:57:45 +02:00
parent 81c4291803
commit e08fc2e587
6 changed files with 99 additions and 0 deletions

View File

@@ -11,6 +11,9 @@
(#2184 by @jubr).
- Fixed `joinUrl` collapsing the `//` in a URL scheme (e.g. producing
`http:/localhost` instead of `http://localhost`) (#2915 by @vsaraikin).
- Fixed `.MATCH` not being available in the `vars` of an include declared with a
wildcard namespace (e.g. `'stack:*'`), so `{{index .MATCH 0}}` can now be used
to feed captured segments into the included Taskfile (#2732 by @vmaerten).
## v3.52.0 - 2026-07-02

View File

@@ -115,6 +115,15 @@ func (c *Compiler) getVariables(t *ast.Task, call *Call, evaluateShVars bool) (*
return nil, err
}
}
// MATCH is populated by GetTask from the wildcard match. Expose it early so
// that include vars coming from a wildcard include namespace can reference
// the captured segments, e.g. `{{index .MATCH 0}}`. It is re-set identically
// when call.Vars is ranged below, which is harmless.
if call != nil && call.Vars != nil {
if match, ok := call.Vars.Get("MATCH"); ok {
result.Set("MATCH", match)
}
}
if t != nil {
for k, v := range t.IncludeVars.All() {
if err := rangeFunc(k, v); err != nil {

View File

@@ -3040,6 +3040,55 @@ func TestWildcard(t *testing.T) {
}
}
func TestIncludesWildcardVars(t *testing.T) {
t.Parallel()
tests := []struct {
name string
call string
expectedOutput string
wantErr bool
}{
{
name: "match feeds include var",
call: "stack:prod:deploy",
expectedOutput: "Deploying to prod\n",
},
{
name: "different match",
call: "stack:staging:deploy",
expectedOutput: "Deploying to staging\n",
},
{
name: "no match",
call: "stack:deploy",
wantErr: true,
},
}
for _, test := range tests {
t.Run(test.call, func(t *testing.T) {
t.Parallel()
var buff bytes.Buffer
e := task.NewExecutor(
task.WithDir("testdata/includes_wildcard_vars"),
task.WithStdout(&buff),
task.WithStderr(&buff),
task.WithSilent(true),
task.WithForce(true),
)
require.NoError(t, e.Setup())
if test.wantErr {
require.Error(t, e.Run(t.Context(), &task.Call{Task: test.call}))
return
}
require.NoError(t, e.Run(t.Context(), &task.Call{Task: test.call}))
assert.Equal(t, test.expectedOutput, buff.String())
})
}
}
// enableExperimentForTest enables the experiment behind pointer e for the duration of test t and sub-tests,
// with the experiment being restored to its previous state when tests complete.
//

View File

@@ -0,0 +1,7 @@
version: "3"
includes:
'stack:*':
taskfile: ./stack/Taskfile.yml
vars:
ENV: '{{index .MATCH 0}}'

View File

@@ -0,0 +1,6 @@
version: "3"
tasks:
deploy:
cmds:
- echo "Deploying to {{.ENV}}"

View File

@@ -461,6 +461,31 @@ includes:
DOCKER_IMAGE: frontend_image
```
You can also use a [wildcard](#wildcard-arguments) in the include's namespace and
reference the captured segments through the `.MATCH` variable when setting the
include vars. This lets a single include be reused across many namespaces without
the included Taskfile needing to know how the value is extracted:
```yaml
version: '3'
includes:
'stack:*':
taskfile: ./taskfiles/Stack.yml
vars:
ENV: '{{index .MATCH 0}}'
```
Running `task stack:prod:deploy` sets `ENV` to `prod` for the included Taskfile.
::: info
`.MATCH` is only available in the include's `vars`. The `taskfile` and `dir`
fields are resolved when Taskfiles are loaded and merged, before any task is
called, so no wildcard has been matched yet at that point.
:::
### Namespace aliases
When including a Taskfile, you can give the namespace a list of `aliases`. This