
Task is a build tool similar to Make or just, which allows you to write project-specific build and dev commands and organise your workflow.
Ever been working on a project with tens or even hundreds of services that need to run locally and all have separate commands to run? Task makes it easier to manage and run them all at once.
This article is up-to-date with Task v3.47.0. If you're using a different version, the features mentioned in this article may not be available.
Task operates using YAML files that define the tasks for your project (ie. taskfile.yml). Inside, you define tasks that run sequentially using the following syntax:
1version: "3"23tasks:4 build:5 desc: Run the build process for the project.6 cmds:7 # Insert commands here...8 - bun run test9 - bun run lint10 - docker build -t my-app .
Task provides a number of ways to handle environment variables in your tasks.
The most primitive way to handle environment variables is to use the env key to define task-specific environment variables.
12tasks:3 build:4 desc: Run the build process for the project.5 env:6 - APP_ENV=development7 cmds:8 # ...
If certain environment variables are required for all tasks, you can set them in the env key at the top level of the taskfile.
1version: "3"23env:4 - API_KEY=sk_test_1234567890
You can also use the dotenv key to load environment variables from a .env file.
1version: "3"23dotenv:4 - .env.local # Highest priority5 - .env # Lowest priority
By default, each command in a task is executed in a new shell. If you need to write a command that requires multiple lines, you can use the | character to write a multi-line command.
1tasks:2 build:3 desc: Run the build process for the project.4 cmds:5 - |6 if [ "$APP_ENV" = "development" ]; then7 bun run test8 else9 bun run build10 fi
Perfect for commands complicated enough to require multiple lines but not complicated enough to require a script file.
Task allows you to define global variables for your tasks. They are defined at the top level of the taskfile.
1version: "3"23vars:4 APP_NAME: myapp5 VERSION: 1.0.0
If a task requires a variable, you can define it as required by using the requires key. The best part about this is that you can also validate the variable against a type. See the example below:
1tasks:2 build:3 desc: Run the build process for the project.4 requires:5 vars:6 - name: ENV7 enum: [dev, beta, prod]
If ENV is not set, or not in dev, beta, or prod, Task will throw an error and the task will not run. This is the default behaviour, but you can also change the config to prompt the user to enter the value of the variable if it is not set.
Want to pass the variable in at runtime? Do this:
1version: "3"23vars:4 END_TO_END: '{{.END_TO_END | default "false"}}'56tasks:7 tet:8 desc: Run the test suite for the project.9 env:10 END_TO_END: '{{.END_TO_END}}'11 cmds:12 - bun run test13 requires:14 vars:15 - name: END_TO_END
Then when running the task, you can pass the variable in at runtime: ie. task build SEND_MAIL=true.
Note also how I can use the | default "false" syntax to set a default value if the variable is not set.
In a recent update, Task added support for control flow.
At the command level, a particular command that should only run if a particular condition is satisfied can be written like this:
1tasks:2 build:3 desc: Run the build process for the project.4 cmds:5 - cmd: bun run build6 if: '[ "$ENV" = "production" ]'
For example, a use case I often find for this is setting and using runtime arguments:
1tasks:2 build:3 desc: Run the dev server for the project.4 cmds:5 - cmd: bun run dev --port {{.PORT}}6 if: '{{ne .PORT 3000}}'
If statements can also be set at the task level if a particular task should only run when a condition is satisfied:
1tasks:2 install:3 desc: Install the dependencies for the project.4 if: '{{eq .INSTALL_DEPENDENCIES "true"}}'5 cmds:6 - cmd: bun install
The update also provided support for loops; I personally haven't found a use case for this yet, but it's a feature that is available if you need it.
1tasks:2 process-items:3 cmds:4 - for: ['a', 'b', 'c']5 cmd: echo "processing {{.ITEM}}"6 if: '[ "{{.ITEM}}" != "b" ]'
This is one of the coolest features of Task.
When working in a development team, you'll often have team members working across different operating systems. Or, you may have a staging environment that uses a different architecture to your production environment.
Never fear! Task allows you to define OS or architecture specific taskfiles that override default tasks.
For example, you may want to use a different package manager for Linux and macOS. You can define a taskfile for each OS and include the common tasks in the main taskfile.
To identify a taskfile as OS specific, alter the name of the taskfile to include the OS name, such as taskfile_darwin.yml or taskfile_windows.yml
So that task can find the correct taskfile for the OS, you can use the include key to include the OS-specific taskfiles in a root taskfile.
I usually set up my directory like this:
1├── infra2│ └──dev3│ ├── taskfile_darwin.yml4│ └── taskfile_windows.yml5└── taskfile.yml
Then in the root taskfile:
1version: "3"23includes:4 os:5 taskfile: ./infra/dev/taskfile_{{OS}}.yml6 flatten: true
I recommend adding flatten: true to your include statements for your OS taskfiles. This abstracts the OS-specific nature of the task to the user running the command. For example, you can have each OS specific taskfile define its own install command, and then regardless of the OS, the correct OS-specific install command will be run. Running task -l will show only the correct install task for the OS.
If you only have a specific task that is OS-specific, and don't want to create a new taskfile for it, you can use the platforms key to limit the platforms the task can run on.
1tasks:2 build:3 desc: Run the build process for the project.4 platforms: [linux]
Task is a powerful build tool that allows you to write project-specific build and dev commands and organise your workflow. It is a great alternative to Make or just, and a great tool to have in your devtools.
If you're interested in learning more about Task, check out the Task documentation.
Banner by Diane Picchiottino on Unsplash