Skip to content

Conditional inclusions #2880

Description

@hookenz

Description

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.

Activity

  1. trulede commented on Jun 12, 2026

    @trulede
    Contributor

    Combine "conditional inclusions" with "optional inclusions" and see if you can find a way that works.

    version: '3'
    var:
      DOCKER:
        sh: "docker" # somehow like this  -  if .dockerenv then echo "docker"
    includes:
      build: ./Taskfile_{{DOCKER}}.yml
      optional: true
    
  2. added theissue type on Jun 12, 2026
  3. cschanhniem commented on Jun 18, 2026

    @cschanhniem

    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:

    vars:
      IN_DOCKER:
        sh: '[ -f /.dockerenv ] && echo "docker" || true'
    includes:
      devcontainer:
        taskfile: ./Taskfile_{{.IN_DOCKER}}.yml
        optional: true

    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:

    includes:
      devcontainer:
        taskfile: ./Taskfile_devcontainer.yml
        optional: true
        when: '{{.IN_DOCKER}} != ""'

    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.

  4. vmaerten commented on Jul 19, 2026

    @vmaerten
    Member

    Hello !
    Re-open it if the solution from @trulede does not work for you

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Fields

    No fields configured for support.

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions