What it is
Task is a simple, dependency-based command runner that helps you automate common development tasks like building, testing, and deploying.
Installation
Linux
# Using apt (Debian/Ubuntu)
sudo apt update && sudo apt install task-yaml
# Using dnf (Fedora)
sudo dnf install task-yaml
# Using pacman (Arch Linux)
sudo pacman -S task-yaml
# Using go (if you have Go installed)
go install github.com/go-task/task/v3/cmd/task@latest
macOS
# Using Homebrew
brew install go-task/tap/go-task
Windows
# Using Chocolatey
choco install go-task
# Using Scoop
scoop install go-task
# Using winget
winget install go-task
Core Concepts
- Taskfile.yml: The central configuration file where you define your tasks. It’s a YAML file that resides in your project’s root directory.
- Task: A named command or a sequence of commands defined within the
Taskfile.yml. Tasks can depend on other tasks. - Dependencies: Tasks can depend on other tasks. When a task is run, its dependencies are executed first. This allows for building complex workflows.
- Recipes: The actual commands to be executed within a task.
- Variables: You can define and use variables within your
Taskfile.ymlfor reusability and flexibility. - Environments: You can set environment variables for specific tasks or globally.
- Generators: Task can generate files based on templates.
Commands / Usage
Defining Tasks in Taskfile.yml
A basic Taskfile.yml looks like this:
version: '3'
tasks:
hello:
desc: Prints a greeting
cmds:
- echo "Hello, Task!"
build:
desc: Builds the project
deps:
- clean
cmds:
- go build -o myapp ./cmd/myapp
vars:
GOOS: linux
GOARCH: amd64
clean:
desc: Cleans the build directory
cmds:
- rm -rf bin/
test:
desc: Runs tests
deps:
- build
cmds:
- go test ./...
deploy:
desc: Deploys the application
cmds:
- echo "Deploying..."
- ./myapp --deploy
vars:
APP_ENV: production
Running Tasks
-
Run a specific task:
task hello # Output: Hello, Task! -
Run a task with dependencies:
task test # Output: # rm -rf bin/ # go build -o myapp ./cmd/myapp # go test ./...(The
cleanandbuildtasks will run beforetestbecause they are listed as dependencies.) -
Run multiple tasks:
task build test # Output: # rm -rf bin/ # go build -o myapp ./cmd/myapp # go test ./... -
Run tasks with environment variables:
task -e APP_ENV=staging deploy # Output: # Deploying... # ./myapp --deploy(This will run the
deploytask withAPP_ENVset tostagingfor that specific run.) -
List available tasks:
task --list # Or task -l(This will show a list of tasks defined in your
Taskfile.ymlwith their descriptions.) -
Show task details (description, dependencies, commands):
task --summary build # Output: # Task: build # Description: Builds the project # Dependencies: # - clean # Commands: # - go build -o myapp ./cmd/myapp # Variables: # GOOS: linux # GOARCH: amd64
Taskfile Configuration Options
version: Specifies the Taskfile schema version (e.g.,'3').env: Global environment variables for all tasks.version: '3' env: BUILD_DIR: bin tasks: build: cmds:
{% raw %} - echo "Building in {{.BUILD_DIR}}" {% endraw %} ```
vars: Global variables for all tasks.version: '3' vars: APP_NAME: my-awesome-app tasks: run: cmds:
{% raw %} - ./{{.APP_NAME}} {% endraw %} ```
ignore_error: Iftrue, Task will not exit if a command fails.version: '3' tasks: cleanup: ignore_error: true cmds: - rm -f temp.log - rm -f non_existent_file.txt # This command will fail, but task will continuesilent: Iftrue, Task will not print commands before executing them.version: '3' tasks: quiet_build: silent: true cmds: - echo "Building quietly..."skip_if: Skip task execution if the condition is true.version: '3' tasks: deploy_prod: skip_if: 'eq(env.BRANCH, "main")' # Skip if BRANCH env var is "main" cmds: - echo "Deploying to production..."preconditions: Tasks that must pass before the current task can run. Similar todepsbut can include conditions.version: '3' tasks: deploy: preconditions: - "command(git diff --quiet HEAD)" # Ensure working directory is clean cmds: - echo "Deploying..."sources: A list of files or directories that, if changed, will cause the task to be re-run (useful for watchers).version: '3' tasks: serve: sources: - src/**/*.go - static/** cmds: - go run cmd/server/main.gogenerates: A list of files that this task is expected to create. If these files exist, the task might be skipped (depending on other conditions).version: '3' tasks: generate_config: generates: - config.yaml cmds: - ./config_generator > config.yamlcmds: The actual commands to execute.- Shell command execution: By default, commands are executed in the shell.
sh: ...: Explicitly run commands usingsh.tasks: run_script: cmds: - sh: | echo "Starting..." ./my_script.sh echo "Done."script: ...: A more idiomatic way to define multi-line shell scripts.tasks: run_script: script: | echo "Starting..." ./my_script.sh echo "Done."
Variables and Templating
Task uses Go’s text/template package for templating.
{% raw %}
- Accessing variables: Use
{{.VAR_NAME}}. {% endraw %}version: '3' vars: GREETING: Hello TARGET: World tasks: greet: cmds:
{% raw %}
- echo "{{.GREETING}}, {{.TARGET}}!"
{% endraw %}
bash
task greet
# Output: Hello, World!
```
{% raw %}
- Environment variables: Access using
{{.ENV.VAR_NAME}}. {% endraw %}version: '3' tasks: show_path: cmds:
{% raw %} - echo "Your PATH is: {{.ENV.PATH}}" {% endraw %} ```
{% raw %}
- Task variables: Access using
{{.TASK_VAR_NAME}}. {% endraw %}version: '3' tasks: build: vars: GOOS: linux GOARCH: amd64 cmds:
{% raw %} - echo "Building for {{.GOOS}}-{{.GOARCH}}" {% endraw %} ```
-
Built-in variables: {% raw %}
{{.ROOT}}: The root directory of the Taskfile. {% endraw %} {% raw %}{{.CURRENT}}: The directory where the task is being run from. {% endraw %} {% raw %}{{.TASK}}: The name of the current task. {% endraw %} {% raw %}{{.PREV_TASK}}: The name of the previously executed task. {% endraw %}
-
Functions: Available functions include
eq,ne,lt,le,gt,ge,contains,default,env,getopt,glob,include,join,k8s,lower,print,printf,quote,repeat,replace,split,toToml,toYaml,upper.version: '3' tasks: conditional_run: cmds:
{% raw %} - '{{if eq (env "CI") "true"}}echo "Running in CI"{{end}}' {% endraw %} {% raw %} - '{{if not (eq (default 0 (getopt "port" "8080")) 8080)}}echo "Using custom port: {{getopt "port" "8080"}}"{{end}}' {% endraw %} ```
Task Arguments (getopt)
Tasks can accept arguments.
version: '3'
tasks:
greet:
cmds:
{% raw %}
- 'echo "Hello, {{.GETOPT.name | default "World"}}!"'
{% endraw %}
task greet --name Alice
# Output: Hello, Alice!
task greet
# Output: Hello, World!
You can also define argument constraints and descriptions.
version: '3'
tasks:
greet:
deps:
- validate_name
cmds:
{% raw %}
- 'echo "Hello, {{.GETOPT.name}}!"'
{% endraw %}
validate_name:
internal: true # Hide from `task --list`
cmds:
{% raw %}
- '{{if not .GETOPT.name}}echo "Error: --name argument is required." && exit 1{{end}}'
{% endraw %}
Including Other Taskfiles
You can split your tasks into multiple files.
Taskfile.yml:
version: '3'
includes:
build: ./build/Taskfile.yml
test: ./test/Taskfile.yml
build/Taskfile.yml:
version: '3'
tasks:
build_app:
cmds:
- echo "Building the app..."
test/Taskfile.yml:
version: '3'
tasks:
run_tests:
cmds:
- echo "Running tests..."
Then you can run them like:
task build:build_app
task test:run_tests
Generators
Task can generate files.
version: '3'
tasks:
generate_readme:
desc: Generates a README.md from a template
generates:
- README.md
vars:
APP_VERSION: 1.0.0
cmds:
- |
{% raw %}
echo "# My App v{{.APP_VERSION}}" > README.md
{% endraw %}
echo "" >> README.md
echo "This is a great application." >> README.md
Watchers
The sources and interval options can be used to create watchers.
version: '3'
tasks:
dev:
desc: Runs the development server and watches for changes
sources:
- src/**/*.go
- static/**
interval: 500ms # Check for changes every 500 milliseconds
cmds:
- go run cmd/server/main.go
Run task dev. Task will start the command and re-run it if any file in src/ or static/ changes.
Common Patterns
-
Build, Test, Run Workflow:
version: '3' tasks: build: cmds: - go build -o myapp ./cmd/myapp test: deps: - build cmds: - go test ./... run: deps: - build cmds: - ./myapp all: deps: - build - testtask all # Runs build and test task run # Runs build and then the app -
Cleaning Build Artifacts:
version: '3' tasks: clean: cmds: - rm -rf bin/ dist/ *.otask clean -
Docker Builds and Runs:
version: '3' tasks: docker-build: cmds: - docker build -t myapp:latest . docker-run: deps: - docker-build cmds: - docker run -p 8080:80 myapp:latest docker-compose-up: cmds: - docker-compose up -d docker-compose-down: cmds: - docker-compose downtask docker-build task docker-run task docker-compose-up -
Linting and Formatting:
version: '3' tasks: fmt: cmds: - go fmt ./... lint: cmds: - golangci-lint run format-and-lint: deps: - fmt - linttask format-and-lint -
Conditional Execution based on Environment:
version: '3' tasks: deploy: cmds:
{% raw %} - '{{if eq (env "CI") "true"}}echo "Deploying from CI…"{{else}}echo "Deploying locally…"{{end}}' {% endraw %} ```
- Passing Arguments to Tasks:
version: '3' tasks: greet: cmds:
{% raw %}
- 'echo "Hello, {{.GETOPT.name | default "there"}}!"'
{% endraw %}
bash
task greet --name "Developer"
task greet
```
Gotchas
-
Working Directory: By default, Task runs commands from the directory containing the
Taskfile.yml. If you need to change this for a specific task, you can use thediroption:version: '3' tasks: run_in_subdir: dir: ./scripts cmds: - ./my_script.sh -
Shell Interpretation: Commands are executed in a shell. Be mindful of shell quoting and escaping rules. For complex scripts, using the
script:block is often cleaner. -
Default Task: If you have a task named
defaultin yourTaskfile.yml, it will be executed when you runtaskwithout any arguments.version: '3' tasks: default: cmds: - echo "This is the default task."task # Output: This is the default task. -
Variable Precedence: Variables defined closer to the task (e.g., in the task’s
varsblock) override globally defined variables. Environment variables passed via-eoverride task variables. -
Error Handling: By default, Task stops execution if any command returns a non-zero exit code. Use
ignore_error: trueon a task to prevent this. -
internal: true: Tasks marked asinternal: trueare not shown intask --listand cannot be directly invoked by name. They are typically used as helper tasks or preconditions.