From 5a0832331d78774f981c831fd60167e4f1b25878 Mon Sep 17 00:00:00 2001 From: Sai Asish Y Date: Fri, 17 Apr 2026 00:05:31 -0700 Subject: [PATCH] fix(compiler): create task.dir before running dynamic (sh:) vars A task's 'dir:' is documented as being created on demand, but dynamic variables (sh:) compile before the task's mkdir runs. With a non-existent dir, the shell exec aborts with: task: Command "cat ../exist.txt" failed: chdir /path/does-not-exist: no such file or directory even though the same command inside cmds: works fine because mkdir runs first. Create the directory (same 0o755 perms as the main task.mkdir uses) in HandleDynamicVar before launching the shell, so sh: variables and cmds: see the same filesystem state. Fixes #1001 Signed-off-by: Sai Asish Y --- compiler.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/compiler.go b/compiler.go index 733d5f3b21..b8da6cebd1 100644 --- a/compiler.go +++ b/compiler.go @@ -166,6 +166,22 @@ func (c *Compiler) HandleDynamicVar(v ast.Var, dir string, e []string) (string, dir = v.Dir } + // The task's `dir:` is documented as "Directory which this task should + // run. Defaults to the current working directory. If the directory does + // not exist, Task creates it." Dynamic variables (`sh:`) compile before + // the task's `mkdir` runs, so without this guard the shell executes + // with Dir pointing at a non-existent path and aborts with a cryptic + // chdir error. Create the directory here with the same 0o755 perms the + // main task mkdir uses, so `sh:` variables behave like task commands. + // See https://github.com/go-task/task/issues/1001. + if dir != "" { + if _, err := os.Stat(dir); os.IsNotExist(err) { + if err := os.MkdirAll(dir, 0o755); err != nil { + return "", fmt.Errorf(`task: cannot make directory %q for dynamic variable: %w`, dir, err) + } + } + } + var stdout bytes.Buffer opts := &execext.RunCommandOptions{ Command: *v.Sh,