Taskfile
What is a Taskfile?
Taskfile is a task runner and build tool designed to be simpler and easier to use than traditional tools like GNU Make. It operates with a single binary called task and requires no additional dependencies. Taskfile allows you to define tasks using a simple YAML schema in a Taskfile.yml file.
1. Why need a Taskfile?
Taskfile offers several advantages over traditional build tools:
- Simplicity: Taskfile simplifies task automation with a straightforward YAML syntax.
- Portability: Taskfile is a single binary that works across different platforms without additional dependencies.
- Flexibility: Taskfile supports advanced features like task dependencies, variables, and error handling.
- CLI Integration: Taskfile tasks can be executed directly from the command line, making it easy to integrate with other tools and scripts.
2. Features of Taskfile
Taskfile provides a range of features to streamline your development workflow:
- Task Definitions: Define tasks in a
Taskfile.ymlusing a simple YAML schema. - Task Dependencies: Specify task dependencies to ensure tasks run in the correct order.
- Variables: Use variables within tasks to store and reuse values.
- Error Handling: Handle errors gracefully by ignoring or failing tasks based on conditions.
- OS based tasks: Run tasks based on the operating system
- Concurrent Tasks: Run tasks concurrently to improve performance.
- Conditional Logic: Implement if-else conditions to control task execution.
- Loops: Use loops to iterate over tasks and execute them multiple times.
- Template Tasks: Create task templates for reuse across multiple tasks.
- Import remote tasks: Import tasks from remote repositories or URLs.
Getting Started
1 Install and Define OS-Specific Tasks
Before starting with Taskfile, you need to install Task on your system. Task is a powerful task runner that makes automation in your projects easy and efficient.
- Remove any existing Task installation (if applicable):
rm -rf $(which task)
- Install Task using the following command:
sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b /usr/local/bin
- Verify the installation:
- After installation, check if Task is installed by running:
# The output should include the version of Task installed.
task --version
2 First Taskfile
Test with first Taskfile.
- Create a new directory named
~/test_taskfile:
mkdir -p ~/test_taskfile
- Create a new file named
Taskfile.ymlinside the~/test_taskfiledirectory:
touch ~/test_taskfile/Taskfile.yml
- Adding a task to Taskfile
version: '3'
tasks:
hello:
cmds:
- echo "Hello World" > ~/test_taskfile/output.txt
- Run the hello task:
cd ~/test_taskfile
task hello
Environment Variables and Advanced Usage
1. Environment Variables
Explore how to use environment variables, task variables, and dynamic variables within Taskfile. How to set, override, and utilize these variables effectively in Taskfile.
Taskfile supports the use of environment variables, similar to how they are used in application development with .env files.
Creating Tasks with Environment Variables
- Create a
.envfile in the~/test_taskfiledirectory:
echo "ENDPOINT=foo.bar" > ~/test_taskfile/.env
- Add a task hello to
Taskfile.yml:
version: '3'
tasks:
hello:
dotenv:
- '.env'
cmds:
- 'echo "Endpoint is: $ENDPOINT"'
- Execute the task:
cd ~/test_taskfile
task hello
- Expected Output:
Endpoint is: foo.bar
Using System Variables
- Modify the msg task to print the HOME variable:
version: '3'
tasks:
msg:
cmds:
- 'echo "Home directory is: $HOME"'
- 'echo "Message is: {{.MESSAGE}}"'
vars:
MESSAGE: FooBar
- Execute the task:
cd ~/test_taskfile
task msg
- Expected Output:
Home directory is: /home/your_username
Message is: FooBar
Overriding Environment Variables
Can override environment variables defined in Taskfile.yml by setting them in the system or passing them via CLI arguments.
- Override environment variables via system:
export MESSAGE=HelloWorld
task msg
- Expected Output:
Message is: HelloWorld
- Override via CLI arguments:
task msg MESSAGE=HelloWorld
Setting Default Values for Task Variables
Set default values for task variables in Taskfile.yml and see how they can be overridden via CLI.
- Set default value for
MESSAGEin the msg task:
tasks:
msg:
cmds:
- 'echo "Message is: {{.MESSAGE}}"'
vars:
MESSAGE: '{{.MESSAGE | default "Testing123"}}'
- Execute the task and observe the default value:
task msg
Message is: Testing123
- Override the default value via CLI:
task msg MESSAGE=HelloWorld
Message is: HelloWorld
Using Shell Command Output as Variable Value
Use the output of a shell command as the value for a variable in Taskfile.
- Create a task os-version that prints the OS version:
tasks:
os-version:
vars:
VERSION:
sh: cat /etc/os-release | grep "VERSION_ID" | cut -d "=" -f2
cmds:
- echo "OS version is {{.VERSION}}"
- Execute the task:
task os-version
- Expected Output:
OS version is 22.04
Including Other Taskfiles, Task Dependencies, and Concurrent Tasks
Explore several advanced features of Taskfile, including the use of variables, task dependencies, and concurrent task execution. Also integrate Docker commands within Taskfile to automate building Docker images for different applications.
1. Understanding Taskfile Variables
Taskfile allows you to define and use variables within tasks. These variables can be passed as arguments when running a task or defined directly within the Taskfile.
- Taskfile configuration valid even though variables are not pre-defined
version: '3'
tasks:
print:
desc: "Prints the name and last name"
cmds:
- echo {{.NAME}} {{.LAST_NAME}}
- Command will print "Foo Bar"
task print NAME=Foo LAST_NAME=Bar
2. Creating a Docker Build Task
Create a task template for building Docker images. The template will be reused across different tasks to build images for multiple applications.
- Create Directory and Task Template:
mkdir -p ~/test_taskfile/task-templates
- Create
docker-task.yml:
version: '3'
tasks:
docker:build:
desc: "Builds a Docker image"
cmds:
- docker build -t {{.NAME}} {{.SRC_DIR}}
- Save this template in
~/test_taskfile/task-templates/docker-task.yml.
3. Including Task Dependencies with includes
Taskfile supports including other Taskfiles, allow to reuse tasks defined in separate files.
- Create a main
Taskfile.ymlin~/test_taskfilethat includes thedocker-task.ymltemplate:
version: '3'
includes:
common: task-templates/docker-task.yml
tasks:
app:build:
cmds:
- task: common:docker:build
vars:
SRC_DIR: "app1"
NAME: "app1"
- Verify the setup by checking that the task correctly references the included Docker build task.
task app:build
4. Running Tasks Concurrently
Taskfile allows to run tasks concurrently, which can be particularly useful when we need to perform similar tasks simultaneously, such as building Docker images for multiple applications.
- Extend
Taskfile.ymlto include tasks forapp,app1, andapp2:
version: '3'
includes:
common: task-templates/docker-task.yml
tasks:
app:build:
cmds:
- task: common:docker:build
vars:
SRC_DIR: "app"
NAME: "app"
app1:build:
cmds:
- task: common:docker:build
vars:
SRC_DIR: "app1"
NAME: "app1"
app2:build:
cmds:
- task: common:docker:build
vars:
SRC_DIR: "app2"
NAME: "app2"
- Run the tasks concurrently:
task --parallel app:build app1:build app2:build
Optimizing and Configuring Taskfile For Multiple OS
This guide focuses on running OS-specific tasks, using working directories, and optimizing task execution in Taskfile. Create tasks that run based on the operating system, utilize working directories, and prevent unnecessary work by fingerprinting files.
Creating and managing tasks in Taskfile that are specific to different operating systems, utilizing working directories for task execution, and preventing unnecessary work using fingerprinting.
1. Running OS-Specific Tasks
Taskfile allows to create tasks that are executed only on specific operating systems. This is useful when the command differ between platforms.
-
Create
Taskfileand Definenode:installTask:- Create a task named node:install in the
Taskfile.ymlthat installs Node.js and npm based on the OS:
- Create a task named node:install in the
version: '3'
tasks:
node:install:
cmds:
- cmd: |-
echo "Installing nodejs and npm on Linux"
sudo apt-get update && sudo apt install -y nodejs npm
platforms:
- linux
- cmd: |-
echo "Installing nodejs and npm on macOS"
brew install nodejs
brew install npm
platforms:
- darwin
desc: "Install Node.js and npm based on the OS"
-
Run the Task:
- Execute the task using the following command:
task node:install
- The task will execute the appropriate commands based on your OS.
2. Using Working Directory for Tasks
Specify a working directory for a task using the dir attribute, which allows the task to execute commands in a specific directory.
-
Define the
npm:buildTask withSRC_DIRVariable:- Create a task named
npm:buildthat uses theSRC_DIRvariable to specify the source directory:
- Create a task named
version: '3'
tasks:
npm:build:
requires:
vars:
- SRC_DIR
dir: "{{.SRC_DIR}}"
cmds:
- npm install && npm run build
desc: "Build the npm project in the specified source directory"
-
Run the Task:
- Execute the task with the specified source directory:
task npm:build SRC_DIR=~/todo/app
- The task will install dependencies and build the project in the
~/todo/appdirectory.
3. Preventing Unnecessary Work
Taskfile supports fingerprinting to prevent tasks from running unnecessarily. You can configure tasks to only run when certain files have changed.
-
Create
npm:installandnpm:buildTasks with Fingerprinting:- Define the
npm:installtask to install dependencies only if package.json changes:
- Define the
version: '3'
tasks:
npm:install:
requires:
vars:
- SRC_DIR
dir: "{{.SRC_DIR}}"
cmds:
- npm install
sources:
- package.json
desc: "Install npm dependencies for the project in the specified source directory"
- Define the
npm:buildtask to build the project only if**/*.jsfiles change:
tasks:
npm:build:
deps:
- npm:install
requires:
vars:
- SRC_DIR
dir: "{{.SRC_DIR}}"
sources:
- '**/*.js'
cmds:
- npm run build
desc: "Build the npm project in the specified source directory"
-
Run the Task:
- Execute the task with the specified source directory:
task npm:build SRC_DIR=~/todo/app
- On subsequent runs, the task will skip execution if no relevant files have changed.
Conditional Logic, Loops
Explore advanced Taskfile features, including ignoring errors, using the defer feature for cleanup, implementing conditional logic, and using loops in Taskfile. The lab involves working with applications written in Node.js, Python, and Go, all of which are containerized using Docker.
Automating tasks for applications written in Node.js, Python, and Go. These applications are containerized using Docker. The Taskfile.yml provided in the ~/todo directory includes tasks for building and pushing Docker images. Handle errors, clean up after tasks, use conditional logic, and loop over multiple tasks in Taskfile.
1. Ignoring Linting Errors in Node.js Application Build
When building a Node.js application, you may encounter linting errors. Taskfile allows you to ignore these errors using the ignore_error: true attribute.
-
Update the npm.yml Template:
- Modify the
~/todo/tasktemplate/npm.ymlto ignore linting errors by addingignore_error: trueto the lint task:
- Modify the
version: '3'
tasks:
install:
cmds:
- npm install
lint:
cmds:
- eslint .
ignore_error: true # Add this line to ignore linting errors
deps: [install]
build:
cmds:
- npm run build
deps: [lint]
- Run the Task:
- Test the updated task using the following command:
cd ~/todo
task ui:build
2. Cleaning Up Cache Files After Build Using defer
After building an application, it is important to clean up cache files. Taskfile's defer feature allows you to run cleanup commands after a task completes, even if it fails.
-
Update the
npm.ymlTemplate:- Modify the build task in the
~/todo/tasktemplate/npm.ymlto include a cleanup task that deletes the.cachedirectory using defer:
- Modify the build task in the
version: '3'
tasks:
build:
internal: true
dir: '{{.SRC}}'
cmds:
- npm run build
- defer: rm -rf .cache # Add this line for cleanup
deps:
- lint
-
Run the Task:
- Test the task and ensure the
.cachedirectory is removed after the build completes:
- Test the task and ensure the
cd ~/todo
task ui:build
3. Using If-Else Conditions in Taskfile
Conditional logic in Taskfile allows you to make tasks more dynamic. Tag Docker images with the git commit hash if available.
-
Update the
docker.ymlTemplate:- Modify the
~/todo/tasktemplate/docker.ymlto include conditional logic for tagging the Docker image with the git commit hash if it exists:
- Modify the
version: '3'
tasks:
build-push:
vars:
GIT_TAG:
sh: "(command -v git > /dev/null && cd {{.SRC}} && git rev-parse --short HEAD) || echo ''"
cmds:
- docker build --pull -t "{{.REGISTRY}}/{{.REPOSITORY}}:{{.IMAGE_TAG}}" .
- docker push "{{.REGISTRY}}/{{.REPOSITORY}}:{{.IMAGE_TAG}}"
- |
{{ if .GIT_TAG }}
echo "Tagging with git commit hash"
docker tag "{{.REGISTRY}}/{{.REPOSITORY}}:{{.IMAGE_TAG}}" "{{.REGISTRY}}/{{.REPOSITORY}}:{{.GIT_TAG}}"
docker push "{{.REGISTRY}}/{{.REPOSITORY}}:{{.GIT_TAG}}"
{{ end }}
-
Run the Task:
- Execute the task with the following command:
cd ~/todo
task docker:build-push IMAGE_TAG=latest REGISTRY=localhost:5000 REPOSITORY=app1 SRC=./app1
4. Re-running Tasks After Adding Git to App
Now that git has been added to the app1 directory, re-run the task to build and push the Docker image with both the latest tag and the git commit hash tag.
-
Run the Task:
- Use the following command to build and push the Docker image:
cd ~/todo
task docker:build-push IMAGE_TAG=latest REGISTRY=localhost:5000 REPOSITORY=app1 SRC=./app1
5. Using Looping in Taskfile
Taskfile allows to loop over a list of items, which is useful for running the same task on multiple applications.
-
Create a Loop to Build and Push All Docker Images:
- Add a task in
Taskfile.ymlto build and push Docker images for all three applications (app1, app2, and app3) using a loop:
- Add a task in
tasks:
build-push-all:
cmds:
- for:
- app1
- app2
- app3
task: docker:build-push
vars:
IMAGE_TAG: "latest"
REGISTRY: "localhost:5000"
REPOSITORY: "{{ .ITEM }}"
SRC: "{{ .ITEM }}"
-
Run the Task:
- Test the task by running:
task build-push-all
Integrating Taskfile with CICD Pipelines
Using Taskfile in GitLab CI/CD pipelines to build and deploy applications in a reusable and templated manner. By following these steps, you will learn how to integrate Taskfile with GitLab to avoid code duplication and streamline CI/CD workflows.
Integrate Taskfile with GitLab CI/CD pipelines to automate the build and deployment processes. You'll work with a Taskfile that is stored in a remote repository and include it in your application repository to leverage predefined tasks.
1. Introduction to Taskfile in GitLab CI/CD
Taskfile is a versatile tool that can be used across different CI/CD pipelines, including GitLab CI/CD. This approach helps avoid code duplication and makes the tasks reusable, ensuring consistency across different environments.
2. Including a Remote Taskfile
You will include a remote Taskfile from another GitLab repository into the Taskfile.yml of your application repository.
-
Include the Remote Taskfile:
-
Navigate to the tasktemplates repository in GitLab and open the raw URL of the Taskfile.yml file.
-
Modify the Taskfile.yml of your application repository to include the remote Taskfile:
-
version: '3'
includes:
app: https://github.com/root/tasktemplates/Taskfile.yml
-
Verify the Inclusion:
- Ensure that the remote Taskfile has been successfully included by running the following command:
cd ~/todo/app
git pull