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
- Task uses
yamlformat, which has some quirks but is a well-established standard. - Each task is an entry in the YAML tree structure, where the key is the task name.
- All available tasks can be listed by running
task --list-all.
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.
