# Taskfile — Automation Patterns > Canonical Taskfile boilerplate for André Bassi's projects. Task names are the project's public API; scripts/ is the audit trail. Every project ships a Taskfile.yaml from day zero. Point an LLM here and it writes Taskfiles exactly in this pattern. ## Hard Rules 1. `version: '3'` always. 2. NEVER ad-hoc shell. Logic lives in `scripts/.sh` (`#!/bin/bash`, `set -euo pipefail`); task wraps the script. 3. Every command logs: `2>&1 | tee /tmp/-.log`. 4. Every task has `desc:` — `task -l` must read like documentation. Usage hint in desc when args: `'Uso: task ses:dkim -- dominio.com'`. 5. Tests gate build/deploy: `build` and `deploy` declare `deps: [test]`. 6. Destructive/production tasks carry `prompt:` confirmation (`prd:apply`, `*:destroy`). 7. Internal helpers: prefix `_` + `internal: true` (hidden from `task -l`). 8. Namespacing with colon by domain: `dev:*`, `test:*`, `build:*`, `deploy:*`, `fly:*`, `supabase:*`, `migrate:*`, `vpn:*`, `logs:*`, `status:*`. ## Canonical Namespace Taxonomy dev dev:api dev:worker web:dev # run locally test test:backend test:frontend test:coverage lint build build:all supabase:start supabase:stop supabase:reset supabase:psql temporal:up temporal:down temporal:status migrate migrate:local deploy deploy:backend deploy:frontend deploy:worker fly:status fly:logs logs:fly status:fly health docker:up docker:down vpn:up vpn:down vpn:status # infra repos plan apply dev:plan dev:apply prd:apply # IaC repos scripts:deploy ssh # remote-host repos ## Anatomy — top level version: '3' vars: APP_NAME: duxcare BACKEND_DIR: backend VERSION: sh: bash scripts/version.sh # computed at runtime env: CGO_ENABLED: 0 # global env AWS_PROFILE: tinnova-crava-admin TG_TF_PATH: tofu includes: # monorepo composition frontend: { taskfile: ./frontend, dir: ./frontend } backend: { taskfile: ./backend, dir: ./backend } tasks: ... ## Anatomy — task level dev: desc: Rodar servidor em modo dev deps: [supabase:start] # ordered prerequisites dir: '{{.BACKEND_DIR}}' # run in subdir dotenv: ['{{.BACKEND_DIR}}/.env.develop'] env: APP_ENV: development # task-scoped env cmds: - go run ./cmd/server 2>&1 | tee /tmp/{{.APP_NAME}}-dev.log ## CLI Args ({{.CLI_ARGS}}) apply: desc: 'Apply componente. Uso: task apply -- env/dev/vpc' cmds: - terragrunt apply --working-dir {{.CLI_ARGS}} 2>&1 | tee /tmp/tg-apply.log ses:dkim: desc: 'Publica DKIM. Uso: task ses:dkim -- dominio.com' cmds: - ./scripts/ses-dkim.sh {{.CLI_ARGS | default "dominio-padrao.com.br"}} - Invocation: `task apply -- env/dev/vpc`, `task rds:start -- dev qas`, `task vpn:up -- prd`. ## Gates & Safety build: desc: Compilar binario deps: [test] # test failure blocks build cmds: [./scripts/build.sh 2>&1 | tee /tmp/build.log] deploy: desc: Deploy completo deps: [test] cmds: - task: deploy:backend # task chaining (sequential) - task: deploy:frontend prd:apply: desc: Apply em producao prompt: "PRODUCAO: confirma apply em prd?" dir: env/prd cmds: [...] dev:destroy: prompt: "CUIDADO: destruir TUDO do ambiente dev?" cmds: [...] ## Idempotency supabase:start: desc: Iniciar Supabase local cmd: supabase start status: - supabase status > /dev/null 2>&1 # skip if already running web:build: sources: ['web/**/*.{ts,tsx,css}'] # rebuild only on change generates: ['web/dist/**/*'] cmds: [pnpm build] ## CI Aggregate ci: desc: Pipeline CI completo cmds: - task: lint - task: test:backend - task: test:frontend - task: build ## Remote-Host Pattern (SSH repos: picoclaw, itlab) vars: SSH_CMD: timeout 30s ssh status: desc: Status do service remoto cmds: - '{{.SSH_CMD}} "systemctl status --no-pager" 2>&1 | tee /tmp/-status.log' logs: cmds: - '{{.SSH_CMD}} "journalctl -u --no-pager -n 50" 2>&1 | tee /tmp/-logs.log' scripts:deploy: desc: Deploy scripts para o servidor cmds: - ./scripts/deploy.sh 2>&1 | tee /tmp/-deploy.log ## Internal Helpers _version:bump: internal: true cmds: - | CUR=$(cat VERSION | tr -d '[:space:]') IFS=. read -r MA MI PA <<< "$CUR" echo "$MA.$MI.$((PA+1))" > VERSION ## Antipatterns (never do) - Task without `desc:` — kills `task -l` discoverability. - Logic inline no Taskfile em vez de `scripts/` — sem audit trail, sem reuso. - Output sem `tee` — log perdido ao terminar. - Paths hardcoded em vez de `vars` + `dir:`. - Deploy sem `deps: [test]`. - Destroy/prod sem `prompt:`. - Shell complexo escapado inline — usar bloco literal YAML `|` ou script. ## Default Project Skeleton Taskfile.yaml # version 3, vars, namespaces acima scripts/ ├── build.sh ├── deploy.sh └── version.sh