
1. What is Taskfile
Taskfile describes various execution tasks using YAML and is primarily written in Go. Compared to Makefile which uses tab-separated and bash syntax, Taskfile appears more modern and user-friendly (although it may turn you into a YAML engineer). Taskfile has built-in advanced features such as dynamic variables and recognition of operating system environment variables, making it more aligned with modern coding practices.
Overall, if you are not very familiar with Makefile and wish to accomplish batch tasks using a similar tool, Taskfile is more beginner-friendly with a lower learning curve and sufficient speed.
2. Installation and Usage
Installing go-task
For Mac users, the official installation method is provided via brew:
$ brew install go-task/tap/go-task
For Linux users, the official site provides installation packages for some Linux distributions. However, since it consists of only one binary file, a quick installation script is also provided:
# For Default Installation to ./bin with debug logging
$ sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d
# For Installation To /usr/local/bin for userwide access with debug logging
# May require sudo sh
$ sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b /usr/local/bin
If you already have a Go development environment set up locally, you can also install it directly using the go command:
$ go install github.com/go-task/task/v3/cmd/task@latest
Quick Start
After installation, you only need to create a Taskfile.yml
YAML file and you can run the corresponding tasks using the task
command:
version: '3'
tasks:
build:
cmds:
- echo "Executing build task"
docker:
cmds:
- echo "Packaging docker image"

If you need to set a default task, simply create a task named default
:
version: '3'
tasks:
default:
cmds:
- echo "This is the default task"
build:
cmds:
- echo "Executing build task"
docker:
cmds:
- echo "Packaging docker image"

3. Advanced Usage
Environment Variables
Taskfile supports referencing three types of environment variables:
-
Shell environment variables -
Environment variables defined within Taskfile -
Environment variables defined in variable files
To reference a shell environment variable, simply use the $ variable_name
syntax:
version: '3'
tasks:
default:
cmds:
- echo "$ABCD"

You can also define environment variables within Taskfile:
version: '3'
env:
TENV2: "t2" # Global environment variable
tasks:
default:
cmds:
- echo "$TENV1"
- echo "$TENV2"
env:
TENV1: "t1" # Single task environment variable

In addition to directly referencing variables, Taskfile also supports loading environment variables from an env file, similar to docker-compose; Taskfile will automatically load the .env
file in the same directory, and you can configure specific files using the dotenv
command within Taskfile:
version: '3'
dotenv: [".env", ".testenv"]
tasks:
default:
cmds:
- echo "$ABCD"
- echo "$TESTENV"

Enhanced Variables
In addition to standard environment variables, Taskfile also includes a widely used enhanced variable called vars
; this variable pattern can be read (interpolated) using Go’s template engine and has special features not available in environment variables. Below is an example of vars
variable:
version: '3'
# Global var variable
vars:
GLOBAL_VAR: "global var"
tasks:
testvar:
# task var variable
vars:
TASK_VAR: "task var"
cmds:
- "echo {{.GLOBAL_VAR}}"
- "echo {{.TASK_VAR}}"
In addition to the usage similar to environment variables, vars
enhanced variables also support dynamic definitions; a common scenario is to obtain the current git commit id every time a task is executed, which can be achieved using the dynamic definition feature of vars
:
version: '3'
tasks:
build:
cmds:
- go build -ldflags="-X main.Version={{.GIT_COMMIT}}" main.go
vars:
# Every time the task is executed, GIT_COMMIT will call a shell command to generate this variable
GIT_COMMIT:
sh: git log -n 1 --format=%h
The vars
variable also has some predefined special variables, such as {{.TASK}}
which always represents the current task name, and {{.CLI_ARGS}}
which can reference command line inputs.
version: '3'
tasks:
yarn:
cmds:
- yarn {{.CLI_ARGS}}
In this case, if you run task yarn -- install
, the value of {{.CLI_ARGS}}
will become install
, thereby executing the yarn install
command.
Additionally, the vars
variable has other features, such as allowing for cross-task references and overrides, which will be introduced later.
Execution Directory
By default, tasks defined in Taskfile are executed in the current directory. If you wish to execute in another directory, you can set the execution directory directly using the dir
parameter without manually writing cd
commands:
version: '3'
tasks:
test1:
dir: /tmp # Execute in specified directory
cmds:
- "ls"
Task Dependencies
In CI environments, we often need to define the execution order and dependencies of tasks; Taskfile provides support for task dependencies through the deps
configuration:
version: '3'
tasks:
build-jar:
cmds:
- echo "Compiling jar package..."
build-static:
cmds:
- echo "Compiling frontend UI..."
build-docker:
deps: [build-jar, build-static]
cmds:
- echo "Packaging docker image..."
Task Invocation
When we define multiple tasks in Taskfile, some tasks may have certain similarities. In this case, we can define template tasks by allowing tasks to call each other and dynamically override vars
variables:
version: '3'
tasks:
docker:
cmds:
#- docker build -t {{.IMAGE_NAME}} {{.BUILD_CONTEXT}}
- echo {{.IMAGE_NAME}} {{.BUILD_CONTEXT}}
build-backend:
cmds:
- task: docker # Reference another task
vars: { # Dynamically pass variables
IMAGE_NAME: "backend",
BUILD_CONTEXT: "maven/target"
}
build-frontend:
cmds:
- task: docker
vars: {
IMAGE_NAME: "frontend",
BUILD_CONTEXT: "public"
}
default: # Default task to call when no task name is provided
cmds:
- task: build-backend
- task: build-frontend

