Skip to main content

Using the Task Tool

·11 mins

Task is a task runner and build tool. It provides a consistent framework for sets of tasks, enabling you to run the same workflows on multiple platforms and environments.

Task is also known as go-task.

How Task Works #

Each copy of Task is a single executable file, with versions for Linux, macOS and Windows. This executable is relatively small, being about 8Mb for the 64-bit Linux version. It uses sets of tasks that are defined in YAML files, and includes a shell interpreter, so that you can use the same syntax for tasks on any platform.

This means that you can use Task in any environment. It only requires a copy of the Task executable, and has no configuration files apart from the YAML files that contain the tasks.

It also provides features for you to customise the behavior of your tasks for the different environments that you might use. The built-in template functions enable you to get consistent inputs for your tasks across different platforms. When needed, you can define operating system specific files, so that Task uses the specific implementation for the current platform.

Task includes two other key features: conditional execution of tasks and running tasks on file changes. These features are designed to be usable with any type of software development.

Here is an example of a Taskfile.yaml, with a build task that only runs when the sources change:

# Tasks for a Hugo static website project
#
# Hugo: https://gohugo.io

version: "3"

silent: true

tasks:
  default:
    cmds:
      - task: build

  build:
    desc: Build Website
    cmds:
      - hugo
    sources:
      - content/**/*.md
    generates:
      - public/**

  clean:
    desc: Delete generated files
    cmds:
      - for: [".hugo_build.lock", "public"]
        cmd: rm -fr {{.ITEM}}

  deploy:
    desc: Deploy Website
    deps: [build]
    cmds:
      - hugo deploy

  serve:
    desc: Run Website in development server
    cmds:
      - hugo server

Installing Task #

Consider using a tool version manager like mise or asdf to install Task. For example, this command installs the latest version of Task with mise:

mise use -gy task

If you use Visual Studio Code and have a Dev Container configuration for a project, use the go-task feature, as shown in the section below.

To add Task to container images or systems without a tool version manager, see the section below for how to install Task with a script.

All of these methods enable you to specify a version of Task for each project that you work on, and ensure that the expected version of Task is present. If possible, avoid using operating system packages. A package installs a single shared copy of Task, and is likely to provide an older version.

Installing Task with a Script #

The Task project provide a script for downloading task from GitHub. You may either fetch this installation script each time, as the documentation describes, or save it.

If you build Docker container images that contain a copy of Task, use a saved copy of the script. This ensures that container image builds are consistent.

To save the installation script:

curl -L https://taskfile.dev/install.sh > install-task.sh

To use the installation script, call it with the Git tag and the -b option. The Git tag specifies the version of Task. The -b option specifies which directory to install it to:

./install-task.sh -b $HOME/.local/bin v3.38.0
Exclude the path for the Task executable file from version control. If you use the script to download a copy of Task into a development project, make sure that the .gitignore (or equivalent) excludes it from version control.

Installing Task with Operating System Packages #

If you do need to install Task with an operating system package manager, it is available for several popular systems. For example, these commands install Task:

winget install Task.Task  # winget on Microsoft Windows
brew install go-task      # Homebrew on macOS
sudo dnf install go-task  # dnf on Fedora Linux

Enabling Visual Studio Code Integration #

To use Task with Visual Studio Code, install the redhat.vscode-yaml and task.vscode-task extensions.

The vscode-yaml extension enables YAML formatting and validation, and vscode-task adds a graphical integration for running tasks.

You must install Task to use the vscode-task extension.

Enabling Integration with JetBrains IDEs #

To enable support for Task in JetBrains IDEs such as PyCharm, install the Taskfile plugin.

Enabling Autocompletion #

To enable autocompletion for Task in a shell, download the appropriate script and install it into the correct location. For example, this command enables autocompletion in the fish shell:

curl -L https://raw.githubusercontent.com/go-task/task/main/completion/fish/task.fish > $HOME/.config/fish/completions/task.fish

The Task project currently provides autocompletions for Bash, zsh, fish and PowerShell.

Adding Task to a Dev Container #

If you are using a Dev Container with Visual Studio Code, you can add the feature go-task to your devcontainer.json file to download Task from GitHub:

    "features": {
        "ghcr.io/devcontainers-contrib/features/go-task:1": {
            "version": "3.38.0"
        }
    }

Ensure that you also include the redhat.vscode-yaml and task.vscode-task extensions in the devcontainer.json file:

    "customizations": {
        "vscode": {
            "extensions": [
                "redhat.vscode-yaml",
                "task.vscode-task"
            ]
        }
    }

The vscode-yaml extension enables YAML formatting and validation, and vscode-task adds a graphical integration for running tasks.

Creating a User Taskfile for Global Tasks #

To define tasks that are available at any time, create a file with the name Taskfile.yaml in your home directory.

Create a task in the Taskfile.yaml with the name default. When Task is invoked without the name of a task, it runs the default task in the Taskfile.yaml.

This example user Taskfile.yaml includes a default task that lists the available tasks:

version: "3"

silent: true

tasks:
  default:
    cmds:
      - task: list

  list:
    desc: List available tasks
    cmds:
      - task --list

  system-info:
    desc: Display system information
    cmds:
      - "echo CPU architecture: {{ARCH}}"
      - "echo Operating system: {{OS}}"

Use the option -g to run the user Taskfile.yaml, rather than the nearest Taskfile:

task -g system-info

Using Task in a Project #

First, add the .task directory to the exclusions for source control. This directory is used to hold files for tracking changes.

Always use the name Taskfile.yaml or Taskfile.yml for Task files. This enables tools that support JSON Schemas to identify the format of the files, so that they can provide autocompletion and validation.

If a project only requires one small set of tasks, then use a single Taskfile. If you need to manage several sets of tasks, use these features:

  1. Taskfiles in subdirectories
  2. Includes

Adding Taskfile.yaml files in subdirectories enables you to override the set of tasks for a project when you change your working directory in the project. This lets you define sets of tasks that are appropriate to the context.

The includes feature of Task enables you to define groups of tasks that can be added to any Taskfile. These groups automatically become namespaces, which ensures that tasks with the same name do not override each other. For example, if you create an include for python and an include for web, they may both have a task called test, which you can call as python:test and web:test.

Using Includes #

If you decide to use Task includes in your project, consider following these guidelines:

  • Create the first task in the root Taskfile.yaml with the name default. When Task is invoked without a namespace or task name, it runs the default task in the Taskfile.yaml.
  • Create subdirectory called tasks/. For each namespace, create a directory with the same name as the namespace, with a Taskfile.yaml file in the directory. Write the tasks for the namespace in the relevant Taskfile.yaml file. Use includes in the root Taskfile.yaml file to enable these namespaces.
  • Use the root Taskfile.yaml to define standard tasks for the project. Each of these should call the relevant tasks in one or more namespaces. Avoid writing tasks in the root Taskfile.yaml that do anything other than running tasks that are defined in namespaces.
  • Remember to include a default task for each namespace. This means that the default task runs when a user types the name of the namespace without specifying the name of the task.
  • Specify any relevant aliases for a namespace with the includes attribute.

This diagram shows the suggested directory structure for a project with task includes:

.
|
| - tasks/
|    |
|    |- pre-commit
|    |    |
|    |    |- Taskfile.yaml
|    |
|    |- package
|         |
|         |- Taskfile_darwin.yaml
|         |- Taskfile_linux.yaml
|         |- Taskfile_windows.yaml
|
|- Taskfile.yaml

Example Taskfile.yaml for a Project #

# Tasks for the Task runner:
#
# https://taskfile.dev

version: "3"

silent: true

# Namespaces
includes:
  package: tasks/package/Taskfile_{{OS}}.yaml
  pre-commit: tasks/pre-commit

# Top-level tasks
tasks:
  default:
    cmds:
      - task: list

  bootstrap:
    desc: Set up environment for development
    cmds:
      - task: pre-commit:setup

  build:
    desc: Build packages
    cmds:
      - task: package:build

  clean:
    desc: Delete generated files
    cmds:
      - task: package:clean

  fmt:
    desc: Format code
    aliases: [format]
    cmds:
      - task: pre-commit:run
        vars: { HOOK_ID: "ruff-format" }

  lint:
    desc: Run all checks
    aliases: [check]
    cmds:
      - task: pre-commit:check

  list:
    desc: List available tasks
    cmds:
      - task --list

The default task runs the list task, so this command displays a list of all of the available tasks, including the tasks in the namespaces:

task

Example Namespace of Tasks for a Project #

# Tasks for pre-commit
#
# https://pre-commit.com/

version: "3"

silent: true

tasks:
  default:
    cmds:
      - task: check

  check:
    desc: Check the project with pre-commit
    cmds:
      - pre-commit run --all-files

  run:
    desc: Run a specific pre-commit check on the project
    cmds:
      - pre-commit run "{{.HOOK_ID}}" --all-files
    requires:
      vars: [HOOK_ID]

  setup:
    desc: Setup pre-commit for use
    cmds:
      - pre-commit install

The default task in this file runs check, so this command runs the check task:

task pre-commit

The result is the same as this command:

task pre-commit:check

Writing Taskfiles #

Follow the style guidelines when writing tasks. Here are some extra suggestions:

  • Use a YAML formatter to format your Task files. The redhat.vscode-yaml extension adds support for formatting YAML files to Visual Studio Code. The Prettier tool formats YAML files, and can be used in any environment.
  • Always put a desc attribute for each task. The description appears next to the task in the output of task –list.
  • Consider adding a summary attribute for each task. The summary appears in the output of task –summary TASK-NAME.
  • Use argument forwarding or wildcard task names to get inputs for a task from the command-line.
  • Specify the requires attribute for each task that uses a variable. This ensures that the task has the necessary variables.
  • Use dotenv files to get configuration from files.
  • Use Bash shell syntax for tasks. Task uses mvdan/sh to provide the equivalent of the bash shell.
  • To ensure that your tasks are portable, check the options for UNIX commands that you call in tasks, such as rm. Different operating systems and Linux distributions provide different implementations of these commands, which means that the options may not be consistent across environments.
  • When it is possible, use the template functions instead of shell commands, because these will behave consistently across different environments.
  • Provide operating system specific Taskfiles when necessary.
Dependencies run in parallel. This means that dependencies of a task should not depend on each other. If you want to ensure that tasks run in sequence, see the documentation on Calling Another Task.

Running Tasks #

To run a task in a Taskfile.yaml, enter task followed by the name of the task:

task example-task

If a task accepts forwarding of arguments, add the arguments to the command:

task example-task -- my-argument-value

You may set variables by specifying environment values with the Task command:

MY_VARIABLE_NAME=my-variable-value task example-task

Checking Taskfiles #

The Task project publish the schema for Task files as a JSON Schema. This means that any software that supports JSON Schemas for YAML documents can check that your Task files are valid. To ensure that your Task files are consistently formatted, use standard tools for YAML files.

Visual Studio Code will both validate and format Task files when the redhat.vscode-yaml extension is installed. This extension use a language server for the YAML format, with support for JSON Schemas.

To validate Task files on the command-line, use check-jsonschema. The check-jsonschema tool automatically includes the schema for Task files. The yamllint command-line tool provides format and quality checks for all types of YAML file.

The check-jsonschema and yamllint projects also provide hooks for pre-commit, so that files are automatically checked before changes are committed to source control.

Validating Task files with pre-commit #

To validate Task files before you commit them to source control, add the hook for check-jsonschema and the hook for yamllint to the pre-commit configuration for your project.

Add these lines to the .pre-commit-config.yaml file in the root directory of your project:

- repo: https://github.com/python-jsonschema/check-jsonschema
  rev: "0.28.4"
  hooks:
    - id: check-taskfile
- repo: https://github.com/adrienverge/yamllint.git
  rev: "v1.35.1"
  hooks:
    - id: yamllint
      args: [--strict]

To ensure that yamllint handles Task files, add a .yamllint.yaml file with this content:

---
# Begin with yamllint default settings
extends: default

rules:
  # Rules for curly braces: {}
  braces:
    forbid: false
    min-spaces-inside: 1
    max-spaces-inside: 1
    min-spaces-inside-empty: 0
    max-spaces-inside-empty: 0

  # Rules for round brackets: ()
  brackets:
    forbid: false
    min-spaces-inside: 0
    max-spaces-inside: 0
    min-spaces-inside-empty: 0
    max-spaces-inside-empty: 0

  # Do not require three dashes at the start of a YAML document
  document-start: disable

  # Rules for line length
  line-length:
    max: 88
    level: error

The pre-commit checks automatically run when you commit code. You may also run the checks yourself at any time, with the pre-commit command-line tool. For example, this command validates all of the Task files in your project:

pre-commit run check-taskfile --all-files

Testing a Task #

To test a task, run it with the –dry option:

task --dry TASK-NAME

This compiles and prints tasks in the order that they would be run.

To debug a task, ensure that silent is not enabled in the appropriate Taskfile.yaml, so that the outputs of the commands are visible:

silent: false

Resources #