Task vs Make - Final Thoughts
Robert Nemet
Posted on August 9, 2023
So, after spending time moving from Make to Task, I decided. I'm going with Task in my future projects. Make will not be removed from my projects immediately, but I will not use it in the future. Let me tell you why.
All this is my personal opinion. I'm not saying that Task is better than Make. I think that Task is better for me.
Global Taskfile
I have a global Task in my home directory for standard stuff. It contains tasks to update my tools, run some diagnostics, etc. Because it is global, it is accessible from any directory. For example:
version: '3'
tasks:
build:
dir: '{{.USER_WORKING_DIR}}'
cmds:
- go build . -o app
In the above example, task -g build
will build the Golang app in my current working directory. I do not need to add Task for every project. I must add Taskfile to the project if I share it. But, as long it is experimental, there is no need. Anyway, storing everyday tasks as global is a nice feature.
The {{.USER_WORKING_DIR}}
is a variable set by Task. It is the current working directory. And the dir
slug defines the folder where the task will be executed. In this case, it is the current working directory. But you can set it to any folder you want. So you do not need to switch directories to run a task. Just set the dir
slug to the folder where you want to run the job. Neat!
Including/Grouping Task Files
The explicit inclusion of other Taskfiles with namespace is really neat. This way, you get a context of tasks. For example:
version: '3'
includes:
tools: ./Taskfile.tools.yml
docs: ./docs/
Assuming I have the task install_tools
in the file Taskfile.tools.yml
, I can run it with task tools:install_tools
. Also, suppose I have Taskfile.yml
file in the docs
directory. I can run it with task docs:build_docs
from the root directory. This way, I'm groping tasks by context.
In case you have a CI pipeline and need to do builds for different platforms. You can split your tasks into different files with the platform postfix. For example, you have different tasks for linux
and windows
platforms. You can split them into separate files. I name them Taskfile_linux.yml
and Taskfile_windows.yml
. Then you can include them in the main Taskfile.yml
file with the following:
version: '3'
includes:
build: ./Taskfile_{{OS}}.yml
If you run task build:build
on the linux
platform, it will include Taskfile_linux.yml
and run the build
task from it. If you run the same task on windows
platform, it will consist of Taskfile_windows.yml
and run the build
job from it. This way, you can have different tasks for different platforms.
Task Dependencies and Skipping Done Tasks
Task dependencies are really nice. You can define a task that depends on other tasks. For example:
version: '3'
tasks:
manifests:
desc: Generate manifests e.g. CRD, RBAC etc.
deps:
- task: tools:controller-gen
cmds:
- controller-gen rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases
sources:
- main.go
- apis/**/*.go
- pkg/**/*.go
generates:
- config/crd/bases/**/*.yaml
The manifests
task in the above example depends on the tools:controller-gen
task. Dependency tasks are run in parallel. This way, you can define tasks that rely on other tasks.
Also, the above example shows how to define sources and generated files. The sources
slug defines files used to generate other files. The generates
slug defines files that are generated by the task. This way, you can tell Task which files are used to generate other files. It is to determine whether the task needs to be run. Task will generate a fingerprint for sources and compare it next time the task
needs to be run. The task is considered done if the fingerprint is not changed, assuming the generated files are present.
Also, there is an alternative with a status
slug. You can define a command that will return 0
if the task is done. For example:
install-cert-manager:
desc: Install cert-manager
deps:
- init-cluster
cmds:
- kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/{{.CERT_MANAGER_VERSION}}/cert-manager.yaml
- echo "Waiting for cert-manager to be ready" && sleep 25
status:
- kubectl -n cert-manager get pods | grep Running | wc -l | grep -q 3
Golang Template Engine
Task uses the Golang template engine. As a Golang user, this is a big plus. I can use all the features of the Golang template engine plus one that Task provides, like including functions from https://go-task.github.io/slim-sprig/
. For example:
version: '3'
tasks:
print-date:
cmds:
- echo {{now | date "2006-01-02"}}
- echo '{{OS}} {{ARCH}}'
Format Is YAML
I like it when a file has a structure and text editors can understand it. I can understand it as well. It is enough said.
Conclusion
There are other things that I like about Task but I will not go into details. I have covered the most important ones, at least for me.
While evaluating Makefile and Task, I discovered another tool called Just. It is more similar to Make than Task. For that reason, I did not want to spend a lot of time on it. But I see it as a good alternative to Make. Especially if you do not like YAML.
Let me know what you think. Thanks for reading. Enjoy!
References
Posted on August 9, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.