Skip to content

fix: avoid clobbering sh: vars when resolving global Ref values#2721

Closed
JonZeolla wants to merge 3 commits intogo-task:mainfrom
JonZeolla:fix/sh-var-regression-2720
Closed

fix: avoid clobbering sh: vars when resolving global Ref values#2721
JonZeolla wants to merge 3 commits intogo-task:mainfrom
JonZeolla:fix/sh-var-regression-2720

Conversation

@JonZeolla
Copy link
Contributor

Summary

  • Fixes a regression in v3.49.0 where global sh: variables resolve to empty strings when the Taskfile has includes:
  • The ReplaceVars call added in fix: Call ReplaceVars() to resolve Ref's for imported global vars. #2632 now only processes variables with Ref values set, leaving already-resolved sh: variables untouched
  • Adds test case covering sh: dynamic vars with included Taskfiles

Root Cause

The ReplaceVars call on line 118 of compiler.go re-processes all TaskfileVars after they've been resolved by rangeFunc. For sh: variables, ReplaceVarWithExtra returns a new Var with the original Sh string but nil Value, destroying the already-resolved result.

Test plan

  • Added TestShVarWithIncludes with golden file asserting sh: vars resolve correctly alongside includes
  • Verified test fails before fix (TDD red phase): DERIVED_VAR= world
  • Verified test passes after fix (TDD green phase): DERIVED_VAR=hello world
  • All existing TestReference subtests still pass (including ref-global from fix: Call ReplaceVars() to resolve Ref's for imported global vars. #2632)
  • Full test suite passes

Fixes #2720

JonZeolla and others added 3 commits March 7, 2026 18:24
…bbering sh: vars

The ReplaceVars call added in go-task#2632 re-processes all TaskfileVars after
they've been resolved by rangeFunc. For sh: variables, this overwrites the
already-resolved Value with a new Var that has the original Sh string but
nil Value, causing dynamic variables to resolve to empty strings when the
Taskfile has includes.

Fix by only resolving variables that actually have Ref values set, leaving
already-resolved sh: variables untouched.

Fixes go-task#2720

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Temporarily reverting the compiler.go fix while keeping the test to
demonstrate TDD — CI should fail on TestShVarWithIncludes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Re-applying the fix now that CI has demonstrated the test catches the
regression. CI should pass on this commit.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes a regression where global sh: vars could be overwritten during post-processing of global variables when a Taskfile contains includes:.

Changes:

  • Adjusts global variable post-processing in Compiler.getVariables to only resolve globals with Ref set.
  • Adds an executor test plus new fixture Taskfiles/golden output to cover sh: vars with includes.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
compiler.go Changes global-var post-pass to resolve only Ref globals (avoiding sh: clobber).
executor_test.go Adds TestShVarWithIncludes to exercise the regression scenario.
testdata/var_sh_with_includes/Taskfile.yml New fixture Taskfile defining global sh: and derived vars plus an include.
testdata/var_sh_with_includes/Included.yml Included fixture Taskfile to trigger include handling.
testdata/var_sh_with_includes/testdata/TestShVarWithIncludes-sh_var_not_clobbered_by_includes.golden Golden output asserting sh: + derived var values remain correct.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

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.
JonZeolla added a commit to JonZeolla/task that referenced this pull request Mar 9, 2026
Add TestShVarWithIncludes to ensure sh: dynamic variables are correctly
resolved when a Taskfile also has includes. This guards against
regressions like the one fixed in v3.49.1 (go-task#2720, go-task#2721).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Regression in v3.49.0: Global sh: variables resolve to empty string in included Taskfiles

2 participants