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..5ea55f89aa --- /dev/null +++ b/testdata/generates-array/Taskfile.yml @@ -0,0 +1,83 @@ +version: '3' + +method: none + +vars: + GLOB_SINGLE: 'foo.*' + 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: + - 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 + - task: dynamic + - task: dynamic-dir + + 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}} + 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 new file mode 100644 index 0000000000..9e7ccc145f --- /dev/null +++ b/testdata/generates-array/testdata/TestGeneratesArray-generates-array.golden @@ -0,0 +1,27 @@ +== 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 +== 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,