Skip to content
Closed
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
8 changes: 7 additions & 1 deletion compiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,13 @@ func (c *Compiler) getVariables(t *ast.Task, call *Call, evaluateShVars bool) (*
}
}
// Resolve any outstanding 'Ref' values in global vars (esp. globals from imported Taskfiles).
c.TaskfileVars = templater.ReplaceVars(c.TaskfileVars, &templater.Cache{Vars: result})
// Only process variables with Ref set to avoid clobbering already-resolved sh: variables.
cache := &templater.Cache{Vars: result}
for k, v := range c.TaskfileVars.All() {
if v.Ref != "" {
c.TaskfileVars.Set(k, ast.Var{Value: templater.ResolveRef(v.Ref, cache)})
Copy link

Copilot AI Mar 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This loop resolves Ref values by mutating c.TaskfileVars, but it never updates the result Vars map that getVariables returns and that is used for templating later in the same call. If a global ref: var failed to resolve in the first pass due to ordering (the regression fixed in #2632), it will still be nil/empty in result for the current execution. Consider also writing the resolved value back into result (and resetting the templater cache if needed), or performing a second evaluation pass that repopulates result from the updated global vars.

Suggested change
c.TaskfileVars.Set(k, ast.Var{Value: templater.ResolveRef(v.Ref, cache)})
resolved := templater.ResolveRef(v.Ref, cache)
c.TaskfileVars.Set(k, ast.Var{Value: resolved})
result.Set(k, ast.Var{Value: resolved})

Copilot uses AI. Check for mistakes.
}
}

if t != nil {
for k, v := range t.IncludeVars.All() {
Expand Down
26 changes: 26 additions & 0 deletions executor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -946,6 +946,32 @@ func TestReference(t *testing.T) {
}
}

func TestShVarWithIncludes(t *testing.T) {
t.Parallel()

tests := []struct {
name string
call string
}{
{
name: "sh var not clobbered by includes",
call: "default",
},
}

for _, test := range tests {
NewExecutorTest(t,
WithName(test.name),
WithExecutorOptions(
task.WithDir("testdata/var_sh_with_includes"),
task.WithSilent(true),
task.WithForce(true),
),
WithTask(cmp.Or(test.call, "default")),
)
}
}

func TestVarInheritance(t *testing.T) {
enableExperimentForTest(t, &experiments.EnvPrecedence, 1)
tests := []struct {
Expand Down
6 changes: 6 additions & 0 deletions testdata/var_sh_with_includes/Included.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
version: '3'

tasks:
noop:
cmds:
- "true"
18 changes: 18 additions & 0 deletions testdata/var_sh_with_includes/Taskfile.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
version: '3'

includes:
other:
taskfile: ./Included.yml
internal: true

vars:
DYNAMIC_VAR:
sh: echo "hello"
DERIVED_VAR: "{{.DYNAMIC_VAR}} world"

tasks:
default:
cmds:
- echo "DYNAMIC_VAR={{.DYNAMIC_VAR}}"
- echo "DERIVED_VAR={{.DERIVED_VAR}}"
silent: true
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
DYNAMIC_VAR=hello
DERIVED_VAR=hello world
Loading