--- url: /docs.md description: >- Task is a task runner and build tool that aims to be simpler and easier to use than GNU Make. Start here to install it, learn it, or look something up. --- # Documentation Install Task, learn the basics, or look up syntax for your `Taskfile.yml`. ## Start with Task {#new-to-task} Install Task and run your first Taskfile. * [Installation](/docs/installation.md) * [Quick Start](/docs/getting-started.md) ## Use Task {#using-task} Find a guide for your everyday workflows. * [Browse the guide](/docs/guide.md) * [Variables](/docs/guide/variables.md) * [Dependencies and task calls](/docs/guide/dependencies.md) ## Reference {#looking-something-up} Look up syntax, flags and functions. * [Taskfile Schema](/docs/reference/schema.md) * [CLI](/docs/reference/cli.md) * [Templating](/docs/reference/templating.md) For settings outside the Taskfile, see [Configuration](/docs/reference/config.md) and [Environment](/docs/reference/environment.md). ## Keeping up * [Changelog](/docs/changelog.md): what shipped, and when. * [Experiments](/docs/experiments.md) and [Deprecations](/docs/deprecations.md): what is coming, and what is going away. * [FAQ](/docs/faq.md): the questions that come up most often. * [Community](/docs/community.md): integrations and tools built by other people. ## Using an AI coding assistant [Task documentation for coding agents](/agents.md) is a compact map of these pages plus the semantics that are easiest to get wrong. Every page is also available as raw Markdown by appending `.md` to its URL, and the whole corpus is at [/llms.txt](/llms.txt). --- --- url: /docs/installation.md description: Installation methods for Task --- # Installation Choose a package manager for your system, or install Task from a binary. ## Official packages {#official-package-managers} Maintained by the Task team and kept up-to-date. ### [Homebrew](https://brew.sh) {#homebrew} [Tap source](https://github.com/go-task/homebrew-tap/blob/main/Casks/go-task.rb) [Homebrew package](https://formulae.brew.sh/formula/go-task) [Formula source](https://github.com/Homebrew/homebrew-core/blob/master/Formula/g/go-task.rb) ```shell brew install go-task/tap/go-task ``` Or use the Homebrew core formula: ```shell brew install go-task ``` ### [dnf](https://docs.fedoraproject.org/en-US/quick-docs/dnf) {#dnf} [View package](https://cloudsmith.io/~task/repos/task/packages/?sort=-format\&q=format%3Arpm) Set up the repository: ```shell curl -1sLf 'https://dl.cloudsmith.io/public/task/task/setup.rpm.sh' | sudo -E bash ``` Install Task: ```shell dnf install task ``` ### [apt](https://doc.ubuntu-fr.org/apt) {#apt} [View package](https://cloudsmith.io/~task/repos/task/packages/?sort=-format\&q=format%3Adeb) Set up the repository: ```shell curl -1sLf 'https://dl.cloudsmith.io/public/task/task/setup.deb.sh' | sudo -E bash ``` Install Task: ```shell apt install task ``` ### [apk](https://wiki.alpinelinux.org/wiki/Alpine_Package_Keeper) {#apk} [View package](https://cloudsmith.io/~task/repos/task/packages/?sort=-format\&q=format%3Aalpine) Set up the repository: ```shell curl -1sLf 'https://dl.cloudsmith.io/public/task/task/setup.alpine.sh' | sudo -E bash ``` Install Task: ```shell apk add task ``` ### [Snap](https://snapcraft.io/task) {#snap} [Source](https://github.com/go-task/snap/blob/main/snap/snapcraft.yaml) ```shell sudo snap install task --classic ``` Requires a Linux distribution with classic confinement support. ### [npm](https://www.npmjs.com) {#npm} [View package](https://www.npmjs.com/package/@go-task/cli) [Source](https://github.com/go-task/task/blob/main/package.json) ```shell npm install -g @go-task/cli ``` Task is also available as a project dependency. ### [WinGet](https://github.com/microsoft/winget-cli) {#winget} [Source](https://github.com/microsoft/winget-pkgs/tree/master/manifests/t/Task/Task) ```shell winget install Task.Task ``` Available through the [WinGet community repository](https://github.com/microsoft/winget-pkgs). ## Community packages {#community-maintained-package-managers} Maintained by the community, outside the Task team's control. These packages may lag behind the latest release. ### [Mise](https://mise.jdx.dev/) {#mise} [View package](https://mise-tools.jdx.dev/tools/task) ```shell mise use -g task ``` For a project-local installation, use `mise use task` instead. This adds Task to your project's `mise.toml`. ### [Macports](https://macports.org) {#macports} [View package](https://ports.macports.org/port/go-task/details/) [Source](https://github.com/macports/macports-ports/blob/master/devel/go-task/Portfile) ```shell port install go-task ``` ### [pip](https://pip.pypa.io) {#pip} [View package](https://pypi.org/project/go-task-bin) [Source](https://github.com/Bing-su/pip-binary-factory/tree/main/task) ```shell pip install go-task-bin ``` ### [Chocolatey](https://chocolatey.org) {#chocolatey} [View package](https://community.chocolatey.org/packages/go-task) [Source](https://github.com/Starz0r/ChocolateyPackagingScripts/blob/master/src/go-task_gh_build.py) ```shell choco install go-task ``` ### [Scoop](https://scoop.sh) {#scoop} [Source](https://github.com/ScoopInstaller/Main/blob/master/bucket/task.json) ```shell scoop install task ``` ### Arch ([pacman](https://wiki.archlinux.org/title/Pacman)) {#arch} [View package](https://archlinux.org/packages/extra/x86_64/go-task/) [Source](https://gitlab.archlinux.org/archlinux/packaging/packages/go-task) ```shell pacman -S go-task ``` ### Fedora ([dnf](https://docs.fedoraproject.org/en-US/quick-docs/dnf)) {#fedora-community} [View package](https://packages.fedoraproject.org/pkgs/golang-github-task/go-task/) [Source](https://src.fedoraproject.org/rpms/golang-github-task) ```shell dnf install go-task ``` ### FreeBSD ([Ports](https://ports.freebsd.org/cgi/ports.cgi)) {#freebsd} [View package](https://cgit.freebsd.org/ports/tree/devel/task) [Source](https://cgit.freebsd.org/ports/tree/devel/task/Makefile) ```shell pkg install task ``` ### [Nix](https://nixos.org) {#nix} [Source](https://github.com/NixOS/nixpkgs/blob/master/pkgs/by-name/go/go-task/package.nix) ```shell nix-env -iA nixpkgs.go-task ``` ### [pacstall](https://github.com/pacstall/pacstall) {#pacstall} [View package](https://pacstall.dev/packages/go-task-deb) [Source](https://github.com/pacstall/pacstall-programs/blob/master/packages/go-task-deb/go-task-deb.pacscript) ```shell pacstall -I go-task-deb ``` ### [pkgx](https://pkgx.sh) {#pkgx} [View package](https://pkgx.dev/pkgs/taskfile.dev) [Source](https://github.com/pkgxdev/pantry/blob/main/projects/taskfile.dev/package.yml) ```shell pkgx task ``` or, if you have pkgx integration enabled: ```shell task ``` ## Binaries & CI {#get-the-binary} Install Task without a package manager: download a binary, use the install script, or add the setup action to your GitHub workflow. ### Download a binary {#binary} 1. Open the [GitHub releases](https://github.com/go-task/task/releases) and download the archive for your operating system and architecture. 2. Extract `task` (`task.exe` on Windows). 3. Move the executable to a directory on your `PATH`. Each release also includes DEB, RPM and APK packages, plus `task_checksums.txt` with SHA-256 checksums for the release files. ### Install with a script {#install-script} Use the [install script](https://github.com/go-task/task/blob/main/install-task.sh) for a shell-based installation, including CI environments. It downloads a prebuilt binary and verifies its checksum; no Go installation is needed. By default, the script installs the latest release into `./bin`, relative to your current directory. Choose a different directory or pin a release: ::: code-group ```shell [Latest release] sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d ``` ```shell [Custom directory] sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b ~/.local/bin ``` ```shell [Pinned version] sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d v3.42.1 ``` ```shell [Directory + version] sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b ~/.local/bin v3.42.1 ``` ::: When combining options, keep the [release tag](https://github.com/go-task/task/releases) last. ::: tip Make Task available in your shell Add the installation directory to your `PATH` to run `task` from anywhere. With the default location, you can also run `./bin/task` directly. On Linux, `~/.local/bin` and `~/bin` are common per-user locations; `/usr/local/bin` is a system-wide location and may require elevated permissions. Do not assume these directories are already on your `PATH`, especially on macOS and Windows. ::: ### GitHub Actions Add the [official setup action](https://github.com/go-task/setup-task) to your job's `steps` before running Task: ```yaml - name: Install Task uses: go-task/setup-task@v2 - name: Verify Task run: task --version ``` Use the action's `version` input to pin a Task release. See the [action documentation](https://github.com/go-task/setup-task#usage) for examples and configuration. ## Build from source Ensure that you have a supported version of [Go](https://golang.org) properly installed and setup. You can find the minimum required version of Go in the [go.mod](https://github.com/go-task/task/blob/main/go.mod#L3) file. You can then install the latest release globally by running: ```shell go install github.com/go-task/task/v3/cmd/task@latest ``` Or you can install into another directory: ```shell env GOBIN=/bin go install github.com/go-task/task/v3/cmd/task@latest ``` ::: tip For CI environments we recommend using the [install script](/docs/installation.md#install-script) instead, which is faster and more stable, since it'll just download the latest released binary. ::: ## Go Tool If you're working in a Go project, a nice possibility is using `go tool`. `go tool` makes it easy to run Task without needing to install the binary manually. This works well on CI. To do that, just run the following to add Task as a tool in your Go project. Task will be added to your `go.mod`. ```bash go get -tool github.com/go-task/task/v3/cmd/task@latest ``` Then, prefix `go tool` when calling Task like below. Go will compile Task on demand before calling it. ```bash go tool task {arguments...} ``` ## Verify installation After installing, open a terminal and check that Task is available: ```shell task --version ``` If you installed Task with `go tool`, run `go tool task --version` instead. You're ready to [create your first Taskfile](/docs/getting-started.md). You can also enable shell completions below. ## Shell completions {#setup-completions} Some installation methods will automatically install completions too, but if this isn't working for you or your chosen method doesn't include them, you can run `task --completion ` to output a completion script for any supported shell. Every shell shares a single source of truth: the script is a thin wrapper that asks the `task` binary itself what to suggest, so Bash, Zsh, Fish, Nushell and PowerShell all offer the same task names, aliases, flags, flag values and `requires` vars. There are a couple of ways these completions can be added to your shell config: ### Option 1. Load the completions in your shell's startup config (Recommended) This method loads the completion script from the currently installed version of task every time you create a new shell. This ensures that your completions are always up-to-date. If your executable isn’t named task, set the `TASK_EXE` environment variable before running eval. ::: code-group ```shell [bash] # ~/.bashrc # export TASK_EXE='go-task' if needed eval "$(task --completion bash)" ``` ```shell [zsh] # ~/.zshrc # export TASK_EXE='go-task' if needed eval "$(task --completion zsh)" ``` ```shell [fish] # ~/.config/fish/config.fish # export TASK_EXE='go-task' if needed task --completion fish | source ``` ```powershell [powershell] # $PROFILE\Microsoft.PowerShell_profile.ps1 Invoke-Expression (&task --completion powershell | Out-String) ``` ```nu [nushell] # ~/.config/nushell/config.nu # # Nushell cannot source a script from stdin, so the script is saved where # Nushell auto-loads it at startup. Autoload directories are read after # config.nu, so the completions become available in the next shell. mkdir ($nu.data-dir | path join "vendor/autoload") task --completion nu | save --force ($nu.data-dir | path join "vendor/autoload/task-completions.nu") ``` ::: ### Option 2. Copy the script to your shell's completions directory This method requires you to manually update the completions whenever Task is updated. However, it is useful if you want to modify the completions yourself. ::: code-group ```shell [bash] task --completion bash > /etc/bash_completion.d/task ``` ```shell [zsh] task --completion zsh > /usr/local/share/zsh/site-functions/_task ``` ```shell [fish] task --completion fish > ~/.config/fish/completions/task.fish ``` ```nu [nushell] task --completion nu | save --force ($nu.data-dir | path join "vendor/autoload/task-completions.nu") ``` ::: ### Zsh customization The Zsh completion supports the standard `verbose` zstyle to control whether task descriptions are shown. By default, descriptions are displayed. To show only task names without descriptions, add this to your `~/.zshrc` (after the completion is loaded): ```shell zstyle ':completion:*:*:task:*' verbose false ``` By default, task aliases are also offered as completions. To complete only the canonical task names, add the `show-aliases` zstyle: ```shell zstyle ':completion:*:*:task:*' show-aliases false ``` ### Nushell caveats Nushell cannot source a script from stdin, so both options above write the script to an autoload directory. Option 1 rewrites it at every startup, which keeps it in sync with the installed version of Task, and the refreshed completions are picked up by the next shell. With option 2, re-run the command after upgrading Task. Nushell shares a single external completer between every command, so the script chains to the one already configured — carapace and friends keep working. Load it from an autoload directory as shown above rather than from `config.nu`, so that your own completer is the one being chained to. If you would rather wire it yourself, the script also exposes a `task-external-completer` command: ```nu $env.config.completions.external.completer = {|spans| match ($spans | first) { task => (task-external-completer $spans) _ => (do $my_other_completer $spans) } } ``` Two engine directives behave differently under Nushell by design: it never appends a space after an external completion (so `NoSpace` is a no-op) and never re-sorts the results (so `KeepOrder` is always honoured). --- --- url: /docs/getting-started.md description: Guide for getting started with Task --- # Quick Start {#getting-started} The following guide will help introduce you to the basics of Task. We'll cover how to create a Taskfile, how to write a basic task and how to call it. If you haven't installed Task yet, head over to our [installation guide](/docs/installation.md). ## Creating your first Taskfile Once Task is installed, you can create your first Taskfile by running: ```shell task --init ``` This will create a file called `Taskfile.yml` in the current directory. If you want to create the file in another directory, you can pass an absolute or relative path to the directory into the command: ```shell task --init ./subdirectory ``` Or if you want the Taskfile to have a specific name, you can pass in the name of the file: ```shell task --init Custom.yml ``` This will create a Taskfile that looks something like this: ```yaml [Taskfile.yml] version: '3' vars: GREETING: Hello, World! tasks: default: desc: Print a greeting message cmds: - echo "{{.GREETING}}" silent: true ``` As you can see, all Taskfiles are written in [YAML format](https://yaml.org/). The `version` attribute specifies the minimum version of Task that can be used to run this file. The `vars` attribute is used to define variables that can be used in tasks. In this case, we are creating a string variable called `GREETING` with a value of `Hello, World!`. Finally, the `tasks` attribute is used to define the tasks that can be run. In this case, we have a task called `default` that echoes the value of the `GREETING` variable. The `silent` attribute is set to `true`, which means that the task metadata will not be printed when the task is run - only the output of the commands. ## Calling a task To call the task, invoke `task` followed by the name of the task you want to run. In this case, the name of the task is `default`, so you should run: ```shell task default ``` Note that we don't have to specify the name of the Taskfile. Task will automatically look for a file called `Taskfile.yml` (or any of Task's [supported file names](/docs/guide/running-tasks.md#supported-file-names)) in the current directory. Additionally, tasks with the name `default` are special. They can also be run without specifying the task name. If you created a Taskfile in a different directory, you can run it by passing the absolute or relative path to the directory as an argument using the `--dir` flag: ```shell task --dir ./subdirectory ``` Or if you created a Taskfile with a different name, you can run it by passing the name of the Taskfile as an argument using the `--taskfile` flag: ```shell task --taskfile Custom.yml ``` ## Adding a build task Let's create a task to build a program in Go. Start by adding a new task called `build` below the existing `default` task. We can then add a `cmds` attribute with a single command to build the program. Task uses [mvdan/sh](https://github.com/mvdan/sh), a native Go sh interpreter. So you can write sh/bash-like commands - even in environments where `sh` or `bash` are usually not available (like Windows). Just remember any executables called must be available as a built-in or in the system's `PATH`. When you're done, it should look something like this: ```yaml version: '3' vars: GREETING: Hello, World! tasks: default: desc: Print a greeting message cmds: - echo "{{.GREETING}}" silent: true build: cmds: - go build ./cmd/main.go ``` Call the task by running: ```shell task build ``` That's about it for the basics, but there's *so much* more that you can do with Task. Check out the rest of the documentation to learn more about all the features Task has to offer! We recommend taking a look at the [usage guide](/docs/guide.md) next. Alternatively, you can check out our reference docs for the [Taskfile schema](/docs/reference/schema.md) and [CLI](/docs/reference/cli.md). --- --- url: /docs/integrations.md description: >- Official and community integrations for Task, including VS Code, JSON schemas, and other tools --- # Integrations ## Visual Studio Code Extension Task has an [official extension for Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=task.vscode-task). The code for this project can be found in [our GitHub repository](https://github.com/go-task/vscode-task). To use this extension, you must have Task v3.45.3+ installed on your system. This extension provides the following features (and more): * View tasks in the sidebar. * Run tasks from the sidebar and command palette. * Go to definition from the sidebar and command palette. * Run last task command. * Multi-root workspace support. * Initialize a Taskfile in the current workspace. To get autocompletion and validation for your Taskfile, see the [Schema](/docs/integrations.md#schema) section below. ![Task for Visual Studio Code](https://github.com/go-task/vscode-task/blob/main/res/preview.png?raw=true) ### Configuration namespace change In v1.0.0 of the extension, the configuration namespace was changed from `task` to `taskfile` in order to fix [an issue](https://github.com/go-task/vscode-task/issues/56). ![Configuration namespace change warning](/img/config-namespace-change.png) If you receive a warning like the one above, you will need to update your settings to use the new `taskfile` namespace instead: ![Configuration namespace diff](/img/config-namespace-diff.png) ## Schema This was initially created by @KROSF in [this Gist](https://gist.github.com/KROSF/c5435acf590acd632f71bb720f685895) and is now officially maintained in [this file](https://github.com/go-task/task/blob/main/website/src/public/schema.json) and made available at https://taskfile.dev/schema.json. This schema can be used to validate Taskfiles and provide autocompletion in many code editors: ### Visual Studio Code To integrate the schema into VS Code, you need to install the [YAML extension](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml) by Red Hat. Any `Taskfile.yml` in your project should automatically be detected and validation/autocompletion should work. If this doesn't work or you want to manually configure it for files with a different name, you can add the following to your `settings.json`: ```json // settings.json { "yaml.schemas": { "https://taskfile.dev/schema.json": [ "**/Taskfile.yml", "./path/to/any/other/taskfile.yml" ] } } ``` You can also configure the schema directly inside of a Taskfile by adding the following comment to the top of the file: ```yaml # yaml-language-server: $schema=https://taskfile.dev/schema.json version: '3' ``` You can find more information on this in the [YAML language server project](https://github.com/redhat-developer/yaml-language-server). ## AI/LLM Assistants Task documentation is optimized for AI assistants like Claude Code, Cursor, and other LLM-powered development tools through the [VitePress LLMs plugin](https://github.com/okineadev/vitepress-plugin-llms). This integration provides: * Structured documentation in LLM-friendly formats * Context-optimized content for AI assistants * Automatic generation of `llms.txt` and `llms-full.txt` files * Enhanced discoverability of Task features for AI tools AI assistants can access Task documentation through: * **[llms.txt](https://taskfile.dev/llms.txt)**: Lightweight overview of Task documentation * **[llms-full.txt](https://taskfile.dev/llms-full.txt)**: Complete documentation with all content These files are automatically generated and kept in sync with the documentation, ensuring AI assistants always have access to the latest Task features and usage patterns. ## Community Integrations In addition to our official integrations, there is an amazing community of developers who have created their own integrations for Task: * [Sublime Text Plugin](https://packagecontrol.io/packages/Taskfile) \[[source](https://github.com/biozz/sublime-taskfile)] by @biozz * [IntelliJ Plugin](https://plugins.jetbrains.com/plugin/17058-taskfile) \[[source](https://github.com/lechuckroh/task-intellij-plugin)] by @lechuckroh * [Zed Extension](https://zed.dev/extensions/taskfile) \[[source](https://github.com/nickalie/zed-taskfile)] by @nickalie * [mk](https://github.com/pycontribs/mk) command line tool recognizes Taskfiles natively. * [fzf-make](https://github.com/kyu08/fzf-make) fuzzy finder with preview window for make, pnpm, yarn, just & task. If you have made something that integrates with Task, please feel free to open a PR to add it to this list. --- --- url: /docs/guide.md description: >- Learn to define tasks, supply inputs, control execution, configure the environment and output, and include other Taskfiles. --- # Guide Use the guide to learn a feature or solve a specific problem. If you are new to Task, start with the [Quick Start](/docs/getting-started.md). For accepted keys, flags and functions, use the [Taskfile Schema](/docs/reference/schema.md), [CLI](/docs/reference/cli.md) and [Templating](/docs/reference/templating.md) references. ## Writing and running tasks {#writing-and-running-tasks} * [Defining tasks](/docs/guide/defining-tasks.md): command lists, descriptions, aliases and display labels. * [Running tasks](/docs/guide/running-tasks.md): choosing a Taskfile, running from other directories, previewing commands and using interactive applications. ## Variables and arguments {#variables-and-environment} * [Variables](/docs/guide/variables.md): declaring values, computing them with shell commands, preserving types and understanding resolution order. * [Command-line arguments](/docs/guide/arguments.md): assigning variables, forwarding arguments with `--` and capturing values from task names. * [Validation and prompts](/docs/guide/required-variables.md): requiring inputs, restricting allowed values and asking for missing values. * [Secret variables](/docs/guide/secret-variables.md): loading sensitive values and masking them in Task's command logs. ## Task execution {#controlling-what-runs} * [Dependencies and task calls](/docs/guide/dependencies.md): running tasks concurrently or in sequence, limiting concurrency and controlling repeated calls. * [Loops](/docs/guide/loops.md): repeating commands or task calls over values, files and matrices. * [Conditional execution](/docs/guide/conditional-execution.md): skipping optional work with `if`, enforcing requirements with `preconditions` and asking for [confirmation](/docs/guide/conditional-execution.md#confirmation-prompts). * [Errors and cleanup](/docs/guide/errors-and-cleanup.md): continuing after selected failures and running cleanup with `defer`. * [Up-to-date checks](/docs/guide/up-to-date.md): skipping unchanged builds, checking for missing outputs and combining file checks with custom conditions. * [Watch mode](/docs/guide/watch.md): rerunning tasks when source files change. ## Environment and output {#execution-environment} * [Environment variables](/docs/guide/environment.md): setting the environment for commands and loading `.env` files. * [Platforms and shells](/docs/guide/platforms.md): selecting platform-specific commands, understanding shell context and setting shell options. * [Output and logging](/docs/guide/output.md): streaming, grouping and prefixing output, hiding command echoes and displaying CI annotations. ## Including Taskfiles {#composing-taskfiles} * [Including Taskfiles](/docs/guide/includes.md): sharing tasks with namespaces, configuring includes and passing variables to them. * [Remote Taskfiles](/docs/remote-taskfiles.md): loading Taskfiles over HTTP or Git and managing trust, checksums and cached copies. --- --- url: /docs/guide/defining-tasks.md description: >- Task syntax shortcuts, internal tasks, aliases, the directory a task runs in, and the help text Task shows for it. --- # Defining tasks Define tasks under `tasks`. Each task has a name and a list of commands in `cmds`. Add `desc` to explain what a task does: ```yaml version: '3' tasks: greet: desc: Print a greeting cmds: - echo "Hello, World!" ``` Run `task greet` to execute it, or `task --list` to see its description. See [Running tasks](/docs/guide/running-tasks.md) for selecting a Taskfile and previewing commands. ## Choose a task name The name under `tasks` is the name you pass to the CLI. Call your main task `default` if it should run when someone types `task` without a name. Use `desc` for the short explanation shown by `task --list`, `aliases` for alternative names, and `label` to customize log messages. The sections below show each independently. ## Use command shortcuts {#short-task-syntax} Use shorthand syntax for tasks that only need commands. Use the full form when you need properties such as `env`, `vars`, `desc` or `silent`: ```yaml version: '3' tasks: build: go build -v -o ./app{{exeExt}} . run: - task: build - ./app{{exeExt}} -h localhost -p 8080 ``` ## Keep helpers internal {#internal-tasks} Set `internal: true` on a helper that should only be called by other tasks. It stays out of `task --list` and `task --list-all`, and a direct CLI call fails. Expose a public task that supplies the helper's inputs: ```yaml version: '3' tasks: build-image-1: cmds: - task: build-image vars: DOCKER_IMAGE: image-1 build-image: internal: true cmds: - docker build -t {{.DOCKER_IMAGE}} . ``` ## Set the working directory {#task-directory} Set `dir` when commands need to run in a subdirectory. Relative paths are resolved from the Taskfile's execution directory: ```yaml version: '3' tasks: serve: dir: public/www cmds: # run http server - caddy ``` Run `task serve` to start the server in `public/www`. Task creates the directory if it does not exist. For included Taskfiles, configure the [include directory](/docs/guide/includes.md#directory-of-included-taskfile). ## Add shorter names {#task-aliases} Aliases are alternative names for tasks. They can be used to make it easier and quicker to run tasks with long or hard-to-type names. You can use them on the command line, when [calling sub-tasks](/docs/guide/dependencies.md#calling-another-task) in your Taskfile and when [including tasks](/docs/guide/includes.md) with aliases from another Taskfile. They can also be used together with [namespace aliases](/docs/guide/includes.md#namespace-aliases). ```yaml version: '3' tasks: generate: aliases: [gen] cmds: - task: gen-mocks generate-mocks: aliases: [gen-mocks] cmds: - echo "generating..." ``` ## Customize log labels {#overriding-task-name} Use `label` to customize the name shown in summaries and status messages. Labels can contain templates. They do not rename the task you call: ```yaml version: '3' tasks: default: cmds: - task: print vars: MESSAGE: hello - task: print vars: MESSAGE: world print: label: 'print-{{.MESSAGE}}' cmds: - echo "{{.MESSAGE}}" ``` ## Describe public tasks {#help} Running `task --list` (or `task -l`) lists all tasks with a description. The following Taskfile: ```yaml version: '3' tasks: build: desc: Build the Go binary. cmds: - go build -v main.go test: desc: Run all Go tests. cmds: - go test -race ./... js: cmds: - esbuild --bundle --minify js/index.js > public/bundle.js css: cmds: - esbuild --bundle --minify css/index.css > public/bundle.css ``` would print the following output: ```shell * build: Build the Go binary. * test: Run all Go tests. ``` Use `--list-all` (or `-a`) to also list tasks without a description. Internal tasks remain hidden. ## Explain a task in detail {#display-summary-of-task} Running `task --summary task-name` will show a summary of a task. The following Taskfile: ```yaml version: '3' tasks: release: deps: [build] summary: | Release your project to GitHub It will build your project before starting the release. Please make sure that you have set GITHUB_TOKEN before starting. cmds: - your-release-tool build: cmds: - your-build-tool ``` `task --summary release` prints: ``` task: release Release your project to GitHub It will build your project before starting the release. Please make sure that you have set GITHUB_TOKEN before starting. dependencies: - build commands: - your-release-tool ``` If a summary is missing, the description will be printed. If the task does not have a summary or a description, a warning is printed. Showing a summary does not execute the task. --- --- url: /docs/guide/running-tasks.md description: >- How Task finds a Taskfile, and how to run one from a subdirectory, from your home directory, from standard input or as a dry run. --- # Running tasks Run `task ` from your project to execute a task. With no name, Task runs the `default` task. Start by listing tasks, then run one: ```shell task --list task build ``` `--list` shows tasks with descriptions. Use `--list-all` to include tasks without descriptions; [internal tasks](/docs/guide/defining-tasks.md#internal-tasks) remain hidden. See [Defining tasks](/docs/guide/defining-tasks.md) to create these entries. Pass several names, such as `task lint test`, to run them in order. Use [`--parallel`](/docs/guide/dependencies.md#limiting-how-much-runs-at-once) for independent tasks that can run concurrently. To pass values or command arguments, see [Command-line arguments](/docs/guide/arguments.md). ## Preview commands {#dry-run-mode} Use `--dry` (or `-n`) to print the commands Task would run: ```shell task --dry build ``` Task still resolves templates and dynamic variables while preparing the commands. A variable declared with `sh` can therefore execute its shell command during a dry run. See [Variables](/docs/guide/variables.md#when-values-are-computed). ## Run from a subdirectory {#running-a-taskfile-from-a-subdirectory} If Task cannot find a Taskfile in the current directory, it searches parent directories. Tasks run from the directory containing that Taskfile unless [their `dir` setting](/docs/guide/defining-tasks.md#task-directory) changes it. Use `{{.USER_WORKING_DIR}}` when the task should act on the directory where you invoked it. In a monorepo, this lets each service use the same task to start its Docker Compose services: ```yaml version: '3' tasks: up: dir: '{{.USER_WORKING_DIR}}' preconditions: - test -f docker-compose.yml cmds: - docker compose up -d ``` From a service directory containing `docker-compose.yml`, run `task up`. The root Taskfile supplies the task; the service directory supplies its files. ## Use a personal Taskfile {#running-a-global-taskfile} Store a Taskfile in your home directory and use `--global` (or `-g`) to call it from anywhere: ```shell task --global from-working-directory ``` Global tasks run in your home directory by default. Set `dir` to `{{.USER_WORKING_DIR}}` when a personal utility should work on the current project: ```yaml version: '3' tasks: from-home: cmds: - pwd from-working-directory: dir: '{{.USER_WORKING_DIR}}' cmds: - pwd ``` ## Read a Taskfile from stdin {#running-a-taskfile-from-stdin} Use `--taskfile -` (or `-t -`) to read a generated Taskfile from standard input. For example, to read an existing file through stdin: ```shell task --taskfile - < ./Taskfile.yml ``` You can also pipe the output of a Taskfile generator into `task --taskfile -`. With no task name, these commands run the generated file's `default` task. ## Run a terminal app {#interactive-cli-application} Set `interactive: true` on a task that launches a terminal application: ```yaml version: '3' tasks: edit: interactive: true cmds: - vim my-file.txt ``` Run `task edit` to open the editor. Interactive applications need direct access to the terminal; buffered or prefixed [output modes](/docs/guide/output.md#output-syntax) and other tasks running concurrently can interfere with their input and display. This task setting is distinct from the CLI `--interactive` option, which [prompts for missing variables](/docs/guide/required-variables.md#prompting-for-missing-variables-interactively). ## Choose a file name {#supported-file-names} Use `--taskfile` to choose a specific file or `--dir` to run in another project: ```shell task --taskfile Taskfile.ci.yml build task --dir ./backend build ``` Task looks for these names in order of priority: * `Taskfile.yml` * `taskfile.yml` * `Taskfile.yaml` * `taskfile.yaml` * `Taskfile.dist.yml` * `taskfile.dist.yml` * `Taskfile.dist.yaml` * `taskfile.dist.yaml` Commit a `.dist` variant if users need to supply their own `Taskfile.yml` override. The local file takes priority; add it to `.gitignore` if it should remain personal. --- --- url: /docs/guide/variables.md description: >- Declare variables, compute values with shell commands, preserve types with references, and understand resolution order. --- # Variables Declare variables with `vars`, then read them in a command with a template: ```yaml version: '3' vars: NAME: World tasks: greet: cmds: - echo "Hello, {{.NAME}}!" ``` Run `task greet` to print `Hello, World!`, or `task greet NAME=Bob` to print `Hello, Bob!`. This example puts the default at the root of the Taskfile so callers can override it. Use a literal for a fixed value, `sh` to compute text with a command, and `ref` to preserve arrays and maps. The examples come first; [resolution order](/docs/guide/variables.md#resolution-order) explains how values from different places interact. Pass `NAME=value` on the command line to supply variables to Task. See [Command-line arguments](/docs/guide/arguments.md#variable-assignments) for quoting, multiple tasks and the difference from arguments after `--`. ::: tip The special variable `.TASK` contains the name of the task being run. ::: ## Choose a value type {#variable-types} Variables support the following types: * `string` * `bool` * `int` * `float` * `array` * `map` ::: info Defining a map requires that you use a special `map` subkey (see example below). ::: ```yaml version: 3 tasks: foo: vars: STRING: 'Hello, World!' BOOL: true INT: 42 FLOAT: 3.14 ARRAY: [1, 2, 3] MAP: map: { A: 1, B: 2, C: 3 } cmds: - 'echo {{.STRING}}' # Hello, World! - 'echo {{.BOOL}}' # true - 'echo {{.INT}}' # 42 - 'echo {{.FLOAT}}' # 3.14 - 'echo {{.ARRAY}}' # [1 2 3] - 'echo {{index .ARRAY 0}}' # 1 - 'echo {{.MAP}}' # map[A:1 B:2 C:3] - 'echo {{.MAP.A}}' # 1 ``` ## Compute a value {#dynamic-variables} Use `sh` to compute a variable from a shell command. Task captures its output and trims the final trailing newline, if present. ```yaml version: '3' tasks: build: cmds: - go build -ldflags="-X main.Version={{.GIT_COMMIT}}" main.go vars: GIT_COMMIT: sh: git log -n 1 --format=%h ``` The result is text. To convert JSON or YAML output into structured data, use [parsing functions with `ref`](/docs/guide/variables.md#parsing-json-yaml-into-map-variables). ## Pass lists and maps {#referencing-other-variables} Use `ref` to pass an array or map to another task without converting it to text. In this example, `show` receives the original list: ```yaml version: '3' tasks: default: vars: SERVICES: [api, worker] cmds: - task: show vars: SERVICES: ref: .SERVICES show: cmds: - echo '{{index .SERVICES 0}}' ``` Run `task` to print `api`. Replacing the reference with `SERVICES: '{{.SERVICES}}'` would pass the string `[api worker]`. Indexing that string returns a byte, not a list element. References also work in dependency calls and variable definitions: ```yaml version: '3' tasks: default: vars: SERVICES: [api, worker] SELECTED: ref: .SERVICES deps: - task: show vars: SERVICE: ref: index .SELECTED 0 show: cmds: - echo '{{.SERVICE}}' ``` Use subkeys, indexes, and functions inside `ref`, without the template braces. See the [Templating reference][templating-reference] for available functions. ## Parse JSON or YAML {#parsing-json-yaml-into-map-variables} If you have a raw JSON or YAML string that you want to process in Task, you can use a combination of the `ref` keyword and the `fromJson` or `fromYaml` templating functions to parse the string into a map variable. For example: ```yaml version: '3' tasks: task-with-map: vars: JSON: '{"a": 1, "b": 2, "c": 3}' FOO: ref: 'fromJson .JSON' cmds: - echo {{.FOO}} ``` ```txt map[a:1 b:2 c:3] ``` ## Understand precedence {#resolution-order} Applied first to last. Later wins. | # | Source | Set by | | --- | ----------------------------------- | ----------------------------------------------------------------------------- | | 1 | The process environment | the shell that ran `task` | | 2 | Special variables | Task itself (`TASK`, `ROOT_DIR`, `CLI_ARGS`, …) | | 3 | Taskfile `env:` | the `env:` block; `dotenv:` files fill only names `env:` does not already set | | 4 | Global `vars:` | the `vars:` block of every Taskfile in the run | | 5 | Include `vars:` | the `vars:` given on an `includes:` entry | | 6 | The included Taskfile's own `vars:` | the `vars:` block of the file being included | | 7 | Call variables | `task foo BAR=1`, or `vars:` on a `task:` command | | 8 | The task's `vars:` | the `vars:` block of the task being run | ## Apply precedence rules {#what-this-means-in-practice} ### Let callers override {#a-task-s-own-variables-cannot-be-overridden-from-the-command-line} Step 8 comes after step 7, so a variable declared on the task always wins: ```yaml version: '3' tasks: greet: vars: NAME: from-task cmds: - echo "{{.NAME}}" ``` ```shell $ task greet NAME=from-cli from-task ``` To let a caller supply a value, give the default somewhere earlier, in global `vars:`, or use a template default: ```yaml version: '3' vars: NAME: from-global tasks: greet: cmds: - echo "{{.NAME}}" ``` ```shell $ task greet NAME=from-cli from-cli ``` To keep the default on the task itself, read the caller's value in a template: ```yaml vars: NAME: '{{.NAME | default "World"}}' ``` ### Configure include defaults {#let-callers-configure-an-included-taskfile} Step 6 comes after step 5, so a constant declared in the included Taskfile's `vars:` overrides the value supplied by `includes.vars`. To keep a configurable default in the included Taskfile, use a template that reads the previously resolved value: ```yaml version: '3' vars: DOCKER_IMAGE: '{{.DOCKER_IMAGE | default "app"}}' tasks: build: cmds: - echo "building {{.DOCKER_IMAGE}}" ``` An inclusion that supplies `DOCKER_IMAGE: backend_image` now prints `building backend_image`; without a supplied value, it prints `building app`. The default stays in one place and applies to every task in the included file. ### Avoid global collisions {#global-variable-names-are-shared-across-every-taskfile-in-the-run} Global `vars:` are merged into one set before any task runs, so a name declared in both the entrypoint and an included Taskfile resolves to the included one, including for tasks defined in the entrypoint. Give globals that belong to an included Taskfile a distinctive name, or move them onto the tasks that use them, where step 8 keeps them local. ### Choose vars or env {#env-and-vars-are-not-the-same-thing} A `vars:` entry is not exported to commands in `cmds`, whichever level it was declared at. Read it with a template instead of `$FOO`. Dynamic variables (`sh:`) have a different environment, described below. `env:` is exported to the environment of the commands Task runs, so `$FOO` works. Whether a template also sees it depends on where it was declared: | Declared at | `{{.FOO}}` | `$FOO` | | ------------------------ | ------------------------------------------ | ------ | | the root of the Taskfile | yes, it is step 3 above | yes | | on a task | keeps the value from other sources, if any | yes | A task's `env:` is assembled after the variable set has been resolved, so it never takes part in the order on this page. If global `env:` sets `FOO: root` and a task's `env:` sets `FOO: task`, the template `{{.FOO}}` renders `root`, while `$FOO` in a command reads `task` (assuming the process environment does not already set `FOO`). The template only renders empty if no other source defines the variable. Read a task-level environment value with `$FOO`, or declare it in `vars:` if a template needs it. ## Understand evaluation {#when-values-are-computed} Dynamic variables (`sh:`) are executed while the set is being built, in the order above. Within a block, variables are resolved in declaration order, so a `sh:` command can also reference variables declared earlier in the same block: ```yaml vars: INPUT: hello OUTPUT: sh: echo {{.INPUT}} ``` Here `OUTPUT` resolves to `hello`. Variables from later declarations or later steps are not available yet. The shell used by `sh:` also receives previously resolved scalar variables, so `sh: echo $INPUT` works in this example too. The process environment takes precedence for shell lookups unless the [Env Precedence experiment](/docs/experiments/env-precedence.md) is enabled. Commands in `cmds` only receive the process environment and Task's `env:` and `dotenv:` values; they do not inherit `vars:` this way. Results are cached for the run, keyed on the command string, so the same `sh:` command appearing twice runs once. To pass a variable without flattening it to text, an array or a map, use `ref:` instead of `{{ }}`. A template renders a string; `ref:` preserves the type. ::: tip Related guide Mark sensitive values with `secret: true` to mask them in Task's own command logs. See [Secret variables](/docs/guide/secret-variables.md) for examples, loading values from external sources and the limits of masking. ::: [templating-reference]: /docs/reference/templating.md --- --- url: /docs/guide/arguments.md description: >- Supply variables with NAME=value, forward arguments after --, and capture values with wildcard task names. --- # Command-line arguments {#passing-arguments} Choose how to pass input based on what needs to receive it: | Input | Example | Read it with | | ----------------------- | ---------------------- | --------------------------------------- | | A Task variable | `task greet NAME=Bob` | `{{.NAME}}` | | Arguments for a command | `task yarn -- install` | `{{.CLI_ARGS}}` | | Part of a task name | `task start:api` | `{{index .MATCH 0}}` | ## Pass named values {#variable-assignments} Pass `NAME=value` to supply a Task variable. A default in the root `vars` block can be overridden from the command line: ```yaml version: '3' vars: NAME: World tasks: greet: cmds: - echo "Hello, {{.NAME}}!" ``` ```shell task greet NAME=Bob task greet "NAME=Jane Doe" ``` Quote assignments that contain spaces. These assignments work across shells, including Windows, and are shared by all tasks in the invocation: ```shell task build test ENV=production ``` A value declared in a task's own `vars` can override the command-line value. See [variable resolution](/docs/guide/variables.md#resolution-order) for precedence and configurable defaults. To set values in a command's shell environment, see [Environment variables](/docs/guide/environment.md). ## Forward command arguments {#forwarding-cli-arguments-to-commands} Everything after `--` is available in `.CLI_ARGS`, including values containing `=`. Insert it into a command to forward those arguments. This example runs `yarn install`: ```shell $ task yarn -- install ``` ```yaml version: '3' tasks: yarn: cmds: - yarn {{.CLI_ARGS}} ``` ## Capture task-name inputs {#wildcard-arguments} Use `*` in a task name to turn a call such as `task start:api` into an input. Task captures each wildcard in the `.MATCH` array. Give the captured values names with `vars` so their meaning stays clear: ```yaml version: '3' tasks: start:*:*: vars: SERVICE: '{{index .MATCH 0}}' REPLICAS: '{{index .MATCH 1}}' cmds: - echo "Starting {{.SERVICE}} with {{.REPLICAS}} replicas" start:*: vars: SERVICE: '{{index .MATCH 0}}' cmds: - echo "Starting {{.SERVICE}}" ``` The first captured value becomes `SERVICE`: ```shell $ task start:foo Starting foo ``` You can use whitespace in your arguments as long as you quote the task name: ```shell $ task "start:foo bar" Starting foo bar ``` If multiple matching tasks are found, the first one listed in the Taskfile will be used. If you are using included Taskfiles, tasks in parent files will be considered first. ```shell $ task start:foo:3 Starting foo with 3 replicas ``` Wildcard task names also work with aliases: ```yaml version: '3' tasks: start:*: aliases: [run:*] vars: SERVICE: '{{index .MATCH 0}}' cmds: - echo "Running {{.SERVICE}}" ``` Call the task through the `run:*` alias: ```shell $ task run:foo Running foo ``` --- --- url: /docs/guide/required-variables.md description: >- Require variables to be set, restrict them to allowed values, and prompt for missing input. --- # Validation and prompts {#required-variables-and-prompts} Use `requires` to catch missing inputs before a task runs. Add `enum` when only certain values are allowed, and enable interactive prompts when users should be able to fill in missing values at the terminal. ## Require an input {#ensuring-required-variables-are-set} Declare inputs in `requires.vars`, then pass them on the command line: ```yaml version: '3' tasks: release: requires: vars: [VERSION] cmds: - echo "Preparing release {{.VERSION}}" ``` Run `task release VERSION=1.2.3` to print the message. Without `VERSION`, Task fails and identifies the missing input. Task variables and values inherited from the process environment can satisfy the requirement. A variable set to an empty string counts as present. To reject an empty value, use an enum of nonempty strings or a [precondition](/docs/guide/conditional-execution.md#using-programmatic-checks-to-cancel-the-execution-of-a-task-and-its-dependencies). ## Restrict allowed values {#ensuring-required-variables-have-allowed-values} Use `name` and `enum` to require a string from a fixed list: ```yaml version: '3' tasks: deploy: requires: vars: - name: ENV enum: [dev, staging, prod] cmds: - echo "Deploying to {{.ENV}}" ``` `task deploy ENV=staging` succeeds. `task deploy ENV=preview` fails before running the command. Enum validation applies to string variables, including strings inherited from the environment. ## Ask for missing input {#prompting-for-missing-variables-interactively} Run `task --interactive deploy` with the example above to choose the missing `ENV` from a menu. Required variables without an enum use a text input instead. To enable this behavior by default, set it in your [Task configuration](/docs/reference/config.md#interactive): ```yaml [~/.taskrc.yml] interactive: true ``` For a task with both kinds of input: ```yaml version: '3' tasks: deploy: requires: vars: - name: ENVIRONMENT enum: [dev, staging, prod] - VERSION cmds: - echo "Deploying {{.VERSION}} to {{.ENVIRONMENT}}" ``` `task --interactive deploy` asks for an environment and a version. Values already supplied through the CLI, environment, or Taskfile do not prompt: ```shell task deploy ENVIRONMENT=prod VERSION=1.0.0 ``` Prompts require a terminal. In CI or another non-interactive environment, missing variables cause an error; supply all required values explicitly. ## Reuse allowed values {#using-variable-references-for-enum-values} Define a shared list and reference it from an enum: ```yaml version: '3' vars: ALLOWED_ENVS: [dev, staging, prod] tasks: deploy: requires: vars: - name: ENV enum: ref: .ALLOWED_ENVS cmds: - echo "Deploying to {{.ENV}}" ``` References can also transform values. Given a `config.json` file containing `{"allowed_environments": ["dev", "prod"]}`, this reads the list from it: ```yaml version: '3' vars: CONFIG: sh: cat config.json tasks: deploy: requires: vars: - name: ENV enum: ref: ( .CONFIG | fromJson ).allowed_environments cmds: - echo "Deploying to {{.ENV}}" ``` For newline-separated command output, convert it to a list before using it as an enum. This example uses the entries in an existing `services/` directory: ```yaml version: '3' vars: AVAILABLE_SERVICES: sh: ls services/ tasks: deploy: requires: vars: - name: SERVICE enum: ref: .AVAILABLE_SERVICES | splitLines | compact cmds: - echo "Deploying {{.SERVICE}}" ``` ::: tip Related guide A prompt for input fills in a value. A [confirmation prompt](/docs/guide/conditional-execution.md#confirmation-prompts) asks whether execution should proceed, even if all input values are already present. ::: --- --- url: /docs/guide/secret-variables.md description: >- Mask sensitive variable values in Task's command logs, load secrets from external sources, and understand what masking does not protect. --- # Secret variables Set `secret: true` on a variable to replace its value with `*****` in Task's own command logs. The command still receives the original value. ```yaml version: '3' vars: API_TOKEN: sh: echo "$MY_API_TOKEN" secret: true tasks: request: cmds: - 'curl -H "Authorization: Bearer {{.API_TOKEN}}" https://api.example.com' # Logged as: curl -H "Authorization: Bearer *****" https://api.example.com ``` Set `MY_API_TOKEN` in the environment before running `task request`. Task's log masks the token, while `curl` receives the real value. The example URL is a placeholder; replace it with your API endpoint. Secret variables can be declared globally or on a task. Use `value` instead of `sh` for a literal or template value, and keep real credentials out of committed Taskfiles. ## Load from a secret store {#loading-secrets} Use a dynamic variable to read a secret from an external source: ```yaml version: '3' vars: API_TOKEN: sh: vault kv get -field=api_key secret/myapp secret: true tasks: request: cmds: - 'curl -H "Authorization: Bearer {{.API_TOKEN}}" https://api.example.com' ``` To read an existing environment variable, replace the `sh` command with `echo "$MY_API_TOKEN"`. Keep local credential files, such as `.env.local`, out of version control. For dynamic variable evaluation and caching, see [When values are computed](/docs/guide/variables.md#when-values-are-computed). ## Mark derived secrets {#derived-values} The `secret` flag does not propagate to variables that reference a secret. Mark every variable carrying the sensitive value as secret: ```yaml version: '3' vars: API_TOKEN: sh: echo "$MY_API_TOKEN" secret: true AUTH_HEADER: value: 'Bearer {{.API_TOKEN}}' secret: true tasks: request: cmds: - 'curl -H "Authorization: {{.AUTH_HEADER}}" https://api.example.com' # Logged as: curl -H "Authorization: *****" https://api.example.com ``` Without `secret: true` on `AUTH_HEADER`, Task's command log would expose the derived value. ## Understand masking limits {#limits-of-masking} Masking applies to Task's own command logs, including when those logs are collected by a CI system. It does not redact: * stdout or stderr produced by commands; * process arguments visible to tools such as `ps`; * values stored in shell history or committed files; * derived variables that are not themselves marked as secret. For example, a command that echoes a secret still prints its real value, even though Task masks the command text it logs before execution. The `secret` flag applies to `vars`, not `env`. Use your command's supported credential mechanism to control how it receives secrets. --- --- url: /docs/guide/dependencies.md description: >- Run checks before a build, put tasks in order, pass variables, and control repeated calls and cleanup. --- # Dependencies and task calls A build often needs several steps: lint the code, run tests, then compile. Use `deps` when the checks can run in parallel, or task calls in `cmds` when each step must finish before the next starts. ## Run dependencies first {#task-dependencies} In a Go project, this Taskfile runs lint and tests before building: ```yaml version: '3' tasks: build: deps: [lint, test] cmds: - go build ./... lint: cmds: - go vet ./... test: cmds: - go test ./... ``` Run `task build`. Task starts `lint` and `test` concurrently and waits for both to succeed before running `go build`. If either fails, the build command does not run. The order of entries in `deps` does not set their execution order. Use [task calls in `cmds`](/docs/guide/dependencies.md#calling-another-task) if tests must wait for lint to finish. You can also group tasks without adding commands. Add this task to the same Taskfile to run only the checks with `task check`: ```yaml tasks: check: deps: [lint, test] ``` ## Run task calls in order {#calling-another-task} To run lint, then tests, then the build, replace `build` in the example above with: ```yaml tasks: build: cmds: - task: lint - task: test - go build ./... ``` Task waits for each command or task call to succeed before starting the next. If lint fails, neither tests nor the build run. Each called task still runs its own dependencies first. You can combine the two forms: use `deps` for independent prerequisites and `cmds` for the steps that need an order. ::: tip Calling tasks from another Taskfile To call a task declared in the root Taskfile from an [included Taskfile](/docs/guide/includes.md), add a leading `:`, for example `task: :build`. ::: ## Pass variables to a task Use the object form of a task call to provide `vars`. You can also set `silent: true` to hide the command being executed for that call: ```yaml version: '3' tasks: greet: cmds: - echo "Hello, {{.RECIPIENT}}!" greet:all: cmds: - task: greet vars: { RECIPIENT: 'World' } - task: greet vars: { RECIPIENT: 'Cruel World' } silent: true ``` `task greet:all` prints both greetings in order. The second call still prints its greeting: [silent mode](/docs/guide/output.md#silent-mode) hides the command, not its output. The same `task`, `vars`, and `silent` fields work in `deps`. Use [references](/docs/guide/variables.md#referencing-other-variables) to pass arrays and maps without turning them into strings. To call a task for each value in a list, see [Looping over tasks](/docs/guide/loops.md#looping-over-tasks). ## Control parallel work Dependencies run concurrently by default. To run tasks from the command line in parallel too, use `task --parallel lint test` (or `task -p lint test`). ### Limit concurrent tasks {#limiting-how-much-runs-at-once} Use `--concurrency` (or `-C`) when tasks compete for resources such as database connections or memory: ```shell task --concurrency 2 build ``` The default is `0`, meaning no limit. A limit of `1` restricts concurrency but does not establish an order between dependencies; use task calls in `cmds` when order matters. ### Cancel on failure {#fail-fast-dependencies} By default, Task lets the other dependencies finish if one fails. In either case, the parent task's commands do not run after a dependency failure. Set `failfast: true` on a task to cancel its remaining dependency work when one dependency fails. For the build example: ```yaml tasks: build: deps: [lint, test] failfast: true cmds: - go build ./... ``` Already-running commands receive cancellation; this does not undo work they have completed. You can also set `failfast: true` in [`.taskrc.yml`][config] or use `task --failfast build`. The CLI flag also applies to tasks started with `--parallel`. ### Read parallel output {#interleaved-output-is-expected} Concurrent tasks can interleave their output, and the order may change between runs. Set `output: prefixed` to label each line with its task, or `output: group` to print each command's buffered output in one block when that command finishes. A group is per command, so blocks from different tasks can still alternate. See [Output and logging](/docs/guide/output.md) for examples. ## Control repeated calls {#repeated-calls} A shared dependency can be reached through several tasks. By default, Task attempts to run it for each call. Use `run` to control repeated calls within one invocation: | Value | Behavior | | ------------------ | ------------------------------------------------- | | `always` (default) | Attempt to run the task on every call. | | `once` | Run the task once, even if it is called again. | | `when_changed` | Run once for each distinct set of call variables. | Set `run` on a task, or at the root of the Taskfile to provide a default for all tasks. A task's own setting takes precedence. ### Run shared setup once {#running-a-task-only-once} If lint and tests both need setup, mark the setup task with `run: once`: ```yaml version: '3' tasks: check: deps: [lint, test] lint: deps: [setup] cmds: - echo "Linting" test: deps: [setup] cmds: - echo "Testing" setup: run: once cmds: - echo "Setting up" ``` `task check` prints `Setting up` once. Both lint and tests wait for that setup to finish. Without `run: once`, setup runs for each task that depends on it. ### Run once per input {#once-per-set-of-variables} Use `run: when_changed` when a task may be called several times with the same inputs: ```yaml version: '3' tasks: default: cmds: - task: generate vars: { CONTENT: '1' } - task: generate vars: { CONTENT: '2' } - task: generate vars: { CONTENT: '2' } generate: run: when_changed cmds: - echo {{.CONTENT}} ``` This prints `1` and `2`. The third call repeats the second call's variables and is skipped. The `run` settings do not remember earlier invocations of Task. To skip work when files or external artifacts have not changed, use [up-to-date checks](/docs/guide/up-to-date.md). ## Clean up after a task {#doing-task-cleanup-with-defer} Use `defer` when a task acquires a resource that needs to be released. For example, this task starts Docker Compose services, runs integration tests, and stops the services even if the tests fail: ```yaml version: '3' tasks: integration: cmds: - docker compose up -d - defer: docker compose down - go test -tags=integration ./... ``` Cleanup is registered when Task reaches the `defer` entry. Dependencies run before `cmds`, so a failed dependency prevents that registration. Keep the resource setup, `defer`, and the work that uses the resource in the command sequence, as above. For multiple cleanup steps, cleanup tasks, and inspecting failures with `.EXIT_CODE`, see [Errors and cleanup](/docs/guide/errors-and-cleanup.md#cleanup-with-defer). [config]: /docs/reference/config.md --- --- url: /docs/guide/loops.md description: >- Repeat a command over a static list, a matrix, a variable, your task's sources, or other tasks. --- # Loops Use `for` to repeat a command or task call with different values. Start with a list, then choose a different input source when the values come from a variable, files, or several combinations. ## Repeat a command {#looping-over-a-static-list} In a project containing `README.md` and `LICENSE`, this task saves a copy of each file in `backup/`. Put `for` beside `cmd` and read the current filename through `.ITEM`: ```yaml version: '3' tasks: backup: cmds: - mkdir -p backup - for: [README.md, LICENSE] cmd: cp '{{.ITEM}}' backup/ ``` Run `task backup` to create `backup/README.md` and `backup/LICENSE`. Command loops run in list order. Use [dependency loops](/docs/guide/loops.md#looping-over-dependencies) for iterations that may run concurrently. For a changing set of files, use [a source glob](/docs/guide/loops.md#looping-over-your-task-s-sources-or-generated-files) instead of maintaining a fixed list. ## Read values from variables {#looping-over-variables} Use `var` to read an existing variable. Strings are split on whitespace: ```yaml version: '3' tasks: greet: vars: NAMES: Alice Bob cmds: - for: { var: NAMES } cmd: echo "Hello, {{.ITEM}}!" ``` For another separator, set `split`: ```yaml version: '3' tasks: greet: vars: NAMES: Alice,Bob cmds: - for: { var: NAMES, split: ',' } cmd: echo "Hello, {{.ITEM}}!" ``` Arrays preserve values containing spaces: ```yaml version: '3' tasks: greet: vars: NAMES: ['Alice Smith', 'Bob Jones'] cmds: - for: { var: NAMES } cmd: echo "Hello, {{.ITEM}}!" ``` Maps expose the key as `.KEY` and the value as `.ITEM`. Their iteration order is not guaranteed: ```yaml version: '3' tasks: services: vars: PORTS: map: { api: 8080, worker: 9090 } cmds: - for: { var: PORTS } cmd: echo "{{.KEY}} uses port {{.ITEM}}" ``` Dynamic variables work too. This example reads newline-separated names from an existing `names.txt` file: ```yaml version: '3' tasks: greet: vars: NAMES: sh: cat names.txt cmds: - for: { var: NAMES, split: "\n" } cmd: echo "Hello, {{.ITEM}}!" ``` ## Name the current item {#renaming-variables} Use `as` to name the current item when that makes a command easier to read: ```yaml version: '3' tasks: greet: cmds: - for: { var: NAMES, as: NAME } cmd: echo "Hello, {{.NAME}}!" vars: NAMES: [Alice, Bob] ``` ## Repeat a task call {#looping-over-tasks} Put `task` beside `for` to reuse another task for each item: ```yaml version: '3' tasks: default: cmds: - for: [api, worker] task: check vars: SERVICE: '{{.ITEM}}' check: cmds: - echo "Checking {{.SERVICE}}" ``` `task` checks `api` before `worker`. Each call finishes before the next starts. You can also template the task name itself: ```yaml version: '3' tasks: default: cmds: - for: [api, worker] task: check:{{.ITEM}} check:api: echo 'Checking API' check:worker: echo 'Checking worker' ``` ## Run iterations in parallel {#looping-over-dependencies} Use a loop in `deps` when the calls can run concurrently: ```yaml version: '3' tasks: default: deps: - for: [api, worker] task: check vars: SERVICE: '{{.ITEM}}' check: cmds: - echo "Checking {{.SERVICE}}" ``` The messages may appear in either order. All the input forms shown on this page also work with dependency loops. See [Dependencies and task calls](/docs/guide/dependencies.md) for concurrency limits and repeated-call policies. ## Try every combination {#looping-over-a-matrix} Use `matrix` to visit every combination of several lists: ```yaml version: '3' tasks: targets: cmds: - for: matrix: OS: [linux, darwin] ARCH: [amd64, arm64] cmd: echo "{{.ITEM.OS}}/{{.ITEM.ARCH}}" ``` `task targets` prints `linux/amd64`, `linux/arm64`, `darwin/amd64`, and `darwin/arm64`. These are ordinary iteration values; they do not change the platform on which Task runs. Use `ref` to reuse lists defined elsewhere: ```yaml version: '3' vars: SYSTEMS: [linux, darwin] ARCHITECTURES: [amd64, arm64] tasks: targets: cmds: - for: matrix: OS: { ref: .SYSTEMS } ARCH: { ref: .ARCHITECTURES } cmd: echo "{{.ITEM.OS}}/{{.ITEM.ARCH}}" ``` ## Loop over files {#looping-over-your-task-s-sources-or-generated-files} Use `for: sources` or `for: generates` to reuse the task's file declarations. Globs are expanded before iteration: ::: code-group ```yaml [Sources] version: '3' tasks: show: sources: ['src/*.txt'] cmds: - for: sources cmd: cat '{{.ITEM}}' ``` ```yaml [Generates] version: '3' tasks: show: generates: ['output/*.txt'] cmds: - for: generates cmd: cat '{{.ITEM}}' ``` ::: Run `task show` with matching files present. Paths are relative to the task's working directory. Source declarations also enable [up-to-date checks](/docs/guide/up-to-date.md); use `method: none` if this task should print the files every time. To build an absolute path, combine a directory with the item using the `joinPath` template function, for example `{{joinPath .TASK_DIR .ITEM}}`. See the [file-path variables](/docs/reference/templating.md#file-paths) for the available directory values. --- --- url: /docs/guide/conditional-execution.md description: >- Skip optional work with if, enforce requirements with preconditions, and ask for confirmation before execution with prompt. --- # Conditional execution Decide what a failed check should mean before choosing a setting: skip optional work, stop with an error, or ask the user whether to continue. | What do you need? | Use | | -------------------------------- | ---------------------------------------------------------------------------------------------------- | | Skip optional work | [`if`](/docs/guide/conditional-execution.md#conditional-execution-with-if) | | Stop when a requirement is unmet | [`preconditions`](/docs/guide/conditional-execution.md#using-programmatic-checks-to-cancel-the-execution-of-a-task-and-its-dependencies) | | Ask before executing commands | [`prompt`](/docs/guide/conditional-execution.md#confirmation-prompts) | | Skip work already completed | [Up-to-date checks](/docs/guide/up-to-date.md) | ## Skip optional work {#conditional-execution-with-if} An `if` condition is a shell command. Exit code `0` allows execution; a non-zero exit code skips the guarded task or command without failing the run. ### Skip a whole task {#task-level-if} Use a task-level condition for optional work. This task only runs when a `docs/` directory exists: ```yaml version: '3' tasks: docs: if: test -d docs cmds: - echo 'Building documentation' ``` Run `task docs` with and without the directory. If it is absent, Task skips the task and its dependencies. Other tasks in the invocation can continue. ### Skip one command {#command-level-if} Put `if` on a command when the rest of the task should still run: ```yaml version: '3' tasks: build: cmds: - cmd: echo 'Building documentation' if: test -d docs - echo 'Building the application' ``` `task build` always prints the application message. It prints the documentation message first only if `docs/` exists. ### Check a Task variable {#using-templates-in-if-conditions} Template comparisons produce `true` or `false`, which are also shell commands with the corresponding exit codes. Put a configurable default at the root: ```yaml version: '3' vars: ENABLE_FEATURE: 'false' tasks: feature: if: '{{eq .ENABLE_FEATURE "true"}}' cmds: - echo 'Feature is enabled' ``` `task feature ENABLE_FEATURE=true` runs the command. Without that override, Task skips it. ### Filter loop iterations {#using-if-with-for-loops} A command's condition is evaluated for each iteration, with that iteration's values: ```yaml version: '3' tasks: process-items: cmds: - for: ['a', 'b', 'c'] cmd: echo "processing {{.ITEM}}" if: '[ "{{.ITEM}}" != "b" ]' ``` Run `task process-items` to print `processing a` and `processing c`. See [Loops](/docs/guide/loops.md) for the other iteration forms. ## Enforce a requirement {#using-programmatic-checks-to-cancel-the-execution-of-a-task-and-its-dependencies} Use `preconditions` when missing a requirement should fail the task. Every check must exit with code `0`. Add `msg` to explain how to resolve a failure: ```yaml version: '3' tasks: generate-files: preconditions: - sh: test -f .env msg: 'Create a .env file before generating files.' cmds: - mkdir -p directory - touch directory/file.txt ``` `task generate-files` fails with the message if `.env` is missing. Create the file and run it again to generate `directory/file.txt`. A plain shell string also works when no custom message is needed. A failed precondition fails tasks that call or depend on this task. Dependencies of the checked task have already run before its preconditions are evaluated; put a check on the task whose commands need protecting. `--force` bypasses preconditions as well as up-to-date checks. ### Choose skip or failure {#if-vs-preconditions} Use `if` when skipping is an acceptable result. Use `preconditions` when a caller should receive an error instead. To require a variable or restrict its allowed values, use [Validation and prompts](/docs/guide/required-variables.md). ## Ask for confirmation {#confirmation-prompts} Use `prompt` to ask for approval before a task's commands run. For example, this task asks before deleting a local build directory: ```yaml version: '3' tasks: clean: prompt: Remove the local build directory? cmds: - rm -rf build/ ``` Run `task clean` in a terminal and answer the confirmation. Declining stops the task with [exit code](/docs/reference/cli.md#exit-codes) `205`. Unlike [prompts for missing input](/docs/guide/required-variables.md#prompting-for-missing-variables-interactively), confirmation prompts do not require `--interactive`. Supplying every required variable does not bypass them. For several confirmations, use a list: ```yaml prompt: - Remove the local build directory? - Have you saved everything you need from it? ``` Task asks after dependencies have completed and before running `cmds`. If approval must precede other tasks, call them from `cmds` in the prompted task instead of listing them as dependencies. Use `task --yes clean` (or `task -y clean`) to accept automatically. In CI or another environment without a terminal, confirmation fails unless automatic acceptance is enabled. ::: tip Related guide To control how often a task runs when several tasks call it, use [`run: once` or `run: when_changed`](/docs/guide/dependencies.md#repeated-calls). These settings apply within one invocation. Use [up-to-date checks](/docs/guide/up-to-date.md) to skip completed work across invocations. ::: --- --- url: /docs/guide/errors-and-cleanup.md description: >- Continue after selected command failures with ignore_error, schedule cleanup with defer, and inspect a failed command's exit code. --- # Errors and cleanup By default, a failed command stops its task. Use `ignore_error` when a failure can be tolerated, and `defer` for cleanup that must run after work finishes. For concurrent dependencies, see [fail-fast behavior](/docs/guide/dependencies.md#fail-fast-dependencies). ## Clean up after a task {#cleanup-with-defer} Schedule a cleanup command with `defer`. Once Task reaches that entry in `cmds`, it registers the command to run when the task finishes, including if a later command fails. Deferred commands run in reverse order of registration. A `defer` that Task never reaches is not registered, for example when an earlier command or a dependency fails. In this example, Task removes `tmpdir/` after the work command finishes: ```yaml version: '3' tasks: default: cmds: - mkdir -p tmpdir/ - defer: rm -rf tmpdir/ - echo 'Work completed' > tmpdir/result.txt ``` Run `task`, then check that `tmpdir/` was removed. Replace the last command with `exit 1` to try the failure path: cleanup still runs and Task reports the failure. The directory name here is for the example; choose a task-specific path for real work. ### Reuse a cleanup task Use a deferred task call when several tasks share the same cleanup: ```yaml version: '3' tasks: default: cmds: - mkdir -p tmpdir/ - defer: { task: cleanup } - echo 'Work completed' > tmpdir/result.txt cleanup: rm -rf tmpdir/ ``` ### Inspect a failed command A special variable `.EXIT_CODE` is exposed when a command exited with a non-zero [exit code](/docs/reference/cli.md#exit-codes). You can check its presence to know if the task completed successfully or not: ```yaml version: '3' tasks: default: cmds: - defer: echo '{{if .EXIT_CODE}}Failed with {{.EXIT_CODE}}!{{else}}Success!{{end}}' - exit 1 ``` ## Continue after an error {#ignoring-command-errors} Set `ignore_error: true` on a command to continue after it fails: ```yaml version: '3' tasks: example: cmds: - cmd: exit 1 ignore_error: true - echo "Continuing after the failed command" ``` Without `ignore_error`, the `echo` command would not run. You can also set `ignore_error` on a task to apply it to that task's commands. The setting does not propagate to other tasks called through `cmds` or `deps`. --- --- url: /docs/guide/up-to-date.md description: >- Skip unchanged builds, check whether work is already done, and choose between source fingerprints and status commands. --- # Up-to-date checks {#skipping-work-that-is-up-to-date} A build should run when its inputs change or its output is missing. Declare those files with `sources` and `generates`, and Task can skip the commands on your next run. For other kinds of work, use `status` to check whether the result is already available. ## Skip unchanged builds {#by-fingerprinting-locally-generated-files-and-their-sources} For a Go application in `main.go`, declare the source file and the executable that the build produces: ```yaml version: '3' tasks: build: sources: [main.go] generates: ['app{{exeExt}}'] cmds: - go build -o app{{exeExt}} main.go ``` Run `task build` to create the executable. Run it again without changing `main.go`, and Task reports that `build` is up to date. Edit `main.go` or delete the executable, and the next call builds it again. The default method, `checksum`, compares the content of source files with a saved fingerprint. It checks that declared outputs exist; it does not compare their contents. Editing the executable alone will not trigger a rebuild. These checks skip the task's commands. Its [dependencies](/docs/guide/dependencies.md) still run first and perform their own up-to-date checks. ### Track the right files `sources` and `generates` accept files and glob patterns, relative to the task's [working directory](/docs/guide/defining-tasks.md#task-directory). Include every file that affects the result, such as source code, configuration, and dependency lockfiles. For a Go module with several source files, replace `sources` and `cmds` in the example above with: ```yaml sources: - '**/*.go' - go.mod - go.sum - exclude: '**/*_test.go' cmds: - go build -o app{{exeExt}} . ``` Patterns are evaluated in order. Put each `exclude` after the positive glob it narrows. Here, test files are excluded because this task builds the application; a test task should track them. Set `use_gitignore: true` at the root of the Taskfile to filter glob matches using `.gitignore` rules. A task can override that setting. Keep it disabled for tasks that need to track ignored files as inputs. `generates` is optional. A task such as a test suite can use `sources` alone to skip work when its inputs have not changed. `generates` alone does not provide a file fingerprint; use `status` if you only need to check that a result exists. ### Choose a check method | Method | How it checks sources | | -------------------- | -------------------------------------------------------------------- | | `checksum` (default) | Compares file contents with a saved fingerprint. | | `timestamp` | Compares modification times with outputs and Task's saved timestamp. | | `none` | Disables file-based skipping. | To use modification times for the build above, add `method: timestamp` to the task: ```yaml tasks: build: method: timestamp sources: [main.go] generates: ['app{{exeExt}}'] cmds: - go build -o app{{exeExt}} main.go ``` You can also set `method` at the root of the Taskfile to change the default for all tasks. A task's own setting takes precedence. With `timestamp`, touching a source can cause a rerun even if its content has not changed. Without `generates`, Task uses its saved timestamp for the comparison. Use `method: none` when `sources` should drive [watch mode](/docs/guide/watch.md) without making the task up to date. ## Check existing work {#using-programmatic-checks-to-indicate-a-task-is-up-to-date} Use `status` when a command can determine whether work is needed. Every check must exit with code `0` for Task to skip the commands. If any check fails, Task runs the commands: ```yaml version: '3' tasks: prepare: status: - test -d directory - test -f directory/file1.txt - test -f directory/file2.txt cmds: - mkdir -p directory - touch directory/file1.txt directory/file2.txt ``` Run `task prepare` twice: the first call creates the files, and the second skips the commands. Delete either file, and Task runs the commands again. Keep status checks fast and read-only: they may run each time the task is called. They can inspect files, installed tools, services, or remote artifacts. A failed check means "do the work", so the commands should handle partially completed work, as `mkdir -p` does above. If a failed check should stop execution with an error, use [preconditions](/docs/guide/conditional-execution.md#using-programmatic-checks-to-cancel-the-execution-of-a-task-and-its-dependencies). ### Combine files and status Use both `sources` and `status` when the result depends on local files but cannot be checked with `generates`. This Docker example tags the image with a checksum of its build inputs: ```yaml version: '3' tasks: image: sources: - Dockerfile - src/**/* status: - docker image inspect example/app:{{.CHECKSUM}} > /dev/null 2>&1 cmds: - docker build -t example/app:{{.CHECKSUM}} . ``` Task skips the build only when the sources are unchanged **and** the status check succeeds. A changed source produces a new tag; a missing image makes the check fail. Include any other files used by your Dockerfile in `sources`. You can also combine `sources`, `generates`, and `status`: every configured check must pass for Task to consider the work up to date. ### Use source values With `method: checksum`, `{{.CHECKSUM}}` contains the source fingerprint. With `method: timestamp`, `{{.TIMESTAMP}}` contains the latest source modification time. Both can be used in `cmds` and `status`; they describe the sources, not the generated files. `.TIMESTAMP` is a Go `time.Time` value. For example, `{{.TIMESTAMP.Unix}}` produces a Unix timestamp, and `{{.TIMESTAMP.Format "2006-01-02"}}` formats a date. ## Check or force a run To check whether tasks are up to date without running their commands: ```shell task --status build ``` You can pass multiple task names. The command exits with a non-zero [exit code](/docs/reference/cli.md#exit-codes) if any task is not up to date. To run a task even when its checks say the work is already done: ```shell task --force build ``` `-f` is the short form of `--force`. To rerun automatically when files change, use [Watch mode](/docs/guide/watch.md). ## Track different inputs With the checksum method, Task stores one source fingerprint per task label. If build variables affect the result, include them in the label and use separate output paths. For example, to build with different Go build tags: ```yaml version: '3' tasks: build: label: 'build-{{.MODE}}' sources: [main.go] generates: ['app-{{.MODE}}{{exeExt}}'] cmds: - go build -tags '{{.MODE}}' -o app-{{.MODE}}{{exeExt}} main.go ``` Run `task build MODE=debug`, then `task build MODE=release`. Each mode has its own executable and saved checksum, so a later `task build MODE=debug` can skip the build. A change to `main.go` makes both modes need a rebuild when next called. These saved checks apply across invocations. To limit repeated task calls within a single invocation, use [`run: once` or `run: when_changed`](/docs/guide/dependencies.md#repeated-calls). ## Store fingerprints Task stores its file fingerprints in `.task/` under the project directory. Usually, add this directory to `.gitignore`. For generated code committed to version control, you may choose to commit the corresponding checksum too. Set `TASK_TEMP_DIR` to store fingerprints elsewhere: ```shell export TASK_TEMP_DIR='~/.task' ``` Relative paths, such as `tmp/task`, are resolved from the project directory. Absolute paths and paths starting with `~` use a separate subdirectory for each project. --- --- url: /docs/guide/watch.md description: Re-run a task automatically whenever its sources change. --- # Watch mode Use `--watch` (or `-w`) to keep a task running and rerun it when its source files change. For a Go project: ```yaml version: '3' tasks: build: sources: - '**/*.go' - go.mod - go.sum cmds: - go build ./... ``` Run `task --watch build`, edit a Go file, and save it to trigger another build. Press Ctrl+C to stop watching. Watch mode detects changes; [up-to-date checks](/docs/guide/up-to-date.md) decide whether the task's commands need to run. Both use the same `sources`. ## Enable watching by default Set `watch: true` on a task if its normal CLI use should start a watcher: ```yaml version: '3' tasks: build: watch: true sources: ['**/*.go'] cmds: - go build ./... ``` Now `task build` starts watch mode. The `watch` setting applies when the task is called from the CLI; calls from another task through `cmds` or `deps` do not start a watcher. ## Combine rapid changes The default watch interval is 100 milliseconds. Increase it if saving several files produces duplicate events: ```shell task --watch --interval=500ms build ``` You can also set `interval: '500ms'` at the root of the Taskfile. The interval groups events close together so that Task reruns the task once for the batch. ## Watch long-running apps Builds and checks finish after each run. Servers keep running and need to stop before a replacement can take over their port. ::: warning There is a [known issue](https://github.com/go-task/task/issues/160) where watch mode may leave child processes running. Prefer building and running the executable directly over `go run`, which starts another process. ::: For applications that need dedicated live-reload behavior, consider tools such as [Air](https://github.com/air-verse/air/). Report Task-specific problems with a small reproducer in the [issue tracker](https://github.com/go-task/task/issues/new?template=bug_report.yml). --- --- url: /docs/guide/environment.md description: >- Set environment variables on a single task or on every task, and load them from `.env` files. --- # Environment variables Use `env` for values a program reads from its environment. In this example, the shell reads `GREETING` with `$GREETING`: ```yaml version: '3' tasks: greet: env: GREETING: Hello, World! cmds: - echo "$GREETING" ``` Run `task greet` to print the greeting. Task uses the same shell syntax on supported platforms, including Windows. For values used in Task templates, use [Variables](/docs/guide/variables.md#env-and-vars-are-not-the-same-thing). ## Set values for commands {#task} Put `env` on a task to configure its commands, or at the root to provide values for every task: ```yaml version: '3' env: GREETING: Hello, World! tasks: greet: cmds: - echo "$GREETING" greet:local: env: GREETING: Hello from this task! cmds: - echo "$GREETING" ``` With no `GREETING` in the process environment, `task greet` uses the root value and `task greet:local` uses the task's value. By default, a variable already present in the process environment takes precedence over Taskfile `env` values. If the result differs from the example, check the environment used to launch Task. The [Env Precedence experiment](/docs/experiments/env-precedence.md) changes this rule. `env` values support templates and dynamic `sh` commands. For example, an entry `BUILD_MODE: '{{.MODE}}'` exports a Task variable to a command's environment. A `vars` entry alone is not exported to commands in `cmds`. ## Load values from files {#env-files} Use `dotenv` to load values from files instead of repeating them in the Taskfile. Create these two files in your project: ::: code-group ```dotenv [.env] GREETING=Hello from .env! ``` ```yaml [Taskfile.yml] version: '3' dotenv: ['.env'] tasks: greet: cmds: - echo "$GREETING" ``` ::: Run `task greet` to use the value from `.env`, assuming the process environment does not already set `GREETING`. ### Choose file precedence When several dotenv files define a name, the **first file wins**. Put local overrides before shared defaults: ```yaml version: '3' vars: ENV: development tasks: greet: dotenv: - .env.local - .env.{{.ENV}} - .env cmds: - echo "$GREETING" ``` `task greet ENV=testing` selects `.env.testing` for the middle entry. This example puts `dotenv` on the task so it can use call variables; root-level dotenv files are loaded before those variables are available. Missing dotenv files are skipped. Keep files containing local credentials out of version control. ### Load values for one task Put `dotenv` on a task when only that task needs the file. Explicit task-level `env` values take precedence over values loaded from dotenv files: ```yaml version: '3' tasks: greet: dotenv: ['.env'] env: GREETING: Hello from the task! cmds: - echo "$GREETING" ``` The process environment still takes precedence by default. Dotenv paths can contain templates, as in `'{{.HOME}}/.env'`. The `dotenv` key is not supported inside included Taskfiles. Load shared values in the entrypoint Taskfile instead. See [Including Taskfiles](/docs/guide/includes.md). --- --- url: /docs/guide/platforms.md description: >- Restrict tasks and commands to an operating system or architecture, and set shell options with `set` and `shopt`. --- # Platforms and shells {#platform-specific-behaviour} Task runs on Linux, macOS, and Windows. A portable Taskfile also needs commands and tools that work on the machines where you run it. Use `platforms` to select OS-specific steps, and keep shared work in the same task. ## Select platform commands {#platform-specific-tasks-and-commands} Set `platforms` on an individual command when only part of a task differs by platform: ```yaml version: '3' tasks: setup: cmds: - cmd: echo 'Preparing Windows tools' platforms: [windows] - cmd: echo 'Preparing Unix tools' platforms: [linux, darwin] - echo 'Preparing shared files' ``` Run `task setup`. A command whose platform does not match is skipped without an error; the shared command runs on every platform. ### Restrict a whole task Put `platforms` on the task when none of its commands should run elsewhere: ```yaml version: '3' tasks: setup:windows: platforms: [windows/amd64] cmds: - echo 'Preparing tools for Windows on amd64' ``` | Restriction | Matches | | ------------------------- | ---------------------------------------------- | | `[windows]` | Windows on any architecture | | `[amd64]` | Any supported OS on amd64 | | `[windows/amd64]` | Windows on amd64 | | `[windows/amd64, darwin]` | Windows on amd64, or macOS on any architecture | OS and architecture names use Go's `GOOS` and `GOARCH` values. These restrictions check the machine running Task; they do not configure a compiler's target. ### Share platform values The `OS` and `ARCH` template functions expose the current platform. Use them to select [OS-specific Taskfiles](/docs/guide/includes.md#os-specific-taskfiles) or construct arguments. Use `exeExt` for executable names that need `.exe` on Windows: ```yaml version: '3' tasks: build: cmds: - go build -o app{{exeExt}} main.go ``` ## Understand command shells Task runs commands through its embedded shell interpreter. It does not start your interactive shell or load that shell's startup configuration. Use [Taskfile `env` and `dotenv`](/docs/guide/environment.md) for values commands need. Each `cmds` entry gets a separate shell context. A `cd` or `export` in one entry does not carry over to the next. Set `dir` and `env` on the task instead: ```yaml version: '3' tasks: inspect: dir: src env: BUILD_MODE: development cmds: - pwd - echo "$BUILD_MODE" ``` If several commands need to share shell state, put them in one multiline `cmd` or a script. Programs such as `go`, `npm`, or `docker` must still be installed. Invoke `bash`, `pwsh`, or `cmd /c` explicitly when a command needs that particular shell, and restrict it to machines where it is available. ## Configure shell options {#set-and-shopt} Use `set` for POSIX shell options and `shopt` for supported Bash-style options. For example, `pipefail` makes a pipeline fail when an earlier command fails, and `globstar` enables recursive `**` globs in shell commands: ```yaml version: '3' set: [pipefail] shopt: [globstar] tasks: list: cmds: - echo **/*.go ``` These options can be set at the root, task, or command level. The shell interpreter supports a subset of shell options; see the [shell options reference](/docs/reference/schema.md#shell-options). `shopt` controls shell expansion inside commands. Globs in `sources` and `generates` use [Task's file matching](/docs/guide/up-to-date.md#track-the-right-files) and do not require `globstar`. --- --- url: /docs/guide/output.md description: >- Choose an output mode, hide command echoes, and display colors and failure annotations in CI. --- # Output and logging When several tasks run together, their output can be hard to follow. Choose an output mode to identify the source of each line or keep each command's output together. Use `silent` separately to hide the command text Task logs. ## Choose an output mode {#output-syntax} | What do you need? | Mode | | ------------------------------------ | ----------------------- | | Live output without extra formatting | `interleaved` (default) | | Live output labeled by task | `prefixed` | | Each command's output in one block | `group` | ### Identify parallel output Set `output: prefixed` at the root of the Taskfile: ```yaml version: '3' output: prefixed tasks: check: deps: [lint, test] lint: cmds: - echo 'Lint passed' test: cmds: - echo 'Tests passed' ``` Run `task --silent check`. The command output identifies each task: ```text [lint] Lint passed [test] Tests passed ``` The lines may appear in either order because the tasks run concurrently. You can override the output mode for one invocation with `task --output group check` (or `task -o group check`). Use a task's `prefix` to distinguish repeated calls with different inputs: ```yaml version: '3' output: prefixed tasks: default: deps: - task: check vars: { SERVICE: api } - task: check vars: { SERVICE: worker } check: prefix: 'check-{{.SERVICE}}' silent: true cmds: - echo 'Passed' ``` The output uses `[check-api]` and `[check-worker]` prefixes. ### Group command output Set `output: group` to buffer each command's stdout and stderr until that command finishes. Long-running commands do not provide live feedback in this mode. Groups are per command, so blocks from different tasks can still alternate between commands. To show buffered output only when a command fails: ```yaml version: '3' silent: true output: group: error_only: true tasks: passes: echo 'Everything passed' fails: echo 'Failure details' && exit 1 ``` `task passes` prints nothing. `task fails` prints `Failure details` and the failure message. Hiding successful output does not change exit codes. ## Hide command echoes {#silent-mode} Use `--silent` (or `-s`) to hide the command text Task logs before execution. The command's own output remains visible: ```yaml version: '3' tasks: greet: cmds: - echo 'Hello, World!' ``` `task greet` shows the command and its output. `task --silent greet` prints only `Hello, World!`. To make this permanent for selected commands: ```yaml version: '3' tasks: greet: cmds: - cmd: echo 'Hello, World!' silent: true ``` You can also set `silent: true` on a task for all its commands, or at the root of the Taskfile for all tasks. To suppress a command's stdout, use shell redirection instead: ```yaml version: '3' tasks: quiet: cmds: - echo 'This output is discarded' > /dev/null ``` Stderr remains visible unless you redirect it too. Silent mode is not a substitute for [secret masking](/docs/guide/secret-variables.md). ## Make CI logs readable {#ci-integration} ### Fold command logs Use `output.group.begin` and `end` to add the markers a CI system uses for collapsible logs. For GitHub Actions: ```yaml version: '3' output: group: begin: '::group::{{.TASK}}' end: '::endgroup::' tasks: default: silent: true cmds: - echo 'Hello, World!' ``` Running `task` produces: ```text ::group::default Hello, World! ::endgroup:: ``` These markers follow [GitHub Actions' grouping format](https://docs.github.com/en/actions/learn-github-actions/workflow-commands-for-github-actions#grouping-log-lines). Other providers, such as [Azure Pipelines](https://docs.microsoft.com/en-us/azure/devops/pipelines/scripts/logging-commands?expand=1\&view=azure-devops\&tabs=bash#formatting-commands), use different markers. ### Control colors {#colored-output} Task enables its colored output when `CI=true`, which most CI providers set automatically. Use `FORCE_COLOR=1` to force colors or `NO_COLOR=1` to disable them. Programs launched by Task may have their own color settings. ### Surface failures {#error-annotations} When `GITHUB_ACTIONS=true`, Task automatically emits an annotation when a task fails, so the workflow can surface the error: ```text ::error title=Task 'build' failed::exit status 1 ``` No Taskfile configuration is required for these annotations. ::: tip Related guide To continue after a selected failure, use `ignore_error`. See [Errors and cleanup](/docs/guide/errors-and-cleanup.md#ignoring-command-errors) for its scope and examples. ::: --- --- url: /docs/guide/includes.md description: >- Reuse tasks across projects with `includes`, covering namespaces, optional and internal includes, flattening, and per-include variables. --- # Including Taskfiles {#including-other-taskfiles} Use `includes` to split a project into smaller Taskfiles or reuse tasks across projects. Give each included file a namespace, then call its tasks through that name. Start with a Markdown file at `docs/README.md`, then create these two Taskfiles: ::: code-group ```yaml [Taskfile.yml] version: '3' includes: docs: taskfile: ./docs/Taskfile.yml dir: ./docs ``` ```yaml [docs/Taskfile.yml] version: '3' tasks: build: cmds: - mkdir -p build - cp README.md build/index.md ``` ::: Run `task docs:build` to copy `docs/README.md` to `docs/build/index.md`. The `docs` namespace comes from the include key, not the directory name. You can also call `docs:build` from another task's `cmds` or `deps`. An include path can point to a file or a directory. With `docs: ./docs`, Task looks for a supported Taskfile name in that directory. Paths are relative to the including file, and included Taskfiles must use the same schema version. ## Set the working directory {#directory-of-included-taskfile} The `dir: ./docs` setting makes the included task read `docs/README.md` and write into `docs/build/`, even when you run `task docs:build` from the project root. Without `dir`, included tasks use the including Taskfile's execution directory. The same commands would look for `README.md` and create `build/` at the project root. A Taskfile's location alone does not set its execution directory. Individual tasks can also specify their own [working directory](/docs/guide/defining-tasks.md#task-directory). ## Configure reusable tasks {#vars-of-included-taskfiles} Use `vars` on an include to configure reusable tasks. This example includes the same file twice with different image names: ::: code-group ```yaml [Taskfile.yml] version: '3' includes: backend: taskfile: ./taskfiles/Docker.yml vars: DOCKER_IMAGE: backend_image frontend: taskfile: ./taskfiles/Docker.yml vars: DOCKER_IMAGE: frontend_image ``` ```yaml [taskfiles/Docker.yml] version: '3' vars: DOCKER_IMAGE: '{{.DOCKER_IMAGE | default "app"}}' tasks: build: cmds: - echo 'Building {{.DOCKER_IMAGE}}' ``` ::: `task backend:build` prints `Building backend_image`; `task frontend:build` prints `Building frontend_image`. The template default in the included file is deliberate. A constant value in that file's `vars` would override the value supplied by the include. See [variable precedence](/docs/guide/variables.md#resolution-order) for the full rules. ## Shorten namespace names {#namespace-aliases} Add namespace `aliases` to shorten frequently used task names: ```yaml version: '3' includes: documentation: taskfile: ./docs/Taskfile.yml dir: ./docs aliases: [docs] ``` Both `task documentation:build` and `task docs:build` call the same task. Namespace aliases can be combined with [task aliases](/docs/guide/defining-tasks.md#task-aliases). ## Allow a missing file {#optional-includes} Use `optional: true` when a missing local Taskfile should not prevent the rest of the project from running: ```yaml version: '3' includes: local: taskfile: ./Taskfile.local.yml optional: true tasks: greet: cmds: - echo 'Hello, World!' ``` `task greet` works even without `Taskfile.local.yml`. Optional includes do not make tasks from a missing file available. ## Keep included tasks internal {#internal-includes} Use `internal: true` when all tasks in an included file are helpers for your public tasks: ```yaml version: '3' includes: docs: taskfile: ./docs/Taskfile.yml dir: ./docs internal: true tasks: build: cmds: - task: docs:build ``` With the `docs/Taskfile.yml` from the first example, `task build` works, but `task docs:build` cannot be called directly. Internal tasks are also hidden from task listings. ## Remove the namespace {#flatten-includes} Use `flatten: true` to expose included tasks without a namespace: ```yaml version: '3' includes: docs: taskfile: ./docs/Taskfile.yml dir: ./docs flatten: true ``` With the first example's included file, call `task build` instead of `task docs:build`. Calls within the Taskfile can also use the flattened name. If the root Taskfile already defines `build`, this inclusion fails with a name collision. Keep the namespace or exclude the conflicting included task. ## Exclude selected tasks {#exclude-tasks-from-being-included} Use `excludes` to remove selected tasks from an include. Names match exactly; append `:*` to exclude a namespace: ::: code-group ```yaml [Taskfile.yml] version: '3' includes: tools: taskfile: ./Tools.yml excludes: [debug, 'internal:*'] ``` ```yaml [Tools.yml] version: '3' tasks: check: echo 'Checking project' debug: echo 'Debug details' internal:setup: echo 'Internal setup' ``` ::: `task tools:check` works. `task tools:debug` and `task tools:internal:setup` fail because those tasks were excluded. `excludes` also works with `flatten`. ## Choose a file by OS {#os-specific-taskfiles} Use the `OS` template function to select a file for the current platform: ```yaml version: '3' includes: build: ./Taskfile_{{OS}}.yml ``` Provide files such as `Taskfile_linux.yml`, `Taskfile_darwin.yml`, and `Taskfile_windows.yml` for the platforms you support. If only a few commands differ, [platform restrictions](/docs/guide/platforms.md#platform-specific-tasks-and-commands) may be easier to maintain. ::: tip Related guide Replace the local path with an HTTP or Git location to reuse tasks maintained outside the project. Only run remote Taskfiles from sources you trust; they execute commands on your machine. See [Remote Taskfiles](/docs/remote-taskfiles.md) for a complete example, checksum pinning, and offline use. ::: --- --- url: /docs/remote-taskfiles.md description: Guide to loading and securely using Taskfiles from HTTP and Git sources --- # Remote Taskfiles Use a remote Taskfile to share tasks across projects without copying the file into each repository. You can call it directly or include it under a namespace. ::: danger Only run remote Taskfiles from sources you trust. Their commands execute on your machine, just like commands from a local Taskfile. ::: The examples use Task's public sample, whose `hello` task prints `Hello Task!`. On first use, Task asks you to trust the downloaded file. Review it before accepting; see [trust and checksums](/docs/remote-taskfiles.md#security) for subsequent changes and CI. ## Run a remote Taskfile {#specifying-a-remote-entrypoint} Pass the remote location to `--taskfile` (or `-t`). With no task name, Task runs the remote file's `default` task. Choose a transport below: ::: code-group ```shell [HTTP/HTTPS] $ task --taskfile https://raw.githubusercontent.com/go-task/task/main/website/src/public/Taskfile.yml task: [hello] echo "Hello Task!" Hello Task! ``` ```shell [Git over HTTP] $ task --taskfile 'https://github.com/go-task/task.git//website/src/public/Taskfile.yml?ref=main' task: [hello] echo "Hello Task!" Hello Task! ``` ```shell [Git over SSH] $ task --taskfile 'git@github.com/go-task/task.git//website/src/public/Taskfile.yml?ref=main' task: [hello] echo "Hello Task!" Hello Task! ``` ::: ## Include shared tasks {#including-remote-taskfiles} Set the include location to a remote URL instead of a local path. The tasks become available under the namespace you choose, just like [local includes](/docs/guide/includes.md): ::: code-group ```yaml [HTTP/HTTPS] version: '3' includes: shared: https://raw.githubusercontent.com/go-task/task/main/website/src/public/Taskfile.yml ``` ```yaml [Git over HTTP] version: '3' includes: shared: https://github.com/go-task/task.git//website/src/public/Taskfile.yml?ref=main ``` ```yaml [Git over SSH] version: '3' includes: shared: git@github.com/go-task/task.git//website/src/public/Taskfile.yml?ref=main ``` ::: ```shell $ task shared:hello task: [hello] echo "Hello Task!" Hello Task! ``` ### Supply authentication {#authenticating-using-environment-variables} The Taskfile location is processed by the templating system, so you can reference environment variables in your URL if you need to add authentication. For example: ```yaml version: '3' includes: shared: https://{{.TOKEN}}@raw.githubusercontent.com/my-org/my-repo/main/Taskfile.yml ``` Prefer the [`remote.headers`](/docs/reference/config.md#remote-headers) configuration option when the server accepts a header. A credential in the URL ends up in error messages and in the confirmation prompt, and the include can no longer be committed as-is. ## Choose a source {#node-types} ### HTTP/HTTPS `https://raw.githubusercontent.com/go-task/task/main/website/src/public/Taskfile.yml` Use a direct URL when the server exposes the Taskfile as plain text. Task downloads the file from that URL. The file must be a valid Taskfile and can be of any name. If a file is not found at the specified URL, Task will append each of the supported file names in turn until it finds a valid file. If it still does not find a valid Taskfile, an error is returned. ### Git over HTTP `https://github.com/go-task/task.git//website/src/public/Taskfile.yml?ref=main` Use a Git URL to select a file from a repository over HTTP/HTTPS. The first part of the URL is the base URL of the Git repository. This is the same URL that you would use to clone the repo over HTTP. * You can optionally add the path to the Taskfile in the repository by appending `//` to the URL. * You can also optionally specify a branch or tag to use by appending `?ref=` to the end of the URL. If you omit a reference, the default branch will be used. ### Git over SSH `git@github.com/go-task/task.git//website/src/public/Taskfile.yml?ref=main` Use an SSH Git URL when repository access depends on your SSH credentials. The first part of the URL is the user and base URL of the Git repository. This is the same URL that you would use to clone the repo over SSH. To use Git over SSH, you need to make sure that your SSH agent has your private SSH keys added so that they can be used during authentication. * You can optionally add the path to the Taskfile in the repository by appending `//` to the URL. * You can also optionally specify a branch or tag to use by appending `?ref=` to the end of the URL. If you omit a reference, the default branch will be used. ## Locate files and directories {#special-variables} The file-path [special variables](/docs/reference/templating.md#file-paths) behave differently when a Taskfile is loaded from a remote source, because there is no local file or directory that corresponds 1:1 to the Taskfile: | Variable | Value when loaded remotely | | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | `TASKFILE` / `ROOT_TASKFILE` | The original URL, unchanged | | `TASKFILE_DIR` / `ROOT_DIR` | Empty string, a directory variable cannot point to a URL | | `TASK_DIR` | Resolved against `USER_WORKING_DIR` (relative `dir:` → joined with `USER_WORKING_DIR`, empty `dir:` → `USER_WORKING_DIR`, absolute `dir:` → kept as-is) | If a remote Taskfile includes a local Taskfile (or vice-versa), each variable reflects the source of the Taskfile it refers to. ## Review trust and changes {#security} ### Review changed files {#automatic-checksums} Running commands from sources that you do not control is always a potential security risk. For this reason, we have added some automatic checks when using remote Taskfiles: 1. When running a task from a remote Taskfile for the first time, Task will print a warning to the console asking you to check that you are sure that you trust the source of the Taskfile. If you do not accept the prompt, then Task will exit with code `104` (not trusted) and nothing will run. If you accept the prompt, the remote Taskfile will run and further calls to the remote Taskfile will not prompt you again. 2. Whenever you run a remote Taskfile, Task will create and store a checksum of the file that you are running. If the checksum changes, then Task will print another warning to the console to inform you that the contents of the remote file has changed. If you do not accept the prompt, then Task will exit with code `104` (not trusted) and nothing will run. If you accept the prompt, the checksum will be updated and the remote Taskfile will run. Sometimes you need to run Task in an environment that does not have an interactive terminal, so you are not able to accept a prompt. In these cases you are able to tell task to accept these prompts automatically by using the `--yes` flag or the `--trusted-hosts` flag. The `--trusted-hosts` flag allows you to specify trusted hosts for remote Taskfiles, while `--yes` applies to all prompts in Task. You can also configure trusted hosts in your [taskrc configuration](/docs/reference/config.md#remote-trusted-hosts) using `remote.trusted-hosts`. Before enabling automatic trust, you should: 1. Be sure that you trust the source and contents of the remote Taskfile. 2. Consider using a pinned version of the remote Taskfile (e.g. A link containing a commit hash) to prevent Task from automatically accepting a prompt that says a remote Taskfile has changed. ### Pin reviewed contents {#manual-checksum-pinning} Alternatively, if you expect the contents of your remote files to be a constant value, you can pin the checksum of the included file instead: ```yaml version: '3' includes: included: taskfile: https://raw.githubusercontent.com/go-task/task/main/website/src/public/Taskfile.yml checksum: '' ``` Replace the placeholder with the SHA-256 checksum of the file you reviewed. A pinned checksum disables the automatic checksum prompts discussed above. However, if the checksums do not match, Task will exit immediately with an error. When setting this up for the first time, you may not know the correct value of the checksum. There are a couple of ways you can obtain this: 1. Add the include normally without the `checksum` key. The first time you run the included Taskfile, a `.task/remote` temporary directory is created. Find the correct set of files for your included Taskfile and open the file that ends with `.checksum`. You can copy the contents of this file and paste it into the `checksum` key of your include. This method is safest as it allows you to inspect the downloaded Taskfile before you pin it. 2. Alternatively, add the include with a temporary random value in the `checksum` key. When you try to run the Taskfile, you will get an error that will report the incorrect expected checksum and the actual checksum. You can copy the actual checksum and replace your temporary random value. ### Use TLS {#tls} Task currently supports both `http` and `https` URLs. However, the `http` requests will not execute by default unless you run the task with the `--insecure` flag. This is to protect you from accidentally running a remote Taskfile that is downloaded via an unencrypted connection. Sources that are not protected by TLS are vulnerable to man-in-the-middle attacks and should be avoided unless you know what you are doing. #### Add certificates {#custom-certificates} If your remote Taskfiles are hosted on a server that uses a custom CA certificate (e.g., a corporate internal server), you can specify the CA certificate using the `--cacert` flag: ```shell task --taskfile https://internal.example.com/Taskfile.yml --cacert /path/to/ca.crt ``` For servers that require client certificate authentication (mTLS), you can provide a client certificate and key: ```shell task --taskfile https://secure.example.com/Taskfile.yml \ --cert /path/to/client.crt \ --cert-key /path/to/client.key ``` ::: warning Encrypted private keys are not currently supported. If your key is encrypted, you must decrypt it first: ```shell openssl rsa -in encrypted.key -out decrypted.key ``` ::: These options can also be configured in the [configuration file](/docs/remote-taskfiles.md#configuration). ## Reuse cached files {#caching-running-offline} | What do you need? | Option | | -------------------------------------------- | --------------- | | Reuse a downloaded copy for one hour | `--expiry 1h` | | Use an existing cache without network access | `--offline` | | Fetch a fresh copy now | `--download` | | Remove cached remote files | `--clear-cache` | For example, run `task --offline shared:hello` after downloading and trusting the include above. Offline mode needs an existing cached copy. Whenever you run a remote Taskfile, the latest copy will be downloaded from the internet and cached locally. This cached file will be used for all future invocations of the Taskfile until the cache expires. Once it expires, Task will download the latest copy of the file and update the cache. By default, the cache is set to expire immediately. This means that Task will always fetch the latest version. However, the cache expiry duration can be modified by setting the `--expiry` flag. If for any reason you lose access to the internet or you are running Task in offline mode (via the `--offline` flag or `TASK_REMOTE_OFFLINE` environment variable), Task will run any available cached files *even if they are expired*. An uncached remote file cannot run offline. By default, Task will timeout requests to download remote files after 10 seconds and look for a cached copy instead. This timeout can be configured by setting the `--timeout` flag and specifying a duration. For example, `--timeout 5s` will set the timeout to 5 seconds. By default, the cache is stored in the Task temp directory (`.task`). You can override the location of the cache by using the `--remote-cache-dir` flag, the `remote.cache-dir` option in your [configuration file](/docs/reference/config.md#remote-cache-dir), or the `TASK_REMOTE_CACHE_DIR` environment variable. This way, you can share the cache between different projects. You can force Task to ignore the cache and download the latest version by using the `--download` flag. You can use the `--clear-cache` flag to clear all cached remote files. ## Set remote defaults {#configuration} It is possible to configure the default behavior of remote Taskfiles using configuration. Check out the following references for more information on how to set up this configuration using flags, environment variables or a configuration file: * [CLI flags](/docs/reference/cli.md#remote) * [Environment Variables](/docs/reference/environment.md#remote-taskfile-variables) * [Config File](/docs/reference/config.md#remote) --- --- url: /docs/reference/schema.md description: A reference for the Taskfile schema --- # Taskfile Schema Reference This page documents all available properties and types for the Taskfile schema version 3, based on the [official JSON schema](https://taskfile.dev/schema.json). ## Root Schema The root Taskfile schema defines the structure of your main `Taskfile.yml`. ### `version` * **Type**: `string` or `number` * **Required**: Yes * **Valid values**: `"3"`, `3`, or any valid semver string * **Description**: Version of the Taskfile schema ```yaml version: '3' ``` ### `output` * **Type**: `string` or `object` * **Default**: `interleaved` * **Options**: `interleaved`, `group`, `prefixed` * **Description**: Controls how task output is displayed ```yaml # Simple string format output: group # Advanced object format output: group: begin: "::group::{{.TASK}}" end: "::endgroup::" error_only: false ``` ### `method` * **Type**: `string` * **Default**: `checksum` * **Options**: `checksum`, `timestamp`, `none` * **Description**: Default method for checking if tasks are up-to-date ```yaml method: timestamp ``` ### [`includes`](/docs/reference/schema.md#include) * **Type**: `map[string]Include` * **Description**: Include other Taskfiles ```yaml includes: # Simple string format docs: ./Taskfile.yml # Full object format backend: taskfile: ./backend dir: ./backend optional: false flatten: false internal: false aliases: [api] excludes: [internal-task] vars: SERVICE_NAME: backend checksum: abc123... ``` ### [`vars`](/docs/reference/schema.md#variable) * **Type**: `map[string]Variable` * **Description**: Global variables available to all tasks ```yaml vars: # Simple values APP_NAME: myapp VERSION: 1.0.0 DEBUG: true PORT: 8080 FEATURES: [auth, logging] # Dynamic variables COMMIT_HASH: sh: git rev-parse HEAD # Variable references BUILD_VERSION: ref: .VERSION # Map variables CONFIG: map: database: postgres cache: redis ``` ### `env` * **Type**: `map[string]Variable` * **Description**: Global environment variables ```yaml env: NODE_ENV: production DATABASE_URL: sh: echo $DATABASE_URL ``` ### [`tasks`](/docs/reference/schema.md#task) * **Type**: `map[string]Task` * **Description**: Task definitions ```yaml tasks: # Simple string format hello: echo "Hello World" # Array format build: - go mod tidy - go build ./... # Full object format deploy: desc: Deploy the application cmds: - ./scripts/deploy.sh ``` ### `silent` * **Type**: `bool` * **Default**: `false` * **Description**: Suppress task name and command output by default ```yaml silent: true ``` ### `dotenv` * **Type**: `[]string` * **Description**: Load environment variables from .env files. When the same variable is defined in multiple files, the first file in the list takes precedence. ```yaml dotenv: - .env.local # Highest priority - .env # Lowest priority ``` ### `run` * **Type**: `string` * **Default**: `always` * **Options**: `always`, `once`, `when_changed` * **Description**: Default execution behavior for tasks ```yaml run: once ``` ### `interval` * **Type**: `string` * **Default**: `100ms` * **Pattern**: `^[0-9]+(?:m|s|ms)$` * **Description**: Watch interval for file changes ```yaml interval: 1s ``` ### `set` * **Type**: `[]string` * **Options**: `allexport`, `a`, `errexit`, `e`, `noexec`, `n`, `noglob`, `f`, `nounset`, `u`, `xtrace`, `x`, `pipefail` * **Description**: POSIX shell options for all commands ```yaml set: [errexit, nounset, pipefail] ``` ### `shopt` * **Type**: `[]string` * **Options**: `expand_aliases`, `globstar`, `nullglob` * **Description**: Bash shell options for all commands ```yaml shopt: [globstar] ``` ### `use_gitignore` * **Type**: `bool` * **Default**: `false` * **Description**: Exclude files matched by `.gitignore` rules when resolving `sources` and `generates` globs for all tasks. Can be overridden per task. ```yaml use_gitignore: true ``` ## Include Configuration for including external Taskfiles. ### `taskfile` * **Type**: `string` * **Required**: Yes * **Description**: Path to the Taskfile or directory to include ```yaml includes: backend: ./backend/Taskfile.yml # Shorthand for above frontend: ./frontend ``` ### `dir` * **Type**: `string` * **Description**: Working directory for included tasks ```yaml includes: api: taskfile: ./api dir: ./api ``` ### `optional` * **Type**: `bool` * **Default**: `false` * **Description**: Don't error if the included file doesn't exist ```yaml includes: optional-tasks: taskfile: ./optional.yml optional: true ``` ### `flatten` * **Type**: `bool` * **Default**: `false` * **Description**: Include tasks without namespace prefix ```yaml includes: common: taskfile: ./common.yml flatten: true ``` ### `internal` * **Type**: `bool` * **Default**: `false` * **Description**: Hide included tasks from command line and `--list` ```yaml includes: internal: taskfile: ./internal.yml internal: true [...] tasks: example: desc: using an internal task cmds: - task: internal:default ``` ### `aliases` * **Type**: `[]string` * **Description**: Alternative names for the namespace ```yaml includes: database: taskfile: ./db.yml aliases: [db, data] ``` ### `excludes` * **Type**: `[]string` * **Description**: Task names or namespace patterns ending in `:*` to exclude from inclusion ```yaml includes: shared: taskfile: ./shared.yml excludes: [internal-setup, 'debug:*', 'experimental:*'] ``` ### `vars` * **Type**: `map[string]Variable` * **Description**: Variables to pass to the included Taskfile ```yaml includes: deploy: taskfile: ./deploy.yml vars: ENVIRONMENT: production ``` ### `checksum` * **Type**: `string` * **Description**: Expected checksum of the included file ```yaml includes: remote: taskfile: https://example.com/tasks.yml checksum: c153e97e0b3a998a7ed2e61064c6ddaddd0de0c525feefd6bba8569827d8efe9 ``` ## Variable Variables support multiple types and can be static values, dynamic commands, references, or maps. ### Static Variables ```yaml vars: # String APP_NAME: myapp # Number PORT: 8080 # Boolean DEBUG: true # Array FEATURES: [auth, logging, metrics] # Null OPTIONAL_VAR: null ``` ### Dynamic Variables (`sh`) ```yaml vars: COMMIT_HASH: sh: git rev-parse HEAD BUILD_TIME: sh: date -u +"%Y-%m-%dT%H:%M:%SZ" ``` ### Variable References (`ref`) ```yaml vars: BASE_VERSION: 1.0.0 FULL_VERSION: ref: .BASE_VERSION ``` ### Map Variables (`map`) ```yaml vars: CONFIG: map: database: host: localhost port: 5432 cache: type: redis ttl: 3600 ``` ### Secret Variables (`secret`) Mark variables as secret to mask their values in command logs. ```yaml vars: API_KEY: value: 'sk-1234567890abcdef' secret: true # This variable will be masked in logs DB_PASSWORD: sh: vault read -field=password secret/db secret: true # Works with dynamic variables too ``` When a variable is marked as `secret: true`, Task will replace its value with `*****` in command logs. The actual command execution still receives the real value. ::: info For complete documentation on secret variables, including security considerations and best practices, see the [Secret variables](/docs/guide/secret-variables.md) section in the Guide. ::: ### Variable Ordering Variables can reference previously defined variables: ```yaml vars: GREETING: Hello TARGET: World MESSAGE: '{{.GREETING}} {{.TARGET}}!' ``` ## Task Individual task configuration with multiple syntax options. ### Simple Task Formats ```yaml tasks: # String command hello: echo "Hello World" # Array of commands build: - go mod tidy - go build ./... # Object with cmd shorthand test: cmd: go test ./... ``` ### Task Properties #### `cmds` * **Type**: `[]Command` * **Description**: Commands to execute ```yaml tasks: build: cmds: - go build ./... - echo "Build complete" ``` #### `cmd` * **Type**: `string` * **Description**: Single command (alternative to `cmds`) ```yaml tasks: test: cmd: go test ./... ``` #### `deps` * **Type**: `[]Dependency` * **Description**: Tasks to run before this task ```yaml tasks: # Simple dependencies deploy: deps: [build, test] cmds: - ./deploy.sh # Dependencies with variables advanced-deploy: deps: - task: build vars: ENVIRONMENT: production - task: test vars: COVERAGE: true cmds: - ./deploy.sh # Silent dependencies main: deps: - task: setup silent: true cmds: - echo "Main task" # Loop dependencies test-all: deps: - for: [unit, integration, e2e] task: test vars: TEST_TYPE: '{{.ITEM}}' cmds: - echo "All tests completed" ``` #### `desc` * **Type**: `string` * **Description**: Short description shown in `--list` ```yaml tasks: test: desc: Run unit tests cmds: - go test ./... ``` #### `summary` * **Type**: `string` * **Description**: Detailed description shown in `--summary` ```yaml tasks: deploy: desc: Deploy to production summary: | Deploy the application to production environment. This includes building, testing, and uploading artifacts. ``` #### `prompt` * **Type**: `string` or `[]string` * **Description**: Prompts shown before task execution ```yaml tasks: # Single prompt deploy: prompt: "Deploy to production?" cmds: - ./deploy.sh # Multiple prompts deploy-multi: prompt: - "Are you sure?" - "This will affect live users!" cmds: - ./deploy.sh ``` #### `aliases` * **Type**: `[]string` * **Description**: Alternative names for the task ```yaml tasks: build: aliases: [compile, make] cmds: - go build ./... ``` #### `method` * **Type**: `string` * **Default**: `checksum` * **Options**: `checksum`, `timestamp`, `none` * **Description**: Method for checking if the task is up-to-date. Refer to the `method` root property for details. ```yaml tasks: build: sources: - go.mod method: timestamp ``` #### `sources` * **Type**: `[]string` or `[]Glob` * **Description**: Source files to monitor for changes ```yaml tasks: build: sources: - '**/*.go' - go.mod # With exclusions - exclude: '**/*_test.go' cmds: - go build ./... ``` #### `generates` * **Type**: `[]string` or `[]Glob` * **Description**: Files generated by this task ```yaml tasks: build: sources: ['**/*.go'] generates: - './app' - exclude: '*.debug' cmds: - go build -o app ./cmd ``` #### `use_gitignore` * **Type**: `bool` * **Default**: `false` * **Description**: Exclude files matched by `.gitignore` rules when resolving this task's `sources` and `generates` globs. Overrides the root-level `use_gitignore` setting. ```yaml tasks: build: use_gitignore: true sources: - '**/*.go' cmds: - go build ./... ``` #### `status` * **Type**: `[]string` * **Description**: Commands to check if task should run ```yaml tasks: install-deps: status: - test -f node_modules/.installed cmds: - npm install - touch node_modules/.installed ``` #### `preconditions` * **Type**: `[]Precondition` * **Description**: Conditions that must be met before running ```yaml tasks: # Simple precondition (shorthand) build: preconditions: - test -d ./src cmds: - go build ./... # Preconditions with custom messages deploy: preconditions: - sh: test -n "$API_KEY" msg: 'API_KEY environment variable is required' - sh: test -f ./app msg: "Application binary not found. Run 'task build' first." cmds: - ./deploy.sh ``` #### `if` * **Type**: `string` * **Description**: Shell command to conditionally execute the task. If the command exits with a non-zero code, the task is skipped (not failed). ```yaml tasks: # Task only runs in CI environment deploy: if: '[ "$CI" = "true" ]' cmds: - ./deploy.sh # Using Go template expressions build-prod: if: '{{eq .ENV "production"}}' cmds: - go build -ldflags="-s -w" ./... ``` #### `dir` * **Type**: `string` * **Description**: The directory in which this task should run * **Default**: If the task is in the root Taskfile, the default `dir` is `ROOT_DIR`. For included Taskfiles, the default `dir` is the value specified in their respective `includes.*.dir` field (if any). ```yaml tasks: current-dir: dir: '{{.USER_WORKING_DIR}}' cmd: pwd ``` #### `requires` * **Type**: `Requires` * **Description**: Required variables with optional enum validation ```yaml tasks: deploy: requires: vars: [API_KEY, ENVIRONMENT] cmds: - ./deploy.sh advanced-deploy: requires: vars: - API_KEY - name: ENVIRONMENT enum: [development, staging, production] - name: LOG_LEVEL enum: [debug, info, warn, error] cmds: - echo "Deploying to {{.ENVIRONMENT}} with log level {{.LOG_LEVEL}}" - ./deploy.sh # Requirements with enum from variable reference reusable-deploy: requires: vars: - name: ENVIRONMENT enum: ref: .ALLOWED_ENVS cmds: - ./deploy.sh ``` #### [`vars`](/docs/reference/schema.md#variable) * **Type**: `map[string]Variable` * **Description**: Task level variables available to individual task ```yaml tasks: default: vars: # Simple values APP_NAME: myapp VERSION: 1.0.0 DEBUG: true PORT: 8080 FEATURES: [auth, logging] cmds: # … ``` See [Prompting for missing variables interactively](/docs/guide/required-variables.md#prompting-for-missing-variables-interactively) for information on enabling interactive prompts for missing required variables. #### `watch` * **Type**: `bool` * **Default**: `false` * **Description**: Automatically run task in watch mode ```yaml tasks: dev: watch: true cmds: - npm run dev ``` #### `platforms` * **Type**: `[]string` * **Description**: Platforms where this task should run ```yaml tasks: windows-build: platforms: [windows] cmds: - go build -o app.exe ./cmd unix-build: platforms: [linux, darwin] cmds: - go build -o app ./cmd ``` ## Command Individual command configuration within a task. ### Basic Commands ```yaml tasks: example: cmds: - echo "Simple command" - ls -la ``` ### Command Object ```yaml tasks: example: cmds: - cmd: echo "Hello World" silent: true ignore_error: false platforms: [linux, darwin] set: [errexit] shopt: [globstar] timeout: 5m ``` ### Task References ```yaml tasks: example: cmds: - task: other-task vars: PARAM: value silent: false ``` ### Deferred Commands ```yaml tasks: with-cleanup: cmds: - echo "Starting work" # Deferred command string - defer: echo "Cleaning up" # Deferred task reference - defer: task: cleanup-task vars: CLEANUP_MODE: full ``` ### For Loops #### Loop Over List ```yaml tasks: greet-all: cmds: - for: [alice, bob, charlie] cmd: echo "Hello {{.ITEM}}" ``` #### Loop Over Sources/Generates ```yaml tasks: process-files: sources: ['*.txt'] cmds: - for: sources cmd: wc -l {{.ITEM}} - for: generates cmd: gzip {{.ITEM}} ``` #### Loop Over Variable ```yaml tasks: process-items: vars: ITEMS: 'item1,item2,item3' cmds: - for: var: ITEMS split: ',' as: CURRENT cmd: echo "Processing {{.CURRENT}}" ``` #### Loop Over Matrix ```yaml tasks: test-matrix: cmds: - for: matrix: OS: [linux, windows, darwin] ARCH: [amd64, arm64] cmd: echo "Testing {{.ITEM.OS}}/{{.ITEM.ARCH}}" ``` #### Loop in Dependencies ```yaml tasks: build-all: deps: - for: [frontend, backend, worker] task: build vars: SERVICE: '{{.ITEM}}' ``` ### Conditional Commands Use `if` to conditionally execute a command. If the shell command exits with a non-zero code, the command is skipped. ```yaml tasks: build: cmds: # Only run in production - cmd: echo "Optimizing for production" if: '[ "$ENV" = "production" ]' # Using Go templates - cmd: echo "Feature enabled" if: '{{eq .ENABLE_FEATURE "true"}}' # Inside for loops (evaluated per iteration) - for: [a, b, c] cmd: echo "processing {{.ITEM}}" if: '[ "{{.ITEM}}" != "b" ]' ``` ### Command Timeouts Use `timeout` to limit how long a command may run. The value uses Go duration syntax (e.g. `30s`, `5m`, `1h30m`) and must be greater than zero. ```yaml tasks: deploy: cmds: - cmd: npm run build timeout: 5m - cmd: ./deploy.sh timeout: 30m ``` When a command exceeds its timeout, it is terminated and the task fails with an error, preventing commands from hanging indefinitely in a pipeline. The timeout bounds the whole step, so an [`if`](/docs/reference/schema.md#command) condition that hangs is cut short too, and [`ignore_error`](/docs/reference/schema.md#command) covers a timeout like any other failure. A timed-out command reports [`EXIT_CODE`](/docs/reference/templating.md#exit-code) `124`, following the convention of `timeout(1)`. A dependency takes the same key: ```yaml tasks: build: deps: - task: fetch-assets timeout: 2m ``` The key goes next to the command whatever form it takes, including a `defer`: ```yaml tasks: deploy: cmds: - defer: task: cleanup timeout: 30s - defer: ./cleanup.sh timeout: 30s ``` A timed-out deferred command is logged and ignored, like other deferred errors. Calling a task that is already running under [`run: once`](/docs/reference/schema.md#task) or [`run: when_changed`](/docs/reference/schema.md#task) joins that execution instead of starting a second one. A `timeout` on such a call bounds how long you wait for it, not the shared execution itself, which only the caller that started it can bound. ## Shell Options ### Set Options Available `set` options for POSIX shell features: * `allexport` / `a` - Export all variables * `errexit` / `e` - Exit on error * `noexec` / `n` - Read commands but don't execute * `noglob` / `f` - Disable pathname expansion * `nounset` / `u` - Error on undefined variables * `xtrace` / `x` - Print commands before execution * `pipefail` - Pipe failures propagate ```yaml # Global level set: [errexit, nounset, pipefail] tasks: debug: # Task level set: [xtrace] cmds: - cmd: echo "This will be traced" # Command level set: [noexec] ``` ### Shopt Options Available `shopt` options for Bash features: * `expand_aliases` - Enable alias expansion * `globstar` - Enable `**` recursive globbing * `nullglob` - Null glob expansion ```yaml # Global level shopt: [globstar] tasks: find-files: # Task level shopt: [nullglob] cmds: - cmd: ls **/*.go # Command level shopt: [globstar] ``` --- --- url: /docs/reference/cli.md description: Complete reference for Task CLI commands, flags, and exit codes --- # Command Line Interface Reference Task has multiple ways of being configured. These methods are parsed, in sequence, in the following order with the highest priority last: * [Configuration files](/docs/reference/config.md) * [Environment variables](/docs/reference/environment.md) * *Command-line flags* In this document, we will look at the last of the three options, command-line flags. All CLI commands override their configuration file and environment variable equivalents. ## Format Task commands have the following syntax: ```bash task [options] [tasks...] [-- CLI_ARGS...] ``` ::: tip If `--` is given, all remaining arguments will be assigned to a special `CLI_ARGS` variable. ::: ## Commands ### `task [tasks...]` Run one or more tasks defined in your Taskfile. ```bash task build task test lint task deploy --force ``` ### `task --list` List all available tasks with their descriptions. ```bash task --list task -l ``` ### `task --list-all` List all tasks, including those without descriptions. ```bash task --list-all task -a ``` ### `task --init` Create a new Taskfile.yml in the current directory. ```bash task --init task -i ``` ::: tip Combine `--list` or `--list-all` with `--silent` (`-ls` or `-as` for shortants) to list only the task names in each line. Useful for scripting with `grep` or similar. ::: ## Options ### General #### `-h, --help` Show help information. ```bash task --help ``` #### `--version` Show Task version. ```bash task --version ``` #### `-v, --verbose` Enable verbose mode for detailed output. * **Config equivalent**: [`verbose`](/docs/reference/config.md#verbose) * **Environment variable**: [`TASK_VERBOSE`](/docs/reference/environment.md#task-verbose) ```bash task build --verbose ``` #### `-s, --silent` Disable command echoing. * **Config equivalent**: [`silent`](/docs/reference/config.md#silent) * **Environment variable**: [`TASK_SILENT`](/docs/reference/environment.md#task-silent) ```bash task deploy --silent ``` #### `--disable-fuzzy` Disable fuzzy matching for task names. When enabled, Task will not suggest similar task names when you mistype a task name. * **Config equivalent**: [`disable-fuzzy`](/docs/reference/config.md#disable-fuzzy) * **Environment variable**: [`TASK_DISABLE_FUZZY`](/docs/reference/environment.md#task-disable-fuzzy) ```bash task buidl --disable-fuzzy # Output: Task "buidl" does not exist # (without "Did you mean 'build'?" suggestion) ``` ### Execution Control #### `-F, --failfast` Stop executing dependencies as soon as one of them fails. * **Config equivalent**: [`failfast`](/docs/reference/config.md#failfast) * **Environment variable**: [`TASK_FAILFAST`](/docs/reference/environment.md#task-failfast) ```bash task build --failfast ``` #### `-f, --force` Force execution even when the task is up-to-date. ```bash task build --force ``` #### `-n, --dry` Compile and print tasks without executing them. * **Environment variable**: [`TASK_DRY`](/docs/reference/environment.md#task-dry) ```bash task deploy --dry ``` #### `-p, --parallel` Execute multiple tasks in parallel. ```bash task test lint --parallel ``` #### `-C, --concurrency ` Limit the number of concurrent tasks. Zero means unlimited. * **Config equivalent**: [`concurrency`](/docs/reference/config.md#concurrency) * **Environment variable**: [`TASK_CONCURRENCY`](/docs/reference/environment.md#task-concurrency) ```bash task test --concurrency 4 ``` #### `-x, --exit-code` Pass through the exit code of failed commands. ```bash task test --exit-code ``` ### File and Directory #### `-d, --dir ` Set the directory where Task will run and look for Taskfiles. ```bash task build --dir ./backend ``` #### `-t, --taskfile ` Specify a custom Taskfile path. ```bash task build --taskfile ./custom/Taskfile.yml ``` #### `-g, --global` Run the global Taskfile from `$HOME/Taskfile.{yml,yaml}`. ```bash task backup --global ``` #### `--temp-dir ` Set the directory used to store Task temporary files, such as checksums. Relative paths are relative to the root Taskfile. * **Config equivalent**: [`temp-dir`](/docs/reference/config.md#temp-dir) * **Environment variable**: [`TASK_TEMP_DIR`](/docs/reference/environment.md#task-temp-dir) ```bash task build --temp-dir .task-cache ``` ### Output Control #### `-o, --output ` Set output style. Available modes: `interleaved`, `group`, `prefixed`. * **Environment variable**: [`TASK_OUTPUT`](/docs/reference/environment.md#task-output) ```bash task test --output group ``` #### `--output-group-begin