From d0cbbc7c3684ab9ce4a6b2d1f463f2f10d47f93f Mon Sep 17 00:00:00 2001 From: Timothy Rule <34501912+trulede@users.noreply.github.com> Date: Sun, 1 Feb 2026 21:03:14 +0100 Subject: [PATCH 1/2] Generates accepts a templated variable representing an array. --- executor_test.go | 14 +++++ internal/templater/templater.go | 22 +++++-- testdata/generates-array/.gitignore | 2 + testdata/generates-array/Taskfile.yml | 62 +++++++++++++++++++ .../TestGeneratesArray-generates-array.golden | 21 +++++++ 5 files changed, 116 insertions(+), 5 deletions(-) create mode 100644 testdata/generates-array/.gitignore create mode 100644 testdata/generates-array/Taskfile.yml create mode 100644 testdata/generates-array/testdata/TestGeneratesArray-generates-array.golden diff --git a/executor_test.go b/executor_test.go index 6e3ff3e1ec..a6673c8c0a 100644 --- a/executor_test.go +++ b/executor_test.go @@ -1072,6 +1072,20 @@ func TestIncludeSilent(t *testing.T) { ) } +func TestGeneratesArray(t *testing.T) { + t.Parallel() + + NewExecutorTest(t, + WithName("generates-array"), + WithExecutorOptions( + task.WithDir("testdata/generates-array"), + task.WithSilent(true), + task.WithForce(true), + ), + WithTask("default"), + ) +} + func TestFailfast(t *testing.T) { t.Parallel() diff --git a/internal/templater/templater.go b/internal/templater/templater.go index 896cba23c1..8c600b0a68 100644 --- a/internal/templater/templater.go +++ b/internal/templater/templater.go @@ -105,11 +105,23 @@ func ReplaceGlobs(globs []*ast.Glob, cache *Cache) []*ast.Glob { return nil } - new := make([]*ast.Glob, len(globs)) - for i, g := range globs { - new[i] = &ast.Glob{ - Glob: Replace(g.Glob, cache), - Negate: g.Negate, + new := []*ast.Glob{} + for _, g := range globs { + v := Replace(g.Glob, cache) + if strings.HasPrefix(v, "[") && strings.HasSuffix(v, "]") { + // A templated var of an array will have form: [a b c], extract that. + for _, glob := range strings.Fields(v[1 : len(v)-1]) { + new = append(new, &ast.Glob{ + Glob: glob, + Negate: g.Negate, + }) + } + } else { + // Otherwise take the glob as provided. + new = append(new, &ast.Glob{ + Glob: v, + Negate: g.Negate, + }) } } return new diff --git a/testdata/generates-array/.gitignore b/testdata/generates-array/.gitignore new file mode 100644 index 0000000000..6bfe71a515 --- /dev/null +++ b/testdata/generates-array/.gitignore @@ -0,0 +1,2 @@ +foo.* +bar.* \ No newline at end of file diff --git a/testdata/generates-array/Taskfile.yml b/testdata/generates-array/Taskfile.yml new file mode 100644 index 0000000000..2002fd4055 --- /dev/null +++ b/testdata/generates-array/Taskfile.yml @@ -0,0 +1,62 @@ +version: '3' + +method: none + +vars: + GLOB_SINGLE: 'foo.*' + GLOB_ARRAY: ['*.txt', '*.yml'] + GLOB_STRING: '*.txt:*.yml' + GLOB_SPLIT: '{{splitList ":" .GLOB_STRING}}' + +tasks: + default: + cmds: + - cmd: touch foo.txt + - cmd: touch bar.txt + - cmd: touch foo.yml + - cmd: touch bar.yml + - task: direct + - task: single + - task: array + - task: string + - task: split + + direct: + sources: + - '*.txt' + cmds: + - echo "== {{.TASK}} ==" + - for: sources + cmd: echo {{.ITEM}} + single: + sources: + - '{{.GLOB_SINGLE}}' + cmds: + - echo "== {{.TASK}} ==" + - for: sources + cmd: echo {{.ITEM}} + array: + sources: + - '{{.GLOB_ARRAY}}' + - exclude: Taskfile.yml + cmds: + - echo "== {{.TASK}} ==" + - for: sources + cmd: echo {{.ITEM}} + string: + sources: + - '{{splitList ":" .GLOB_STRING}}' + - exclude: Taskfile.yml + cmds: + - echo "== {{.TASK}} ==" + - for: sources + cmd: echo {{.ITEM}} + split: + sources: + - '{{.GLOB_SPLIT}}' + - exclude: Taskfile.yml + cmds: + - echo "== {{.TASK}} ==" + - for: sources + cmd: echo {{.ITEM}} + diff --git a/testdata/generates-array/testdata/TestGeneratesArray-generates-array.golden b/testdata/generates-array/testdata/TestGeneratesArray-generates-array.golden new file mode 100644 index 0000000000..ba644d5609 --- /dev/null +++ b/testdata/generates-array/testdata/TestGeneratesArray-generates-array.golden @@ -0,0 +1,21 @@ +== direct == +bar.txt +foo.txt +== single == +foo.txt +foo.yml +== array == +bar.txt +bar.yml +foo.txt +foo.yml +== string == +bar.txt +bar.yml +foo.txt +foo.yml +== split == +bar.txt +bar.yml +foo.txt +foo.yml From fee4249d39f162eeed22ecf7617ee3c88e06f4a5 Mon Sep 17 00:00:00 2001 From: Timothy Rule <34501912+trulede@users.noreply.github.com> Date: Mon, 2 Feb 2026 18:21:27 +0100 Subject: [PATCH 2/2] Add a test for a dynamic var. --- testdata/generates-array/Taskfile.yml | 23 ++++++++++++++++++- .../TestGeneratesArray-generates-array.golden | 6 +++++ variables.go | 12 ++++++++-- 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/testdata/generates-array/Taskfile.yml b/testdata/generates-array/Taskfile.yml index 2002fd4055..5ea55f89aa 100644 --- a/testdata/generates-array/Taskfile.yml +++ b/testdata/generates-array/Taskfile.yml @@ -7,7 +7,10 @@ vars: GLOB_ARRAY: ['*.txt', '*.yml'] GLOB_STRING: '*.txt:*.yml' GLOB_SPLIT: '{{splitList ":" .GLOB_STRING}}' - + GLOB_DYNAMIC: + sh: 'echo "[*.txt]"' + GLOB_DYNAMIC_DIR: + sh: 'echo "./"' tasks: default: cmds: @@ -20,6 +23,8 @@ tasks: - task: array - task: string - task: split + - task: dynamic + - task: dynamic-dir direct: sources: @@ -59,4 +64,20 @@ tasks: - echo "== {{.TASK}} ==" - for: sources cmd: echo {{.ITEM}} + dynamic: + sources: + - '{{.GLOB_DYNAMIC}}' + - exclude: Taskfile.yml + cmds: + - echo "== {{.TASK}} ==" + - for: sources + cmd: echo {{.ITEM}} + dynamic-dir: + sources: + - "{{.GLOB_DYNAMIC_DIR}}/foo*" + - exclude: Taskfile.yml + cmds: + - echo "== {{.TASK}} ==" + - for: sources + cmd: echo {{.ITEM}} diff --git a/testdata/generates-array/testdata/TestGeneratesArray-generates-array.golden b/testdata/generates-array/testdata/TestGeneratesArray-generates-array.golden index ba644d5609..9e7ccc145f 100644 --- a/testdata/generates-array/testdata/TestGeneratesArray-generates-array.golden +++ b/testdata/generates-array/testdata/TestGeneratesArray-generates-array.golden @@ -19,3 +19,9 @@ bar.txt bar.yml foo.txt foo.yml +== dynamic == +bar.txt +foo.txt +== dynamic-dir == +foo.txt +foo.yml diff --git a/variables.go b/variables.go index c10bfcd576..bc78acb17b 100644 --- a/variables.go +++ b/variables.go @@ -99,6 +99,14 @@ func (e *Executor) compiledTask(call *Call, evaluateShVars bool) (*ast.Task, err } cache := &templater.Cache{Vars: vars} + globber := func(globs []*ast.Glob) []*ast.Glob { + // Delays globbing until dynamic variables are available. + if evaluateShVars { + return templater.ReplaceGlobs(globs, cache) + } else { + return origTask.Sources + } + } new := ast.Task{ Task: origTask.Task, Label: templater.Replace(origTask.Label, cache), @@ -106,8 +114,8 @@ func (e *Executor) compiledTask(call *Call, evaluateShVars bool) (*ast.Task, err Prompt: templater.Replace(origTask.Prompt, cache), Summary: templater.Replace(origTask.Summary, cache), Aliases: origTask.Aliases, - Sources: templater.ReplaceGlobs(origTask.Sources, cache), - Generates: templater.ReplaceGlobs(origTask.Generates, cache), + Sources: globber(origTask.Sources), + Generates: globber(origTask.Generates), Dir: templater.Replace(origTask.Dir, cache), Set: origTask.Set, Shopt: origTask.Shopt,