Absolute path bug when using include and dir
Description
When setting an absolute directory (ex. /tmp) from a variable and using that in the dir: directive of an included taskfile, it is treated as a relative path. However, it is dependent on the syntax of the include. Minimal working example included below.
When included using this syntax, the variable is treated as a relative path:
vars:
TARGET_DIR: '/tmp'
includes:
broken:
taskfile: ./Taskfile.broken.yml
When used like this:
tasks:
broken:
dir: "{{.TARGET_DIR}}"
cmds:
- |
echo "PWD DIR: $(pwd)"
Version
3.45.4
Operating system
OSX 26
Experiments Enabled
No response
Example Taskfile
# Taskfile.yml
version: '3'
vars:
TARGET_DIR: '/tmp'
includes:
working: ./Taskfile.working.yml
broken:
taskfile: ./Taskfile.broken.yml
# this also doesn't fix it
#vars:
# TARGET_DIR: "{{.TARGET_DIR}}"
tasks:
default:
cmds:
- task: working:working
- task: broken:broken
# Taskfile.working.yml
version: '3'
tasks:
working:
dir: "{{.TARGET_DIR}}"
cmds:
- |
echo "TARGET_DIR: {{.TARGET_DIR}}"
echo "PWD DIR: $(pwd)"
cd {{.TARGET_DIR}}
echo "PWD AFTER CD: $(pwd)"
# Taskfile.broken.yml
version: '3'
tasks:
broken:
dir: "{{.TARGET_DIR}}"
cmds:
- |
echo "TARGET_DIR: {{.TARGET_DIR}}"
echo "PWD DIR: $(pwd)"
cd {{.TARGET_DIR}}
echo "PWD AFTER CD: $(pwd)"
I have identified a second bug but it feels potentially related to the same code path.
When using a variable in the dir of an included task, it does not apply to dynamic vars but does to the cmds, even when using the simple include syntax. Changing to dir: /tmp works as expected.
# Taskfile.yml
version: '3.45.4'
vars:
TARGET_DIR: /tmp
includes:
broken: ./Taskfile.broken.yml
tasks:
default:
cmds:
- task: broken:broken
# Taskfile.broken.yml
version: '3.45.4'
tasks:
broken:
dir: "{{.TARGET_DIR}}"
vars:
TEST_DIR:
sh: echo $(pwd)
cmds:
- echo {{.TEST_DIR}}
- echo $(pwd)
This line is causing the observed behaviour: https://github.com/go-task/task/blob/4e84c6bb766e10a6a9b4b08bfe75519a240ceff8/taskfile/ast/tasks.go#L174
Variable AdvancedImport is set for the expanded style of imports.
However, this is probably as designed (if not expected). @roycamp I think you get the expected behaviour if you do this:
---
version: '3'
vars:
TARGET_DIR: '/tmp'
includes:
working: ./Taskfile.working.yml
broken:
taskfile: ./Taskfile.broken.yml
dir: "{{.TARGET_DIR}}"
tasks:
default:
cmds:
- task: working:working
- task: broken:broken
---
# Taskfile.working.yml
version: '3'
tasks:
working:
dir: "{{.TARGET_DIR}}"
cmds:
- |
echo "PWD DIR: $(pwd)"
---
# Taskfile.broken.yml
version: '3'
tasks:
broken:
cmds:
- |
echo "PWD DIR: $(pwd)"
Produces output:
$ task
task: [working:working] echo "PWD DIR: $(pwd)"
PWD DIR: /tmp
task: [broken:broken] echo "PWD DIR: $(pwd)"
PWD DIR: /tmp
However, one might suggest that the above linked code:
if include.AdvancedImport {
task.Dir = filepathext.SmartJoin(include.Dir, task.Dir)
which is called with parameter task.Dir = "{{.TARGET_DIR}}" is not operating as one might expect. The variable task.Dir should probably be expanded before this function is called; the variable is there (line 179) but I don't think the templating system is available (CompileTask) at that point.
I think this is a bug. The solution might be to calculate the task.Dir later, when template expansion is available.
@roycamp I've developed a fix. You might try it ... it probably breaks things somewhere else ...
As an aside, I observed a few other strange things. I would suggest refactoring the "get variables" code out of compiler.go into its own file/module and design something that can handle the complexity a little better. A simple strategy pattern or similar.
Thanks, @trulede! I can confirm this fixes both bugs for me.
Defining dir at the include level would not meet my use case of reusing a task across multiple directories. Thank you for the quick turn around!
Any ETA for that fix? I wanted to refactor my taskfiles and I have the same problem as described by the author.
@maciej-lech this is a well known problem that has been around for a long time, everyone encounters it ... and just find a way around it (or give up I guess). You can see here, many of them : https://github.com/go-task/task/issues?q=is%3Aissue%20state%3Aopen%20include%20dir
You can try the PR I wrote. It works, however some unit tests fail, I did not investigate why. Its possible that those tests depend on the changed behaviour, and its also possible the PR breaks something. If you identify the later, I can help resolve it.