Skip to content

Add if: keyword to control when a task or command is ran? #608

Closed
1 of 1 issue completed
Closed

Description

@ProfessorManhattan

There are some cases where task: is called under cmds: and the task needs to be conditionally run. Take the following as an example:

---
version: '3'

tasks:
  python:requirements:
    deps:
      - :install:software:python
    run: once
    cmds:
      - task: python:requirements:poetry
      - task: :{{if eq .REPOSITORY_TYPE "ansible"}}common:python:requirements:ansible{{else}}donothing{{end}}
    status:
      - '[[ "${container:=}" == "docker" ]]'

I'm currently getting around the issue by having a task called donothing that gets called when the task should not run. It would be nice if we could conditionally run tasks somehow. Maybe something like this:

---
version: '3'

tasks:
  test:
    cmds:
      - task: anothertask
        when:
          sh: '[[ "$CONTAINER" == 'docker' ]]'

Activity

  1. changed the title [-]Ability to Run Tasks Conditionally[/-] [+]Add `if:` keyword to control when a task or command is ran?[/+] on Dec 30, 2021
  2. andreynering commented on Dec 30, 2021

    @andreynering
    Member

    Hi @ProfessorManhattan,

    Something I had in mind for a while is that we could potentially have an if keyword for tasks and commands:

    version: '3'
    
    tasks:
      foo:
        cmds:
          - echo 'bar'
        if: '[[ "$ENV" == "bar" ]]'

    or

    version: '3'
    
    tasks:
      foo:
        cmds:
          - cmd: echo 'bar'
            if: '[[ "$ENV" == "bar" ]]'

    NOTE: We already have preconditions, which is similar but makes the call fail when an error is returned, which means if still makes sense to have for skipping without failing.

  3. ghostsquad commented on Jan 7, 2022

    @ghostsquad
    Contributor

    isn't that what status is supposed to do? decide to skip a step?

    Unlike status which will skip a task if it is up to date, and continue executing tasks that depend on it, a precondition will fail a task, along with any other tasks that depend on it.

  4. andreynering commented on Jan 11, 2022

    @andreynering
    Member

    isn't that what status is supposed to do? decide to skip a step?

    You have a good point, we'd have some intersection between these two features.

    status currently prints Task "foo" is up-to-date messages, though. This is accurate for statuses but not for other reasons why you'd want to skip a task.

    Also, if would be the opposite of status with regarding the expected status: if runs on zero, status runs on non-zero statuses.

    So, I don't have a strong opinion, but maybe it would still make sense to have both if and status. Alternatively, we could add if: only to commands and not to the task itself.

  5. ProfessorManhattan commented on Jan 16, 2022

    @ProfessorManhattan
    Author

    Thanks @andreynering

    Another thing we might want to keep in mind is handling the --force command line parameter. Currently, this will just ignore status which forces the user to wrap everything in ifs (even using status) for logic that doesn't relate to a particular project. In my use case, I share the same set of Taskfiles across hundreds of repositories of many different types - it is not feasible to generate the Taskfiles for each project type.. it would take too long.

    I would like to see an if: that can be added at the least to the task and cmd level. It should also not be impacted by any --force logic. Take the following example:

    ---
    version: '3'
    
    tasks:
      mytask:
        cmds:
          - gsettings set org.gnome.settings-daemon.plugins.color night-light-enabled true
        if:
          - ls /usr/bin/*session | grep gnome-session
        status:
          - [[ $(gsettings get org.gnome.settings-daemon.plugins.color night-light-enabled) == 'true' ]]
    

    Both the if: and status: are used in this example. The if: parameter makes sure GNOME is being run on the system and the status: is used to achieve idempotency. This would make even more sense if we were running a command that requires a reboot. In this example, we would need the --force command to obey the if: or else we would get an error if GNOME is not on the system.

    Currently, with any complex flows that rely on status, things can easily get messy when using --force but the --force command could potentially be pretty useful for cache busting. I see adding an if: parameter as a possible solution.

  6. ssbarnea commented on Jun 14, 2022

    @ssbarnea
    Contributor

    Probably the best use-case for if would be to add steps that depend only on specific platforms, like Windows, Linux, MacOS. Adding extra code for platform on each called shells script is a PITA, one that could be easily avoided with an "if".

  7. andreynering commented on Jan 5, 2023

    @andreynering
    Member

    For those interested, there's an ongoing discussion about a possible key specificly to select given OS/Arch on a task or command:

  8. max-sixty commented on Jan 6, 2023

    @max-sixty

    A +1 for if.

    I really over-use status, which requires some mental gymnastics ("Only run this on ARM machines, which means status needs to be 0 on x86 machines, so raise an error on ARM"!), and isn't compatible with --force ("now we're running it on x86 machines?!")

    Though I do empathize that it still has lots of overlap with status...

  9. titpetric commented on Jun 30, 2023

    @titpetric

    @andreynering i have a couple of env vars where if would be useful. I'd be willing to open up a PR if there isn't anything in flight, going with your proposal in the reply. In combination with #1220 this would allow us to put most of our release pipeline into taskfiles, enabling matrix tests and targeting along with that "run offline" concept.

  10. andreynering commented on Jun 30, 2023

    @andreynering
    Member

    @titpetric Contributions are welcome! I would advise you to wait until #1220 is merged to avoid any conflicts. Also, that would allow you to test both features together.

  11. JonZeolla commented on Aug 1, 2023

    @JonZeolla
    Contributor

    FYI #1220 has been merged

  12. max-sixty commented on Aug 1, 2023

    @max-sixty

    FYI #1220 has been merged

    Though IIUC this is a bit different — for rather than if?

  13. 7 remaining items

  14. trulede commented on Oct 18, 2024

    @trulede
    Contributor

    I would not go down the path of IF and ELSE. If you really need such a complexity then just write a bash script and be done with it.

    Better is to use STATUS to evaluate the condition for which a TASK should run. That effectively gives you the IF/ELSE logic you need, and you will get a better outcome. So, for you that might mean a TASK where the status checks for the absence of an installed dependency, and when missing, the commands install that dependency.

    This is I think, not very complicated to try.

  15. Firesphere commented on Oct 24, 2024

    @Firesphere

    I don't think status covers what an if would mean.
    Aside from semantics, where using "status" as an "if" simply doesn't make sense, I think an if option would be very useful.

    If I take the following:

    tasks:
      generate:
        cmds:
          - git checkout branchname/{{.RELEASE}}
          - if: {{.TAG eq "true"}}
            cmd: git tag -a {{.RELEASE}} -m "Tagging {{.RELEASE}}
          - git push -a
    

    That if construct is a lot more readable than using a status, which does a test, and furthermore, it allows me to run task generate TAG=true RELEASE=1.2.3

    Currently, the task is constructed like so

    tasks:
      generate:
        cmds:
          - git checkout branchname/{{.RELEASE}}
          - {{if .TAG | eq "true"}}git tag -a {{.RELEASE}} -m "Tagging {{.RELEASE}}{{end}}
          - git push -a
    

    The tag command is a lot less readable, causing more chance of confusion and errors

  16. Diaphteiros commented on Mar 10, 2025

    @Diaphteiros

    I would also like to have another kind of conditional in addition to status and preconditions.

    My usecase is that I'm working on a library-style shared repository with Taskfiles, which can then be imported in all of our projects, so that we have the same tasks available everywhere.

    I have a task generate-code which runs some kind of code generation, and also a generate-docs one. However, I don't want to call both of them manually, so I also have a task generate which simply combines both of them.

      generate:
        desc: "Run code generation and documentation generation."
        <...>
        cmds:
        - task: generate-code
        - task: generate-docs

    The problem is that not all of our projects are configured to generate documentation, so that task needs to check whether it should run or not.

    I can use status for this, however it will print task: Task "generate-docs" is up to date when run in repos without documentation generation configured, which does not quite fit the situation, and more importantly, it will break when run with --force, which a developer might want to use for other reasons.

    I cannot use preconditions, since this would cause the task to fail, which is not what I want here.

    I cannot use a shell if, because I'm using the task: generate-docs syntax in the generate task and there is no shell involved. With the above example, I could have the generate-docs task run a shell script which checks the condition and just does nothing if the corresponding configuration is missing, but this is just a simplified example and there are scenarios with more deeply nested task structures, where this is not feasible.

    To solve this, I'd like to have an option to skip the execution of a specific task based on a condition, but it must also skip the task when --force is set and must skip it without failing it. I don't really care whether this happens in form of a new field for either status or preconditions, or as a completely new field if/when/skip/whatever.

  17. Diaphteiros commented on Mar 10, 2025

    @Diaphteiros

    @gedw99 I'm not sure I fully understand what you mean. Can you give me an example somehow?

  18. DerpgonCz commented on May 21, 2025

    @DerpgonCz

    To add to this, GitLab CI has a nice system for deciding whether to add a job to the pipeline: https://docs.gitlab.com/ci/jobs/job_rules/#rules-examples

    Basically:

    1. Iterate through all rules
      1.. If a rule's if condition succeeds, decide what to do based on when
      • on_success - add the job to the pipeline if no previous pipeline failed (default behavior)
      • on_failure - add the job to the pipeline if any previous pipeline failed
      • never - DO NOT add the job to the pipeline, regardless of any previous status
      • always - add the job to the pipeline, regardless of any previous status
      • manual - add the job to the pipeline, but make it a manual action (have to click in UI)
      • delayed - add the job to the pipeline, but run with a delay
    2. If a when does not match (only for on_success and on_failure), continue checking rules
    3. If no rule's if matches, do not add the the job to the pipeline

    This could be used (with minor alterations) for Task. But since there is no "failed" or "success" state (at least not being worked with, it is either success or the whole task ... command fails and execution is halted), it could be renamed to mode, with values fail (raise an error), skip (silently skip the task), warn (skip the task, show a warning, but continue execution). Possibly even force to always run and skip all other checks.

    It could even be combined with rest of conditional executions - like status, deps, preconditions, sources, and generates.

    tasks:
      install:
        rules:
          # Skip when packages are installed
          - if: "test -d node_modules"
            mode: skip
          # Runs by default, imagine an "- if: test 1 = 1"
      build:
        rules:
          # Skip on PROD
          - if: "test {{ .ENV }} = PROD"
            mode: skip
          # Only rebuild when files changed
          - sources: src/**/*
            generates: dist/main.js
        cmd: yarn build

    Currently, we have status (skip on success, execute on failure), preconditions (execute on success, fail on failure), deps (same as preconditions, but those are tasks and run in parallel).

  19. vitonsky commented on Dec 6, 2025

    @vitonsky

    Is there any updates on it?

    Issue was created in 2021, it's about 5 years ago. Will it be implemented?

  20. added
    area: execChanges related to the execution of commands.
    on Dec 7, 2025
  21. andreynering commented on Dec 7, 2025

    @andreynering
    Member

    @vitonsky We for sure want to have this. It's mostly a matter of finding the time to implement it.

  22. marked Switch statements #2345 as a duplicate of this issue on Dec 8, 2025
  23. andreynering commented on Jan 21, 2026

    @andreynering
    Member

    Hello everyone. This is finally implemented on:

    It should be released soon!

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

    area: execChanges related to the execution of commands.

    Fields

    No fields configured for feature.

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions