Repository: go-task/task (Go)

Issue:
[v3.39.0] defer no longer evaluates variables
<!--

Thanks for your bug report!

Before submitting this issue, please make sure the same problem was not
already reported by someone else.

Please describe the bug you're facing. Consider pasting example Taskfiles
showing how to reproduce the problem.

-->

`defer` is no longer evaluating variables in task 3.39.0. In the sample `Taskfile.yml` below, I've created a simple reproduction.

To reproduce:

Run `task my-repro` with the taskfile below.

See the following output is 

```bash
my-value

```

Expected behavior (+ the behavior on v3.38.0):

```bash
my-value
my-value
```
```yaml
# https://taskfile.dev

version: '3'
silent: true
tasks:
  my-repro:
    cmds:
      - defer: echo {{ .MY_VAR }}
      - echo {{ .MY_VAR }}
    vars:
      MY_VAR: my-value

```

- Task version: 3.39.0
- Operating system: wsl2/ubuntu22.04, Shell: GNU bash, version 5.1.16(1)-release (x86_64-pc-linux-gnu)
- Experiments enabled:



Discussion:
I believe this was the change that introduced the behavior change: https://github.com/go-task/task/pull/1762

I pulled down the commit before the change: `git co 35119c12ab222fc5a2a1d28536f5e575136e103e`, and validated that the command works as expected on this commit via `task my-repro`.

Pulling down the change: `git co b259edeb65e24f28c41604b9819869aca082d3b6` and running the `task my-repro` shows the erroneous behavior


I have the same problem.
However, I have been able to pass variables to defer for some time now, but it seems that was not correct in the schema, so I am having trouble.

https://github.com/go-task/vscode-task/issues/158
`+1` 

* Task version: `v3.39.0 (h1:Loe6ppP1x38Puv1M2wigp91bH/garCt0vLWkJsiTWNI=)`
* Operating system: `Linux ubuntu20 6.8.0-40-generic #40~22.04.3-Ubuntu SMP PREEMPT_DYNAMIC Tue Jul 30 17:30:19 UTC 2 x86_64 x86_64 x86_64 GNU/Linux`
* Experiments enabled: `ENV_PRECEDENCE GENTLE_FORCE REMOTE_TASKFILES`
This seems like a major regression that should have been caught by a test case.

