Taskfile como Alternativa a Makefile
Taskfile es una herramienta de automatización moderna escrita en Go que usa YAML en lugar de la sintaxis críptica de Makefile. Permite definir tareas, dependencias y variables de forma clara y portable, siendo una excelente alternativa a Make para proyectos que necesitan automatización de comandos sin la complejidad de herramientas CI/CD completas. Esta guía cubre todo lo necesario para usar Taskfile en proyectos Linux de producción.
Requisitos Previos
- Linux (Ubuntu/Debian o CentOS/Rocky)
- Acceso a terminal con permisos de usuario (sudo para instalación del sistema)
- Conocimientos básicos de YAML
Instalación de Task
# Método 1: Script oficial de instalación (recomendado)
sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b /usr/local/bin
# Método 2: Con Homebrew (si está disponible en Linux)
brew install go-task/tap/go-task
# Método 3: Con snap
snap install task --classic
# Método 4: Descarga manual del binario
curl -L https://github.com/go-task/task/releases/latest/download/task_linux_amd64.tar.gz | tar xz
sudo mv task /usr/local/bin/
# Verificar la instalación
task --version
Autocompletado en bash/zsh
# Para bash
task --completion bash > ~/.task_completion
echo "source ~/.task_completion" >> ~/.bashrc
# Para zsh
task --completion zsh > ~/.zsh_completions/_task
echo "fpath=(~/.zsh_completions $fpath)" >> ~/.zshrc
source ~/.zshrc
Estructura Básica de un Taskfile
El archivo Taskfile.yml debe estar en la raíz del proyecto:
# Taskfile.yml
version: '3'
# Variables globales del proyecto
vars:
APP_NAME: mi-aplicacion
BUILD_DIR: ./dist
tasks:
# Tarea por defecto (se ejecuta con solo 'task')
default:
desc: "Mostrar tareas disponibles"
cmds:
- task --list
# Tarea de construcción básica
build:
desc: "Compilar la aplicación"
cmds:
- echo "Compilando {{.APP_NAME}}..."
- go build -o {{.BUILD_DIR}}/{{.APP_NAME}} ./cmd/main.go
# Tarea de pruebas
test:
desc: "Ejecutar pruebas unitarias"
cmds:
- go test ./... -v -cover
# Tarea de limpieza
clean:
desc: "Eliminar archivos generados"
cmds:
- rm -rf {{.BUILD_DIR}}
- echo "Limpieza completada"
# Listar todas las tareas disponibles
task --list
# Ejecutar una tarea específica
task build
# Ejecutar múltiples tareas
task clean build test
# Ver qué comandos ejecutaría una tarea sin ejecutarlos
task --dry build
Dependencias y Orden de Ejecución
version: '3'
tasks:
# Tarea que depende de otras tareas (se ejecutan antes)
deploy:
desc: "Desplegar la aplicación"
deps: [lint, test, build]
cmds:
- echo "Desplegando en producción..."
- rsync -avz dist/ usuario@servidor:/var/www/app/
lint:
desc: "Verificar calidad del código"
cmds:
- golangci-lint run ./...
test:
desc: "Ejecutar todas las pruebas"
cmds:
- go test ./... -race
build:
desc: "Compilar binario"
cmds:
- go build -o dist/app ./cmd/
# Dependencias en paralelo (más rápido)
ci:
desc: "Pipeline completo de CI"
deps:
- task: lint
- task: test
cmds:
- task: build
- task: deploy
Control de ejecución con fuentes y destinos
tasks:
build:
desc: "Compilar solo si los fuentes cambiaron"
sources:
- "**/*.go"
- go.mod
- go.sum
generates:
- dist/app
cmds:
- go build -o dist/app ./cmd/
Variables y Entorno
version: '3'
# Variables con diferentes niveles de precedencia
vars:
# Variable estática
VERSION: "1.0.0"
# Variable calculada con comando shell
GIT_COMMIT:
sh: git rev-parse --short HEAD
# Variable con valor por defecto (sobreescribible desde CLI)
ENV: "development"
# Cargar variables desde archivo .env
dotenv: ['.env', '.env.local']
tasks:
info:
desc: "Mostrar información del proyecto"
cmds:
- echo "Versión: {{.VERSION}}"
- echo "Commit: {{.GIT_COMMIT}}"
- echo "Entorno: {{.ENV}}"
# Variables de entorno del sistema también disponibles
- echo "Usuario: $USER"
build:
desc: "Construir con metadatos de versión"
vars:
# Variable local a esta tarea
BUILD_TIME:
sh: date -u +"%Y-%m-%dT%H:%M:%SZ"
cmds:
- |
go build \
-ldflags "-X main.version={{.VERSION}} -X main.commit={{.GIT_COMMIT}} -X main.buildTime={{.BUILD_TIME}}" \
-o dist/app ./cmd/
# Pasar variables desde la línea de comandos
# Uso: task deploy ENV=produccion VERSION=2.0.0
deploy:
desc: "Desplegar en entorno específico"
cmds:
- echo "Desplegando versión {{.VERSION}} en {{.ENV}}"
- ./scripts/deploy.sh {{.ENV}}
Condicionales y Validaciones
version: '3'
tasks:
deploy:
desc: "Desplegar con verificaciones previas"
# Verificar que se cumplan condiciones antes de ejecutar
preconditions:
- sh: "git diff --quiet"
msg: "Hay cambios sin commitear. Haz commit antes de desplegar."
- sh: "test -f dist/app"
msg: "El binario no existe. Ejecuta 'task build' primero."
cmds:
- echo "Todas las verificaciones pasaron, desplegando..."
# Ejecutar solo si no existe el archivo de destino
generate-config:
desc: "Generar configuración inicial"
status:
- test -f config/app.yaml
cmds:
- cp config/app.yaml.example config/app.yaml
- echo "Configuración generada"
# Tarea de plataforma específica
install-deps:
desc: "Instalar dependencias del sistema"
cmds:
# Detectar el sistema operativo
- |
if command -v apt-get &>/dev/null; then
sudo apt-get install -y curl git jq
elif command -v dnf &>/dev/null; then
sudo dnf install -y curl git jq
else
echo "Sistema operativo no soportado"
exit 1
fi
Includes y Taskfiles Modulares
Para proyectos grandes, divide las tareas en archivos separados:
# Taskfile.yml principal
version: '3'
includes:
# Incluir Taskfile de Docker
docker:
taskfile: ./taskfiles/docker.yml
dir: . # Directorio desde donde se ejecutan los comandos
# Incluir Taskfile de base de datos
db:
taskfile: ./taskfiles/database.yml
optional: true # No fallar si no existe
# Incluir con alias
k8s:
taskfile: ./taskfiles/kubernetes.yml
aliases: [kubernetes, kube]
tasks:
default:
cmds:
- task --list
# taskfiles/docker.yml
version: '3'
tasks:
build:
desc: "Construir imagen Docker"
cmds:
- docker build -t {{.IMAGE_NAME}}:{{.TAG}} .
push:
desc: "Subir imagen al registro"
cmds:
- docker push {{.IMAGE_NAME}}:{{.TAG}}
up:
desc: "Levantar servicios con Docker Compose"
cmds:
- docker compose up -d
down:
desc: "Detener servicios"
cmds:
- docker compose down
# Ejecutar tareas de módulos incluidos
task docker:build
task docker:push
task db:migrate
Integración con CI/CD
GitHub Actions
# .github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Instalar Task
uses: arduino/setup-task@v2
with:
version: 3.x
- name: Ejecutar CI completo
run: task ci
GitLab CI
# .gitlab-ci.yml
stages:
- test
- build
- deploy
variables:
TASK_VERSION: "3.x"
before_script:
- sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b /usr/local/bin
test:
stage: test
script:
- task test
build:
stage: build
script:
- task build
artifacts:
paths:
- dist/
deploy:
stage: deploy
script:
- task deploy ENV=produccion
only:
- main
Taskfile completo para proyecto web
# Taskfile.yml de ejemplo completo
version: '3'
dotenv: ['.env']
vars:
APP: mi-app
REGISTRY: registry.empresa.com
TAG:
sh: git describe --tags --always --dirty 2>/dev/null || echo "dev"
tasks:
# Desarrollo local
dev:
desc: "Iniciar servidor de desarrollo"
cmds:
- docker compose -f docker-compose.dev.yml up
# Calidad de código
lint:
desc: "Ejecutar linters"
cmds:
- eslint src/
- prettier --check "src/**/*.{js,ts}"
# Pruebas
test:
desc: "Ejecutar suite completa de pruebas"
cmds:
- jest --coverage --ci
# Construcción
build:
desc: "Construir para producción"
deps: [lint, test]
cmds:
- npm run build
- docker build -t {{.REGISTRY}}/{{.APP}}:{{.TAG}} .
# Despliegue
deploy:
desc: "Desplegar en producción"
preconditions:
- sh: "[ '{{.ENV}}' != '' ]"
msg: "Define ENV=staging o ENV=produccion"
cmds:
- docker push {{.REGISTRY}}/{{.APP}}:{{.TAG}}
- kubectl set image deployment/{{.APP}} app={{.REGISTRY}}/{{.APP}}:{{.TAG}}
- kubectl rollout status deployment/{{.APP}}
Solución de Problemas
Task no encontrado: command not found
# Verificar que el binario está en el PATH
which task
echo $PATH
# Reinstalar en ubicación estándar
sudo sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b /usr/local/bin
Variables no se expanden correctamente
# Las variables de Taskfile usan {{.VAR}}, no $VAR
# $VAR se refiere a variables de entorno del sistema
# Verificar con --dry
task --dry mi-tarea
Tarea se ejecuta aunque no haya cambios
# Verificar que sources y generates están bien configurados
task --verbose mi-tarea
# Forzar re-ejecución ignorando cache
task --force mi-tarea
Error de precondición poco descriptivo
# Usar msg descriptivo en preconditions
# Y ejecutar con --verbose para más información
task --verbose deploy
Conclusión
Taskfile resuelve los problemas más comunes de Makefile: sintaxis intuitiva en YAML, soporte nativo de variables, dependencias declarativas y portabilidad entre sistemas operativos. Es especialmente útil para proyectos modernos que necesitan automatizar builds, pruebas y despliegues sin introducir la complejidad de una plataforma CI/CD completa. La modularidad con includes lo hace escalable para proyectos monorepo grandes.


