task icon indicating copy to clipboard operation
task copied to clipboard

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

Open ProfessorManhattan opened this issue 4 years ago • 23 comments
trafficstars

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' ]]'

ProfessorManhattan avatar Nov 13 '21 13:11 ProfessorManhattan

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.

andreynering avatar Dec 30 '21 20:12 andreynering

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.

ghostsquad avatar Jan 07 '22 06:01 ghostsquad

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.

andreynering avatar Jan 11 '22 14:01 andreynering

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.

ProfessorManhattan avatar Jan 16 '22 01:01 ProfessorManhattan

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".

ssbarnea avatar Jun 14 '22 10:06 ssbarnea

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

  • https://github.com/go-task/task/issues/978
  • https://github.com/go-task/task/pull/980

andreynering avatar Jan 05 '23 01:01 andreynering

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...

max-sixty avatar Jan 06 '23 19:01 max-sixty

@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 https://github.com/go-task/task/pull/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.

titpetric avatar Jun 30 '23 11:06 titpetric

@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.

andreynering avatar Jun 30 '23 12:06 andreynering

FYI #1220 has been merged

JonZeolla avatar Aug 01 '23 22:08 JonZeolla

FYI #1220 has been merged

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

max-sixty avatar Aug 01 '23 22:08 max-sixty

Right; I'm just saying this is ready for development per this comment

JonZeolla avatar Oct 25 '23 13:10 JonZeolla

Right; I'm just saying this is ready for development per this comment

Sorry, I didn't read up far enough. Thanks @JonZeolla

max-sixty avatar Oct 25 '23 17:10 max-sixty

if keyword have any progress?

thinkgos avatar Feb 06 '24 08:02 thinkgos

status kind of works, but its interactions with --force and sources make it unviable.

A when (more presence than if) that hooks to the same logic that status currently has, would be suitable. Any preference? If I can figure out the code, I might try create a PR.

timrulebosch avatar Mar 22 '24 10:03 timrulebosch

status kind of works, but its interactions with --force and sources make it unviable.

A when (more presence than if) that hooks to the same logic that status currently has, would be suitable. Any preference? If I can figure out the code, I might try create a PR.

After reading though the code, and considering the problem a bit more, there seem to be 3 possible paths:

  1. Add a new keyword like "if" or "when". I'm not convinced this is a good idea for the reasons; the calling task may need to know about the called task (and that might not be knowable until runtime) so that could end up being a mess, and as soon as there is an "if" ... we can be sure "else", "switch", "or" and friends will follow.
  2. Add a "skip" which is a duplication of the logic behind "status", but behaves independently of the "--force" flag. That gets the desired behaviour ... but ... conflicts a little with ...
  3. Add a "skip" attribute to the existing preconditions and change the behaviour of preconditions such that if all preconditions which fail have that skip attribute (set to true), then the task is skipped rather than erroring.

To my mind, adding a "skip" attribute to preconditions, and adjusting the behavior of precondition evaluation, is the better option.

trulede avatar Mar 29 '24 11:03 trulede