Including Other Files
Taskfile supports including other Taskfiles via the includes
keyword, facilitating structured handling of Taskfiles.
Note that since the included files may contain multiple tasks, you need to name the included files and reference the target tasks by name:
version: '3'
includes:
file1: ./file1.yaml # Directly reference yaml file
dir2: ./dir2 # When referencing a directory, the Taskfile.yaml in that directory is referenced by default

When including other Taskfiles, by default, commands will be executed in the directory of the main Taskfile. You can also control the execution directory of tasks within the included Taskfile using the dir
parameter:
version: '3'
includes:
dir1: ./dirtest.yaml # Directly execute in current directory
dir2:
taskfile: ./dirtest.yaml
dir: /tmp # Execute in specified directory

Defer Handling
Familiar with Go language should know that there is a very convenient keyword defer
; this directive is used to define actions to be executed at the end of the code, commonly for resource cleanup. Taskfile also supports this directive, allowing us to perform cleanup operations during task execution:
version: '3'
tasks:
default: # Default task to call when no task name is provided
cmds:
- wget -q https://github.com/containerd/nerdctl/releases/download/v0.19.0/nerdctl-full-0.19.0-linux-amd64.tar.gz
# Define cleanup action
- defer: rm -f nerdctl-full-0.19.0-linux-amd64.tar.gz
- tar -zxf nerdctl-full-0.19.0-linux-amd64.tar.gz

Of course, the defer directive can also reference other tasks for cleanup:
version: '3'
tasks:
cleanup:
cmds:
- rm -f {{.FILE}}
default: # Default task to call when no task name is provided
cmds:
- wget -q https://github.com/containerd/nerdctl/releases/download/v0.19.0/nerdctl-full-0.19.0-linux-amd64.tar.gz
# Reference other task for cleanup, while also passing dynamic variables
- defer: {task: cleanup, vars: {FILE: nerdctl-full-0.19.0-linux-amd64.tar.gz}}
- tar -zxf nerdctl-full-0.19.0-linux-amd64.tar.gz
4. Advanced Applications
Dynamic Detection
Output Detection
Sometimes, we may want to handle caching for certain tasks, such as not downloading a file again if it has already been downloaded. For this requirement, Taskfile allows us to define source files and generated files, using the hash values of these files to determine whether to execute the task:
version: '3'
tasks:
default:
cmds:
- wget -q https://github.com/containerd/nerdctl/releases/download/v0.19.0/nerdctl-full-0.19.0-linux-amd64.tar.gz
sources:
- testfile
generates:
- nerdctl-full-0.19.0-linux-amd64.tar.gz

As seen in the above image, when the task is executed for the first time, a .task
directory will be generated, containing the hash values of the files; when the task is executed again, if the hash values do not change, the actual task will not be executed. Taskfile has two default file detection methods: checksum
and timestamp
. The checksum
method performs hash detection on the files (default), which only requires defining the sources
configuration; the timestamp
method performs timestamp detection on the files, which requires defining both sources
and generates
configurations.
version: '3'
tasks:
build:
cmds:
- go build .
sources:
- ./*.go
generates:
- app{{exeExt}}
method: checksum # Specify detection method
In addition to the two built-in detection modes, we can also define our own detection commands using the status
configuration. If the command execution result is 0, the file is considered up-to-date, and the task does not need to be executed:
version: '3'
tasks:
generate-files:
cmds:
- mkdir directory
- touch directory/file1.txt
- touch directory/file2.txt
# test existence of files
status:
- test -d directory
- test -f directory/file1.txt
- test -f directory/file2.txt
Input Detection
The above output detection checks the results of files generated by tasks. In some cases, we may want to determine a condition before running a task, without executing anything. For this, we can use the preconditions
configuration directive:
version: '3'
tasks:
generate-files:
cmds:
- mkdir directory
- touch directory/file1.txt
- touch directory/file2.txt
# test existence of files
preconditions:
- test -f .env
- sh: "[ 1 = 0 ]"
msg: "One doesn't equal Zero, Halting"
Go Template Engine
In the variable section above, a portion of the template engine usage has been demonstrated. In fact, Taskfile integrates the slim-sprig[1] library, which provides some convenient methods that can be used within the template engine:
version: '3'
tasks:
print-date:
cmds:
- echo {{now | date "2006-01-02"}}
For more information on these methods and the usage of the template engine, please refer to the Go Template documentation and the slim-sprig[2] documentation.
Interactive Terminal
Some task commands may require an interactive terminal to execute. In this case, you can set the interactive
option for the task; when interactive
is set to true
, the task can open an interactive terminal during execution:
version: '3'
tasks:
cmds:
- vim my-file.txt
interactive: true
For more details on using Taskfile, please refer to its official documentation[3]. This article does not cover everything due to space constraints.
References
slim-sprig: https://go-task.github.io/slim-sprig/
[2]slim-sprig: https://go-task.github.io/slim-sprig/
[3]Official documentation: https://taskfile.dev/
Link: https://mritd.com/2022/04/25/taskfile-a-better-build-tool-than-makefile/
(All rights reserved by the original author, please delete if infringed)
