diff --git a/executor_test.go b/executor_test.go index 087a317084..2c3bd7bf57 100644 --- a/executor_test.go +++ b/executor_test.go @@ -796,6 +796,8 @@ func TestForCmds(t *testing.T) { {name: "loop-generates-glob"}, {name: "loop-vars"}, {name: "loop-vars-sh"}, + {name: "loop-vars-ref"}, + {name: "loop-vars-ref-as"}, {name: "loop-task"}, {name: "loop-task-as"}, {name: "loop-different-tasks"}, diff --git a/internal/templater/templater.go b/internal/templater/templater.go index 896cba23c1..053e2aeb16 100644 --- a/internal/templater/templater.go +++ b/internal/templater/templater.go @@ -32,6 +32,12 @@ func (r *Cache) Err() error { } func ResolveRef(ref string, cache *Cache) any { + return ResolveRefWithExtra(ref, cache, nil) +} + +// ResolveRefWithExtra resolves a ref string using the cache plus any +// additional data (e.g. loop variables) provided in extra. +func ResolveRefWithExtra(ref string, cache *Cache, extra map[string]any) any { // If there is already an error, do nothing if cache.err != nil { return nil @@ -42,15 +48,21 @@ func ResolveRef(ref string, cache *Cache) any { cache.cacheMap = cache.Vars.ToCacheMap() } + data := cache.cacheMap + if len(extra) > 0 { + data = maps.Clone(cache.cacheMap) + maps.Copy(data, extra) + } + if ref == "." { - return cache.cacheMap + return data } t, err := template.New("resolver").Funcs(templateFuncs).Parse(fmt.Sprintf("{{%s}}", ref)) if err != nil { cache.err = err return nil } - val, err := t.Resolve(cache.cacheMap) + val, err := t.Resolve(data) if err != nil { cache.err = err return nil @@ -121,7 +133,7 @@ func ReplaceVar(v ast.Var, cache *Cache) ast.Var { func ReplaceVarWithExtra(v ast.Var, cache *Cache, extra map[string]any) ast.Var { if v.Ref != "" { - return ast.Var{Value: ResolveRef(v.Ref, cache)} + return ast.Var{Value: ResolveRefWithExtra(v.Ref, cache, extra)} } return ast.Var{ Value: ReplaceWithExtra(v.Value, cache, extra), diff --git a/internal/templater/templater_test.go b/internal/templater/templater_test.go new file mode 100644 index 0000000000..c86f32950a --- /dev/null +++ b/internal/templater/templater_test.go @@ -0,0 +1,20 @@ +package templater + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/go-task/task/v3/taskfile/ast" +) + +func TestReplaceVarWithExtra_ResolvesRefFromExtra(t *testing.T) { + t.Parallel() + + cache := Cache{Vars: ast.NewVars()} + extra := map[string]any{"ITEM": "a"} + + got := ReplaceVarWithExtra(ast.Var{Ref: ".ITEM"}, &cache, extra) + + require.Equal(t, "a", got.Value) +} diff --git a/testdata/for/cmds/Taskfile.yml b/testdata/for/cmds/Taskfile.yml index e9a95234be..cce22ea3d1 100644 --- a/testdata/for/cmds/Taskfile.yml +++ b/testdata/for/cmds/Taskfile.yml @@ -94,6 +94,35 @@ tasks: var: FOO cmd: cat "{{.ITEM}}" + # Loop variable passed by ref should work (currently broken) + loop-vars-ref: + vars: + FOO: "a b" + cmds: + - for: + var: FOO + task: process + vars: + THING: + ref: .ITEM + + # Loop variable with alias passed by ref + loop-vars-ref-as: + vars: + FOO: "x y" + cmds: + - for: + var: FOO + as: VAL + task: process + vars: + THING: + ref: .VAL + + process: + internal: true + cmd: echo "{{.THING}}" + # Loop over another task loop-task: vars: diff --git a/testdata/for/cmds/testdata/TestForCmds-loop-vars-ref-as.golden b/testdata/for/cmds/testdata/TestForCmds-loop-vars-ref-as.golden new file mode 100644 index 0000000000..b77b4eb1d9 --- /dev/null +++ b/testdata/for/cmds/testdata/TestForCmds-loop-vars-ref-as.golden @@ -0,0 +1,2 @@ +x +y diff --git a/testdata/for/cmds/testdata/TestForCmds-loop-vars-ref.golden b/testdata/for/cmds/testdata/TestForCmds-loop-vars-ref.golden new file mode 100644 index 0000000000..422c2b7ab3 --- /dev/null +++ b/testdata/for/cmds/testdata/TestForCmds-loop-vars-ref.golden @@ -0,0 +1,2 @@ +a +b