You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
There is a nice little feature that you support currently is preconditions.
e.g.
preconditions:
- sh: "[ ! -f /.dockerenv ]"
msg: "Run this from the host, not inside the devcontainer."
It would be cool to apply this to an include. For example, so I can how some tasks apply only to the task when running inside a devcontainer. Or alternatively outside a devcontainer.
Or apply to whatever precondition you define.
e.g.
includes:
preconditions:
- sh: "[ ! -f /.dockerenv ]"
msg: "Run this from the host, not inside the devcontainer."
lib:
taskfile: ./Included.yml
flatten: true
Unless you have a better alternative idea or is there something I can do to make this work regardless?
And why would I want to do this you ask?
Well in situations like these:
dev:
desc: Run dev containers (run from host, not inside devcontainer)
preconditions:
- sh: "[ ! -f /.dockerenv ]"
msg: "Run this from the host, not inside the devcontainer. The dev servers should already be running."
cmds:
- docker compose up --build
I'd like to hide the "dev" option or make it not even available or even show up in the help if the precondition is false.
The design tension here is that includes are resolved during taskfile loading (in taskfile/reader.go::include()), while preconditions are evaluated during task execution. So naively porting preconditions: to the includes: block means evaluating shell commands at parse time — which changes the loading model and introduces ordering issues (e.g., if the precondition depends on a task you haven't run yet).
@trulede's template-variable + optional: true approach works for cases where the decision can be reduced to a variable. The current vars: system supports sh: for dynamic variables, so you could express the devcontainer check as:
But this is fragile — when IN_DOCKER is empty, the include resolves to ./Taskfile_.yml which likely doesn't exist but could accidentally match a real file.
A cleaner design would be adding a when field to the Include struct that takes a Go template expression evaluated against the existing vars/globals, evaluated during include resolution (not task execution). This keeps the evaluation at load time but uses the template engine rather than shell, avoiding the need to spawn subprocesses during loading:
The when field would be a string template expression, evaluated in reader.go::include() before the goroutine spawns. If the expression evaluates to "false" or an empty string, the include is skipped (same as optional: true with missing file). This is consistent with how Taskfile already uses Go templates for interpolation and avoids the architectural complexity of deferred include resolution.
The type change would be minimal: add When string to ast.Include, add when to the YAML decoding in ast.Include.UnmarshalYAML, and add a conditional skip in reader.go just before the goroutine spawns.
Happy to sketch a PR if this direction makes sense.
Activity
trulede commentedon Jun 12, 2026
Combine "conditional inclusions" with "optional inclusions" and see if you can find a way that works.
cschanhniem commentedon Jun 18, 2026
The design tension here is that includes are resolved during taskfile loading (in
taskfile/reader.go::include()), while preconditions are evaluated during task execution. So naively portingpreconditions:to theincludes:block means evaluating shell commands at parse time — which changes the loading model and introduces ordering issues (e.g., if the precondition depends on a task you haven't run yet).@trulede's template-variable +
optional: trueapproach works for cases where the decision can be reduced to a variable. The currentvars:system supportssh:for dynamic variables, so you could express the devcontainer check as:But this is fragile — when
IN_DOCKERis empty, the include resolves to./Taskfile_.ymlwhich likely doesn't exist but could accidentally match a real file.A cleaner design would be adding a
whenfield to theIncludestruct that takes a Go template expression evaluated against the existing vars/globals, evaluated during include resolution (not task execution). This keeps the evaluation at load time but uses the template engine rather than shell, avoiding the need to spawn subprocesses during loading:The
whenfield would be a string template expression, evaluated inreader.go::include()before the goroutine spawns. If the expression evaluates to"false"or an empty string, the include is skipped (same asoptional: truewith missing file). This is consistent with how Taskfile already uses Go templates for interpolation and avoids the architectural complexity of deferred include resolution.The type change would be minimal: add
When stringtoast.Include, addwhento the YAML decoding inast.Include.UnmarshalYAML, and add a conditional skip inreader.gojust before the goroutine spawns.Happy to sketch a PR if this direction makes sense.
vmaerten commentedon Jul 19, 2026
Hello !
Re-open it if the solution from @trulede does not work for you