Task (also known as go-task) is a task runner and build tool written in Go. Its primary goal is to be simpler and easier to use than alternatives like GNU Make. It uses YAML files (typically Taskfile.yml
or Taskfile.yaml
) to define tasks, making it more readable and modern compared to Makefile syntax. Being a single binary written in Go, it has no runtime dependencies (other than the binary itself) and is cross-platform (Linux, macOS, Windows).
- Taskfile: A YAML file (default:
Taskfile.yml
orTaskfile.yaml
in the current directory) that defines the configuration and tasks. - Tasks: Named sets of commands or actions defined within the Taskfile under the
tasks:
key. - Commands (
cmds
): The actual shell commands or actions a task executes. Can be single commands, multi-line scripts (using|
), or calls to other tasks (task: other-task-name
). - Running Tasks: Use the
task
command followed by the task name (e.g.,task build
). If no task name is provided, it runs the task nameddefault
. - YAML Syntax: Taskfiles use standard YAML syntax. Indentation (typically 2 spaces) is significant.
version: '3' # Specifies the Taskfile schema version (required)
# Optional global configurations
includes: # Include tasks from other files/directories
# namespace: path/to/another/Taskfile.yml
# another: ./DockerTasks.yml
# remote: # Experimental feature
# taskfile: https://.../Taskfile.yml
dotenv: ['.env', '.env.local'] # Load environment variables from .env files
env: # Define global environment variables available to all tasks
GLOBAL_VAR: value
vars: # Define global variables accessible via Go templating ({{.VAR_NAME}})
OUTPUT_DIR: bin/
DYNAMIC_VAR: # Can be dynamic based on a shell command
sh: git rev-parse HEAD
output: prefixed # Global output style (interleaved, group, prefixed)
method: timestamp # Default method for up-to-date checks (timestamp, checksum)
run: always # Default run behavior (always, once, when_changed)
tasks:
# --- Task Definition ---
build: # Name of the task (use kebab-case recommended)
desc: "Builds the application binary" # Optional: Description shown in `task --list`
summary: | # Optional: Longer description shown in `task --summary build`
This task compiles the Go source code
and places the binary in the {{.OUTPUT_DIR}} directory.
dir: backend/ # Optional: Execute commands in this directory
env: # Task-specific environment variables (override global)
CGO_ENABLED: 0
vars: # Task-specific variables (override global)
TARGET_OS: linux
cmds: # List of commands to execute sequentially
# Simple command
- go build -o {{.OUTPUT_DIR}}myapp -ldflags="-s -w" ./cmd/myapp
# Multi-line command
- |
echo "Building for {{.TARGET_OS}}..."
GOOS={{.TARGET_OS}} go build -o {{.OUTPUT_DIR}}myapp-{{.TARGET_OS}} ./cmd/myapp
# Calling another task sequentially
- task: generate-assets
# Call another task with specific variables
- task: notify
vars: { MESSAGE: "Build complete!" }
# Deferred command (runs after task completes, even on error)
- cmd: echo "Cleanup..."
defer: true
deps: # List of task dependencies (run BEFORE cmds, potentially in parallel)
- clean
- setup-tools
preconditions: # Optional: Checks before running deps/cmds. Fail if any fail.
- sh: "[ -f main.go ]"
msg: "main.go not found!"
- sh: "which go"
msg: "Go compiler not found in PATH"
status: # Optional: Check if task is up-to-date (alternative to sources/generates)
# Run task only if this command exits non-zero
- test -f {{.OUTPUT_DIR}}myapp
sources: # Optional: Files/globs to check for changes
- '**/*.go'
- 'go.mod'
- 'go.sum'
generates: # Optional: Files/globs generated by the task
- '{{.OUTPUT_DIR}}myapp' # Used with sources for up-to-date checks (timestamp/checksum)
silent: false # Optional: If true, don't print task name/command before execution
interactive: false # Optional: If true, allocates a TTY for interactive commands
ignore_error: false # Optional: If true, continue execution even if a command fails
run: default # Task-specific run behavior (always, once, when_changed)
# --- Other Tasks ---
clean:
desc: "Remove build artifacts"
cmds:
- rm -rf {{.OUTPUT_DIR}}
generate-assets:
# ... commands to generate assets ...
notify:
internal: true # Optional: Hide from `task --list` unless `--list-all` is used
cmds:
- echo "{{.MESSAGE}}"
setup-tools:
# ... commands ...
default: # Special task run when `task` is called without arguments
desc: "Default task: lists available tasks"
cmds:
- task --list # A common default task pattern
# Task using wildcard for dynamic arguments
# Run like: task benchmark:my_module
benchmark:*:
vars:
MODULE_NAME: "{{index .MATCH 0}}"
cmds:
- python -m {{.MODULE_NAME}}
# --- End of Taskfile ---
- Definition (
vars
): Can be defined globally or per-task. Global vars are accessible in all tasks. Task vars override global vars. - Syntax: Use Go's template syntax
{{.VAR_NAME}}
to reference variables withincmds
,deps
,vars
, etc. - Dynamic Variables: Can be set based on the output of a shell command using
sh: "command"
. - Environment Variables (
env
):- Can be defined globally or per-task using the
env:
key. - Can be loaded from
.env
files using thedotenv:
key. - Can be accessed within commands using standard shell syntax (
$VARNAME
or%VARNAME%
on Windows). - Can be passed on the command line before the task name:
MYVAR=value task mytask
.
- Can be defined globally or per-task using the
- Task-Specific Variables: Variables passed when calling another task (
task: other-task vars: {KEY: VAL}
). - CLI Arguments (
.CLI_ARGS
): A special variable capturing arguments passed after--
on the command line (task mytask -- arg1 arg2
). - Wildcard Matches (
.MATCH
): In tasks with*
in their name,.MATCH
is an array containing the matched wildcard parts.{{index .MATCH 0}}
gets the first match. - Built-in Variables:
.TASK
(current task name),.OS
,.ARCH
,.ROOT_DIR
(Taskfile directory),.USER_WORKING_DIR
(directory wheretask
was invoked, useful with-g
).
- Tasks listed under
deps:
are executed before thecmds:
of the current task. - Dependencies are run in parallel by default for performance. They should generally not depend on each other's side effects within the same
deps
block. - If serial execution is needed, use sequential
task:
calls within thecmds:
block instead ofdeps
.
- Purpose: Avoid re-running tasks (like builds) if inputs haven't changed and outputs exist.
- Methods:
- Timestamp: Define
sources
(inputs) andgenerates
(outputs). Task runs if any source is newer than any generated file, or if generated files don't exist. Setmethod: timestamp
. - Checksum: Define
sources
. Task calculates a checksum of sources. Task runs if the checksum differs from the last successful run. Setmethod: checksum
(often the default). - Status (
status
): Define shell commands understatus:
. The task is considered up-to-date if all status commands exit with code 0. Task runs if any status command exits non-zero. This overridessources
/generates
.
- Timestamp: Define
- Forcing Execution: Use the
--force
(or-f
) flag to run a task regardless of its up-to-date status.
- Purpose: Organize tasks by splitting them into multiple files or directories.
- Syntax: Use the
includes:
key at the top level.includes: # Assigns tasks from ./docker/Taskfile.yml to the 'docker' namespace # Run tasks like: task docker:build docker: ./docker/Taskfile.yml # Can also point to a directory, Task looks for Taskfile.yml inside docs: ./documentation # Can specify a different Taskfile name utils: taskfile: ./common/Utils.yaml # Optional: Change the working directory for included tasks # dir: ./common
- Namespacing: Included tasks are typically accessed via
<namespace>:<taskname>
. - Flattening: Use
flatten: true
within an include definition to add tasks directly to the main namespace (use with caution to avoid name clashes). - Internal Includes: Advanced feature for embedding Taskfiles within Go binaries.
task <task-name>
: Run a specific task.task
: Run thedefault
task.task --list
/task -l
: List tasks with descriptions.task --list-all
/task -a
: List all tasks, including internal ones or those without descriptions.task --init
/task -i
: Create a basicTaskfile.yml
in the current directory.task --watch
/task -w <task-name>
: Run a task and re-run it whenever source files change.task --force
/task -f
: Force task execution, ignoring up-to-date checks.task --dry
: Show what commands would run without executing them.task --parallel
/task -p task1 task2
: Run specified tasks concurrently.task --dir <path>
/task -d <path>
: Specify the directory containing the Taskfile.task --taskfile <path>
/task -t <path>
: Specify a Taskfile name/path other than the default.task --global
/task -g
: Look for and run$HOME/Taskfile.yml
.task --summary <task-name>
: Show detailed summary/documentation for a task.task -- <args...>
: Pass arguments after--
to the.CLI_ARGS
variable inside the task.task <task-name> VAR=value OTHER='some value'
: Pass variables directly to a task (alternative to env vars).
- Use
version: '3'
. - Use 2 spaces for indentation.
- Separate top-level sections (
version
,includes
,vars
,tasks
, etc.) with blank lines. - Separate individual tasks within
tasks:
with blank lines. - Use uppercase for
vars
names (e.g.,{{.OUTPUT_DIR}}
). - Use kebab-case for task names (e.g.,
build-image
). - Use
:
for namespace separation (docker:build
). - Prefer external scripts for complex multi-line logic over embedding large scripts in
cmds
. - Provide
desc
for tasks intended for users.
Taskfile provides a powerful yet simple way to automate development workflows using readable YAML. Its features like dependency management, variables, includes, and up-to-date checks make it a compelling alternative to Makefiles, especially in Go projects and cross-platform environments.