Task es un ejecutor de tareas escrito en Go que lee sus instrucciones de un archivo Taskfile.yml. Cubre el uso más habitual de Make (tener a mano los comandos de compilar, probar y desplegar un proyecto) con una sintaxis YAML legible, variables explícitas y sin las trampas de los tabuladores ni de los objetivos que coinciden con nombres de archivo. En este tutorial instalarás Task en Ubuntu 24.04 y construirás paso a paso un Taskfile para un proyecto de ejemplo.
Los ejemplos solo usan herramientas del sistema (cp, tar, rsync) para que puedas ejecutarlos sin instalar ningún lenguaje. En tu proyecto real sustituirás esos comandos por go build, npm run build, pytest o lo que corresponda.
Requisitos previos
- Un servidor o equipo con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath, con un usuario no root con privilegios
sudo. gitinstalado (algunas variables del ejemplo leen el commit actual).- Conocimientos básicos de YAML y de la terminal.
Paso 1: Instalar Task
El proyecto publica un paquete .deb en cada versión de GitHub. Descárgalo e instálalo con apt, que así queda registrado en el sistema y se puede desinstalar limpiamente:
cd /tmp
curl -fsSLO https://github.com/go-task/task/releases/latest/download/task_linux_amd64.deb
sudo apt install -y ./task_linux_amd64.deb
En servidores arm64 usa task_linux_arm64.deb. Si prefieres actualizaciones automáticas, Task también está disponible como snap con sudo snap install task --classic.
Comprueba la instalación:
task --version
Task version: v3.x.y
Notael binario se llama
task. No lo confundas con Taskwarrior, un gestor de listas de tareas que también usa ese nombre en algunos repositorios.
Activa el autocompletado en bash añadiendo esta línea a tu ~/.bashrc:
echo 'eval "$(task --completion bash)"' >> ~/.bashrc
source ~/.bashrc
Paso 2: Crear el proyecto de ejemplo y el primer Taskfile
Crea un proyecto mínimo con un par de archivos fuente:
mkdir -p ~/demo-task/src && cd ~/demo-task
git init -q
echo '<h1>Hola desde Task</h1>' > src/index.html
echo 'body { font-family: sans-serif; }' > src/style.css
git add . && git commit -qm "Primer commit"
Crea el Taskfile.yml en la raíz del proyecto:
nano Taskfile.yml
version: '3'
vars:
APP: demo-web
DIST: dist
tasks:
default:
desc: Mostrar las tareas disponibles
cmds:
- task --list
build:
desc: Copiar los fuentes a la carpeta de distribución
cmds:
- mkdir -p {{.DIST}}
- cp -r src/. {{.DIST}}/
clean:
desc: Borrar los archivos generados
cmds:
- rm -rf {{.DIST}} *.tar.gz
Las variables se referencian con la sintaxis de plantillas de Go, {{.NOMBRE}}. Solo las tareas con desc aparecen en task --list.
Lista y ejecuta las tareas:
task --list
task build
task: Available tasks for this project:
* build: Copiar los fuentes a la carpeta de distribución
* clean: Borrar los archivos generados
* default: Mostrar las tareas disponibles
task: [build] mkdir -p dist
task: [build] cp -r src/. dist/
Task muestra cada comando antes de ejecutarlo con el prefijo task: [nombre]. Si un comando falla, la tarea se detiene con el mismo código de salida, igual que Make.
Paso 3: Evitar trabajo repetido con sources y generates
Make decide si recompilar comparando fechas de archivos. Task hace lo mismo con sources y generates, pero por defecto compara sumas de verificación del contenido. Cambia la tarea build:
build:
desc: Copiar los fuentes a la carpeta de distribución
sources:
- src/**/*
generates:
- '{{.DIST}}/index.html'
cmds:
- mkdir -p {{.DIST}}
- cp -r src/. {{.DIST}}/
Ejecuta la tarea dos veces seguidas:
task build
task build
task: [build] mkdir -p dist
task: [build] cp -r src/. dist/
task: Task "build" is up to date
La segunda vez no hace nada porque los fuentes no han cambiado. Modifica src/index.html y vuelve a ejecutarla para ver que se repite. Para forzarla aunque esté al día, usa task --force build. Task guarda las sumas en el directorio .task/; añádelo a tu .gitignore.
Paso 4: Encadenar tareas con dependencias
Una tarea puede depender de otras. Las indicadas en deps se ejecutan antes y en paralelo; si necesitas un orden estricto, llámalas desde cmds con task:. Añade una tarea que empaquete el resultado:
package:
desc: Crear un tar.gz con la versión actual
deps: [build]
vars:
COMMIT:
sh: git rev-parse --short HEAD
cmds:
- tar -czf {{.APP}}-{{.COMMIT}}.tar.gz -C {{.DIST}} .
- ls -lh {{.APP}}-{{.COMMIT}}.tar.gz
La variable COMMIT es dinámica: su valor es la salida del comando indicado en sh. Ejecuta el empaquetado:
task package
task: Task "build" is up to date
task: [package] tar -czf demo-web-a1b2c3d.tar.gz -C dist .
task: [package] ls -lh demo-web-a1b2c3d.tar.gz
-rw-rw-r-- 1 usuario usuario 312 sep 25 10:00 demo-web-a1b2c3d.tar.gz
Para ver qué haría una tarea sin ejecutarla, usa task --dry package.
Paso 5: Variables desde la línea de comandos y desde .env
Las variables se pueden pasar al invocar Task, y tienen prioridad sobre las del Taskfile. Además, la clave dotenv carga archivos .env como variables de entorno.
Crea un archivo .env con el destino del despliegue:
echo 'DEPLOY_PATH=/tmp/demo-deploy' > .env
Añade dotenv al principio del Taskfile, justo después de version:
version: '3'
dotenv: ['.env']
Y una tarea de despliegue que exige una variable ENV:
deploy:
desc: Publicar dist en DEPLOY_PATH (uso, task deploy ENV=staging)
deps: [build]
requires:
vars: [ENV]
preconditions:
- sh: git diff --quiet
msg: Hay cambios sin confirmar en git. Haz commit antes de desplegar.
cmds:
- mkdir -p "$DEPLOY_PATH/{{.ENV}}"
- rsync -a --delete {{.DIST}}/ "$DEPLOY_PATH/{{.ENV}}/"
- echo "Desplegado {{.APP}} en $DEPLOY_PATH/{{.ENV}}"
requires.varsdetiene la tarea si falta la variable, con un mensaje claro.preconditionsejecuta comprobaciones previas; si alguna devuelve un código distinto de 0, muestramsgy aborta.$DEPLOY_PATHes una variable de entorno que viene del.env, por eso se usa con la sintaxis del shell.
Prueba primero sin la variable y después con ella:
task deploy
task deploy ENV=staging
task: Task "deploy" cancelled because it is missing required variables: ENV
task: Task "build" is up to date
task: [deploy] mkdir -p "$DEPLOY_PATH/staging"
task: [deploy] rsync -a --delete dist/ "$DEPLOY_PATH/staging/"
task: [deploy] echo "Desplegado demo-web en $DEPLOY_PATH/staging"
Desplegado demo-web en /tmp/demo-deploy/staging
Si tienes cambios sin confirmar, la precondición lo impedirá. Añade .task/, dist/, *.tar.gz y .env a .gitignore y haz commit para que el árbol quede limpio.
Paso 6: Ejecutar una tarea solo cuando haga falta con status
status es lo contrario de preconditions: si todos sus comandos terminan con éxito, la tarea se considera hecha y se omite. Es útil para pasos de preparación idempotentes:
setup:
desc: Crear el .env a partir de la plantilla si no existe
status:
- test -f .env
cmds:
- cp .env.example .env
Con .env ya presente, task setup responde task: Task "setup" is up to date sin tocar nada.
Paso 7: Dividir el Taskfile con includes
En proyectos grandes conviene separar las tareas por área. Crea un Taskfile para las tareas de servidor:
mkdir -p taskfiles
nano taskfiles/servidor.yml
version: '3'
tasks:
espacio:
desc: Mostrar el espacio libre en disco
cmds:
- df -h /
servicios:
desc: Listar los servicios de systemd que han fallado
cmds:
- systemctl --failed --no-pager
Inclúyelo en el Taskfile.yml principal, al mismo nivel que vars y tasks:
includes:
srv:
taskfile: ./taskfiles/servidor.yml
optional: true
Las tareas incluidas se invocan con el espacio de nombres como prefijo:
task srv:espacio
task: [srv:espacio] df -h /
Filesystem Size Used Avail Use% Mounted on
/dev/vda1 39G 4.2G 35G 11% /
Con optional: true, Task no falla si el archivo incluido no existe.
Paso 8: Usar Task en GitHub Actions
Como el Taskfile es la única fuente de verdad de cómo se compila el proyecto, el pipeline de CI solo tiene que instalar Task y llamar a las mismas tareas que usas en local. Crea .github/workflows/ci.yml:
name: CI
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Instalar Task
uses: arduino/setup-task@v2
with:
version: 3.x
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Empaquetar
run: task package
En otros sistemas de CI instala el .deb del paso 1 en la imagen del runner o usa una imagen que ya incluya task.
Taskfile frente a Makefile
| Aspecto | Make | Task |
|---|---|---|
| Formato | Sintaxis propia, recetas con tabulador obligatorio | YAML |
| Recompilar solo si hay cambios | Por fecha de archivos | Por suma de verificación (o fecha, con method: timestamp) |
| Variables | Expansión en varias fases, difícil de depurar | Plantillas Go, dinámicas con sh: |
Archivos .env | No | dotenv |
| Dependencias en paralelo | Con make -j | Por defecto en deps |
| Disponibilidad | Preinstalado en casi todos los sistemas | Hay que instalar un binario |
Si tu proyecto compila C o C++ y depende del grafo de archivos de Make, sigue con Make. Si lo usas solo como lista de comandos del proyecto, Task es más fácil de leer y mantener.
Solución de problemas
task: No Taskfile found. Task busca Taskfile.yml (o Taskfile.yaml, taskfile.yml) en el directorio actual y en los superiores. Comprueba el nombre del archivo o indica la ruta con task --taskfile ruta/Taskfile.yml.
Una variable aparece vacía. Las variables del Taskfile y de la línea de comandos se usan con {{.VAR}}; las de entorno y las del .env, con $VAR dentro del comando. Revisa el comando final con task --dry nombre.
La tarea dice is up to date pero quieres ejecutarla. Usa --force, o borra el directorio .task/ para reiniciar todas las sumas de verificación.
Error de YAML al cargar el Taskfile. Los valores que contienen {{ }} al principio o dos puntos deben ir entre comillas, por ejemplo - '{{.DIST}}/index.html'.
Conclusión
Tienes Task instalado y un Taskfile que compila solo cuando cambian los fuentes, empaqueta con el commit actual, despliega con variables obligatorias y precondiciones, se divide en archivos incluidos y se reutiliza tal cual en CI. Como siguientes pasos, traslada los comandos de tu Makefile actual tarea por tarea, usa task --summary nombre para documentar las tareas más complejas y añade task --list al README del proyecto.