All my `defer: rm -f "{{.VAR}}"` statements are now evaluating to `rm -f ""` 🙃 
Just updated to Task version: v3.39.1 (h1:yIaLD5TZWXU/SdmadBReQKu8jblRxzRT+lJG4xC3CMw=) but I still have the same issue
@kevintijssen are you sure? I just updated and it works fine on `linux/amd64` (which I think you're using from your version hash) ...

```yaml
# Taskfile.1803.yml
version: "3"
vars:
  FILE_VAR: "file-issue-1803"
tasks:
  repro:
    vars:
      TASK_VAR: "task-issue-1803"
    cmds:
      - defer: rmdir "{{.FILE_VAR}}"
      - defer: rmdir "{{.TASK_VAR}}"
      - mkdir -p "{{.FILE_VAR}}"
      - mkdir -p "{{.TASK_VAR}}"
```

```
❯ task -t Taskfile.1803.yml repro
task: [repro] mkdir -p "file-issue-1803"
task: [repro] mkdir -p "task-issue-1803"
task: [repro] rmdir "task-issue-1803"
task: [repro] rmdir "file-issue-1803"
```

* Task version: `v3.39.1 (h1:yIaLD5TZWXU/SdmadBReQKu8jblRxzRT+lJG4xC3CMw=)`
* Operating system: `Linux ubuntu20 6.8.0-40-generic #40~22.04.3-Ubuntu SMP PREEMPT_DYNAMIC Tue Jul 30 17:30:19 UTC 2 x86_64 x86_64 x86_64 GNU/Linux`
* Experiments enabled: `ENV_PRECEDENCE GENTLE_FORCE REMOTE_TASKFILES`
Task version: v3.39.1 (h1:yIaLD5TZWXU/SdmadBReQKu8jblRxzRT+lJG4xC3CMw=)
Linux 828aaae9a89d 6.4.16-linuxkit #1 SMP PREEMPT Fri Nov 10 14:49:23 UTC 2023 aarch64 GNU/Linux

```
wait-for:
    desc: "Will wait for resources to gain the requested condition."
    deps:
      - task: check-tools
    requires:
      vars:
        - RESOURCE
        - CONDITION

    vars:
      CONDITIONAL_ARGUMENTS: "{{if .KUBECONTEXT}}--context={{.KUBECONTEXT}} {{end}} {{if .NAMESPACE}} --namespace={{.NAMESPACE}} {{end}}"
      TEMP_FILE:
        sh: mktemp

    cmds:
      - defer: rm {{.TEMP_FILE}}
      - cmd: |
          cat > {{.TEMP_FILE}} <<EOF
            until kubectl {{.CONDITIONAL_ARGUMENTS}} get {{.RESOURCE}} &> /dev/null; do
              sleep 2
            done

            {{- if ne .CONDITION "exists"}}
            kubectl {{ .CONDITIONAL_ARGUMENTS }} wait --for condition={{.CONDITION}} {{.RESOURCE}} --timeout 500s
            {{end}}
          EOF
      - task: execute-bash
        vars:
          GUM_SPIN_TITLE: "{{.GUM_SPIN_TITLE}}"
          TEMP_FILE: "{{.TEMP_FILE}}"

      - cmd: gum log --level info "{{.RESOURCE}} condition {{.CONDITION}} met"
```


```task: [dev:k3d:utils:wait-for] gum log --level info "deployment/coredns condition available met"
INFO deployment/coredns condition available met
task: "dev:k3d:utils:wait-for" finished
task: [dev:k3d:utils:wait-for] rm 
rm: missing operand
Try 'rm --help' for more information.
task: ignored error in deferred cmd: exit status 1
```

Looks like the bug is _partially_ fixed 🙈 

@andreynering @vmaerten the fix in #1814 doesn't seem to support dynamic vars

```yaml
# Taskfile.1803.yml
version: "3"
vars:
  FILE_VAR: "file-issue-1803"
tasks:
  repro:
    vars:
      TASK_VAR: "task-issue-1803"
      SH_TASK_VAR:
        sh: echo "sh-task-issue-1803"
    cmds:
      - defer: rmdir "{{.FILE_VAR}}"
      - defer: rmdir "{{.TASK_VAR}}"
      - defer: rmdir "{{.SH_TASK_VAR}}"
      - mkdir -p "{{.FILE_VAR}}"
      - mkdir -p "{{.TASK_VAR}}"
      - mkdir -p "{{.SH_TASK_VAR}}"
```

```
❯ task -t Taskfile.1803.yml repro
task: [repro] mkdir -p "file-issue-1803"
task: [repro] mkdir -p "task-issue-1803"
task: [repro] mkdir -p "sh-task-issue-1803"
task: [repro] rmdir ""
rmdir: failed to remove '': No such file or directory
task: [repro] rmdir "task-issue-1803"
task: [repro] rmdir "file-issue-1803"

❯ test -d sh-task-issue-1803 && echo 'directory still exists :('
directory still exists :(
```
You're totally right.
I've opened another PR to fix this!
woohoo! 🚀 

```
❯ task --version
Task version: v3.39.2 (h1:Zt7KXHmMNq5xWZ1ihphDb+n2zYLCo4BdRe09AnMMIgA=)

❯ task -t Taskfile.1803.yml repro
task: [repro] mkdir -p "file-issue-1803"
task: [repro] mkdir -p "task-issue-1803"
task: [repro] mkdir -p "sh-task-issue-1803"
task: [repro] rmdir "sh-task-issue-1803"
task: [repro] rmdir "task-issue-1803"
task: [repro] rmdir "file-issue-1803
```

Thanks @vmaerten for the quick turn around on this 💪 
It's still broken on snapcraft.io stable channel.
It doesn't seem to make sense for me to manually switch to the edge channel for this snap to get a working version, or no?!
@cwegener I just released on Snapcraft. Sorry, it was just forgotten.
Thanks! The stable snapcraft channel works again now..
This seems to still be broken for defer with tasks and pattern matching:
```
version: "3"
tasks:
  repro:
    vars:
      TASK_VAR: "OK"
    cmds:
      - defer: {task: "pattern-{{.TASK_VAR}}_task_eval" }
      - defer: {task: pattern-OK_task_fixed }
      - defer: echo deferred-{{.TASK_VAR}}_cmd_eval
      - defer: echo deferred-OK_cmd_fixed
      - echo task {{.TASK_VAR}}

  pattern-*:
    vars:
      OK: '{{index .MATCH 0}}'
    cmds:
    - cmd: "echo deferred: {{.OK}}"
```
Expected:
```
task OK
deferred-OK_cmd_fixed
deferred-OK_cmd_eval
deferred: OK_task_fixed
deferred: OK_task_eval
```
Actual:
```
task OK
deferred-OK_cmd_fixed
deferred-OK_cmd_eval
deferred: OK_task_fixed
deferred: _task_eval  #FAILURE
```
Version:
```
$ task --version
Task version: v3.39.2 (h1:Zt7KXHmMNq5xWZ1ihphDb+n2zYLCo4BdRe09AnMMIgA=)
```

Hi, I think that defer vars evaluation still have some issues. I'm using 3.41.0.
Here is a 
```
version: '3'

env:
  VAR3: Hey, there!

tasks:
  defer:debug:
    cmds:
      - echo '{{.VAR1}}'
      - echo '{{.VAR2}}'
      - echo '{{.VAR3}}'

  defer:main:
    cmds:
      - defer: {task: defer:debug, vars: {VAR1: '{{.VAR1}}', VAR2: 'value2', VAR3: '{{.VAR3}}'}}
      - echo '{{.VAR1}}'
      - echo '{{.VAR2}}'
      - echo '{{.VAR3}}'

  defer:sub:
    cmds:
      - task: defer:main
        vars:
          VAR1: 'sub1'
          VAR2: 'sub2'
          VAR3: 'sub3'
```

if I run `task defer:main VAR1=test VAR2=hi VAR3=hello`
I get this output:

```
test
hi
hello
test
value2
hello
```
and this is fine and it is what I was expecting.
But if I run `task defer:sub`
I get this output:
```
sub1
sub2
sub3

value2
Hey, there!
```
Basically, variables that are passed to a task from another task are ignored by the defer, while correctly used by other commends. In the deferred task, they are set to empty or to the value from env (if any). They are corrected evaluated if values are passed directly from CLI to the task with the defer.
If I remove defer, everything works as expected.
The "defer task with vars" problem was tracked in #1909 and fixed in [v3.43.1](https://github.com/go-task/task/releases/tag/v3.43.1)! 🚀




Patch:
diff --git a/task.go b/task.go
index d0f82a2d3a..0b24da0729 100644
--- a/task.go
+++ b/task.go
@@ -324,8 +324,13 @@ func (e *Executor) runDeferred(t *ast.Task, call *ast.Call, i int, deferredExitC
 	ctx, cancel := context.WithCancel(context.Background())
 	defer cancel()
 
+	origTask, err := e.GetTask(call)
+	if err != nil {
+		return
+	}
+
 	cmd := t.Cmds[i]
-	vars, _ := e.Compiler.FastGetVariables(t, call)
+	vars, _ := e.Compiler.FastGetVariables(origTask, call)
 	cache := &templater.Cache{Vars: vars}
 	extra := map[string]any{}
 


Test patch:
diff --git a/task_test.go b/task_test.go
index 33c3a3099b..c78cb38442 100644
--- a/task_test.go
+++ b/task_test.go
@@ -1788,7 +1788,7 @@ func TestExitCodeZero(t *testing.T) {
 	require.NoError(t, e.Setup())
 
 	require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "exit-zero"}))
