Modern approach to running tasks. Exploring “task”

This post is about a task runner named task. I haven’t used it for too long, but it impressed me enough to write an article about it.

The idea is simple: I write down my tasks in a file, specify what commands I want them to run, and then I call a program to run it. Just like make and Makefiles. Or like GitLab and .gitlab-ci.yml. With task, it is the task program and taskfile.yml.

Features of task

This is basically a TL;DR for the usage documentation for taskfile, focusing on my favorite things.

General

Dependencies

Since tasks are named, they can be referenced by their names, like this: deps: [assets].

Platform awareness

task knows on which platform it runs, so I can delegate all the OS-detection logic to the task runner. No more if statements for each OS.

Fingerprinting

It is basically the main feature of make: run a task only if the files have changed. In this case, however, there is actually an explicit syntax:

    sources:
      - src/**/*.c
      - src/**/*.h
    generates:
      - build/main

With that, task will happily report task: Task "build" is up to date if nothing has changed.

Dynamic variables

Now I do not have to read variables at the beginning of a task definition. Instead, if I need to read something, I can explicitly assign it to a variable:

vars:
  BUILD_DATE:
    sh: date +%Y-%m-%d

Looping

There are many examples of loop definitions supported by task. They provide a way for creating task templates.

CLI arguments

Using task, I can include configuration variables in the task name:

tasks:
  demo-*:
    vars:
      ID: '{{index .MATCH 0}}'
    cmds:
      echo demo-{{.ID}}

It works through pattern matching, so there are limitations to this approach. Unfortunately, I could not figure out a way to validate variables passed in this way.

Cleanup with defer keyword

It works exactly as you would expect if you worked with go. Inside of the task, you can include commands that will be executed when task is finished, regardless if it succeeded or not.

Confirmation prompts

No more writing custom yes/no prompts. Taskfile supports it out-of-the-box:

  clean:
    cmds:
      - rm -rf build
    prompt: This will remove all build artifacts... Do you want to continue?

It will make task ask for confirmation before running commands:

$ task clean
This will remove all build artifacts... Do you want to continue? [y/N]:

Summary

I do not like complexity. task makes it easy to write complex taskfiles, as well as simple ones. Most importantly, it does not enforce anything, and that is the important thing.

task comes as a simple binary. taskfiles are more readable than Makefile and more maintainable than a bunch of shell scripts.