-	assert.Equal(t, "EXIT_CODE=", strings.TrimSpace(buff.String()))
+	assert.Equal(t, "FOO=bar - EXIT_CODE=", strings.TrimSpace(buff.String()))
 }
 
 func TestExitCodeOne(t *testing.T) {
@@ -1802,7 +1802,7 @@ func TestExitCodeOne(t *testing.T) {
 	require.NoError(t, e.Setup())
 
 	require.Error(t, e.Run(context.Background(), &ast.Call{Task: "exit-one"}))
-	assert.Equal(t, "EXIT_CODE=1", strings.TrimSpace(buff.String()))
+	assert.Equal(t, "FOO=bar - EXIT_CODE=1", strings.TrimSpace(buff.String()))
 }
 
 func TestIgnoreNilElements(t *testing.T) {
diff --git a/testdata/exit_code/Taskfile.yml b/testdata/exit_code/Taskfile.yml
index ce4568d0c4..e78bd2cd85 100644
--- a/testdata/exit_code/Taskfile.yml
+++ b/testdata/exit_code/Taskfile.yml
@@ -7,11 +7,15 @@ vars:
 
 tasks:
   exit-zero:
+    vars:
+      FOO: bar
     cmds:
-      - defer: echo {{.PREFIX}}{{.EXIT_CODE}}
+      - defer: echo FOO={{.FOO}} - {{.PREFIX}}{{.EXIT_CODE}}
       - exit 0
 
   exit-one:
+    vars:
+      FOO: bar
     cmds:
-      - defer: echo {{.PREFIX}}{{.EXIT_CODE}}
+      - defer: echo FOO={{.FOO}} - {{.PREFIX}}{{.EXIT_CODE}}
       - exit 1