Fatigué des Makefiles illisibles et de leur syntaxe à tabulations ? Task offre une alternative moderne en YAML : lisible, portable, et conçue pour le DevOps.
Task (ou Taskfile) est un outil d’automatisation écrit en Go. Il exécute des tâches définies dans un fichier Taskfile.yml en respectant les dépendances, avec variables dynamiques, cache intelligent et support multi-fichiers. Un seul binaire, zéro dépendance.
Ce guide est fait pour vous si…
Section intitulée « Ce guide est fait pour vous si… »Pourquoi Task plutôt que Make ?
Section intitulée « Pourquoi Task plutôt que Make ? »Make existe depuis 1976 et reste un excellent outil. Mais sa syntaxe a vieilli et peut être déroutante pour les débutants (tabulations obligatoires, variables cryptiques). Task a été conçu pour résoudre ces irritants :
| Critère | Make | Task |
|---|---|---|
| Syntaxe | Propre à Make, tabulations obligatoires | YAML standard, indentation libre |
| Variables | $(VAR) et $@, $<, $^ | {{.VAR}} (templates Go) |
| Installation | Préinstallé Linux/macOS | Binaire unique, multi-plateforme |
| Cache intelligent | Basé sur dates de fichiers | sources/generates + checksum |
| Multi-fichiers | include basique | includes avec namespaces |
| Boucles | Shell uniquement | Natif (for, matrix) |
| Watch mode | Externe (inotifywait) | Intégré (--watch) |
| Documentation | Commentaires manuels | desc → task --list auto |
Installation
Section intitulée « Installation »La méthode recommandée — installe le binaire unique dans ~/.local/bin :
sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b ~/.local/binAjoutez ~/.local/bin à votre PATH si ce n’est pas déjà fait :
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc && source ~/.bashrc# Avec Homebrewbrew install go-task
# Ou avec le script universelsh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b ~/.local/bin# Avec Chocolateychoco install go-task
# Avec Scoopscoop install task
# Ou télécharger le binaire depuis GitHub Releases# Debian/Ubuntu (snapd)snap install task --classic
# Arch Linux (AUR)yay -S go-task-bin
# Fedora (COPR)dnf copr enable atim/go-task && dnf install go-task
# npm (si Node.js disponible)npm install -g @go-task/cliVérification
Section intitulée « Vérification »task --versionSortie attendue :
Task version: v3.48.0Comprendre la structure d’un Taskfile
Section intitulée « Comprendre la structure d’un Taskfile »Un Taskfile est un fichier YAML qui définit des tâches (tasks). Chaque tâche contient des commandes (cmds) à exécuter.
Premier Taskfile
Section intitulée « Premier Taskfile »Créez un fichier Taskfile.yml avec task --init :
task --initContenu généré :
version: '3'
vars: GREETING: Hello, World!
tasks: default: cmds: - echo "{{.GREETING}}" silent: trueExécution :
taskSortie :
Hello, World!Sans argument, Task exécute la tâche default. L’option silent: true masque la commande (n’affiche que la sortie).
Anatomie d’une tâche
Section intitulée « Anatomie d’une tâche »Une tâche Task se compose de plusieurs attributs optionnels qui contrôlent son comportement. Contrairement à Make où tout est implicite, Task rend chaque aspect explicite — vous voyez immédiatement ce que fait une tâche en lisant sa définition.
Voici une tâche typique avec tous les attributs courants :
version: '3'
tasks: build: desc: Compile l'application # Description (visible dans --list) deps: [lint, test] # Dépendances (exécutées avant) vars: APP_NAME: myapp # Variables locales cmds: - echo "Building {{.APP_NAME}}" # Commandes à exécuter silent: false # Afficher les commandes (défaut)Chaque attribut a un rôle précis. Le tableau suivant résume les plus utilisés :
| Attribut | Rôle | Obligatoire |
|---|---|---|
cmds | Liste des commandes shell à exécuter | Oui |
desc | Description affichée par task --list | Non (mais recommandé) |
deps | Tâches à exécuter avant celle-ci | Non |
vars | Variables locales à la tâche | Non |
silent | Masquer les commandes (true/false) | Non |
Lister les tâches
Section intitulée « Lister les tâches »task --listSortie :
task: Available tasks for this project:* build: Compile l'application* default: Tâche par défaut* lint: Vérifie le code* test: Lance les testsSeules les tâches avec desc sont listées. Pour masquer une tâche de la liste, utilisez internal: true.
Variables
Section intitulée « Variables »Les variables rendent vos Taskfiles réutilisables et configurables. Elles utilisent la syntaxe Go templates : {{.NOM_VARIABLE}}.
Variables statiques
Section intitulée « Variables statiques »Définies au niveau global (section vars: racine) ou par tâche :
version: '3'
vars: APP_NAME: myapp VERSION: "1.0.0"
tasks: build: cmds: - echo "Building {{.APP_NAME}} v{{.VERSION}}"Variables dynamiques
Section intitulée « Variables dynamiques »Évaluées au moment de l’exécution via le mot-clé sh:. La commande shell est exécutée et son résultat devient la valeur de la variable :
version: '3'
vars: GIT_COMMIT: sh: git rev-parse --short HEAD TIMESTAMP: sh: date +%Y%m%d-%H%M%S
tasks: build: cmds: - echo "Commit {{.GIT_COMMIT}} at {{.TIMESTAMP}}"Sortie :
task: [build] echo "Commit a1b2c3d at 20260210-142530"Commit a1b2c3d at 20260210-142530Variables d’environnement (.env)
Section intitulée « Variables d’environnement (.env) »Chargez automatiquement les fichiers .env avec le mot-clé dotenv:. Task les lit dans l’ordre spécifié — la dernière valeur lue l’emporte :
version: '3'
dotenv: ['.env', '.env.local']
tasks: show-config: cmds: - echo "API_KEY = {{.API_KEY}}" - echo "DATABASE_URL = {{.DATABASE_URL}}"Fichier .env :
API_KEY=secret123DATABASE_URL=postgres://localhost:5432/mydbSurcharge en ligne de commande
Section intitulée « Surcharge en ligne de commande »task build VERSION=2.0.0La variable VERSION passe de 1.0.0 à 2.0.0 pour cette exécution uniquement. Les variables CLI ont priorité sur toutes les autres sources.
Variables d’environnement système
Section intitulée « Variables d’environnement système »Passez des variables d’environnement à vos commandes avec le mot-clé env: :
version: '3'
tasks: deploy: env: DEPLOY_ENV: production LOG_LEVEL: info cmds: - ./deploy.shDépendances entre tâches
Section intitulée « Dépendances entre tâches »Les dépendances (deps) garantissent un ordre d’exécution. Task les exécute en parallèle dès que possible pour gagner du temps.
Dépendances simples
Section intitulée « Dépendances simples »version: '3'
tasks: lint: cmds: - echo "Linting..."
test: cmds: - echo "Testing..."
build: desc: Compile l'application deps: [lint, test] cmds: - echo "Building..."Exécution :
task buildSortie :
task: [lint] echo "Linting..."Linting...task: [test] echo "Testing..."Testing...task: [build] echo "Building..."Building...lint et test s’exécutent en parallèle, puis build s’exécute.
Appel séquentiel dans cmds
Section intitulée « Appel séquentiel dans cmds »Pour un contrôle séquentiel (une tâche après l’autre), appelez les tâches dans cmds avec la syntaxe task: :
version: '3'
tasks: deploy: cmds: - task: build - task: push - task: notifyIci, build → push → notify s’exécutent dans l’ordre, un à la fois.
Dépendances avec variables
Section intitulée « Dépendances avec variables »Passez des variables aux tâches appelées via le bloc vars: :
version: '3'
tasks: greet: cmds: - echo "Hello, {{.NAME}}!"
greet-all: cmds: - task: greet vars: NAME: Alice - task: greet vars: NAME: BobTask vs Make : tableau de correspondance
Section intitulée « Task vs Make : tableau de correspondance »Si vous connaissez déjà Make, vous retrouverez des concepts similaires dans Task — mais avec une syntaxe différente. La bonne nouvelle : Task est plus explicite, donc plus facile à comprendre une fois la correspondance établie.
Le piège principal : oublier que Task n’a pas de variables automatiques ($@, $<, $^). En Task, tout est explicite — ce qui rend le code plus lisible au prix d’un peu plus de verbosité.
Voici les équivalences les plus courantes :
| Make | Task | Notes |
|---|---|---|
$(VAR) / ${VAR} | {{.VAR}} | Syntaxe Go template |
$@ (cible) | Pas d’équivalent direct | Utilisez une variable explicite |
$< (première dépendance) | Pas d’équivalent direct | Utilisez une variable |
$^ (toutes les dépendances) | Pas d’équivalent direct | Listez explicitement |
VAR := $(shell cmd) | VAR: { sh: cmd } | Variable dynamique |
target: dep1 dep2 | deps: [dep1, dep2] | Exécution parallèle |
.PHONY: target | Automatique | Toutes les tâches sont “phony” |
include file.mk | includes: { ns: file.yml } | Avec namespace |
@echo "msg" | silent: true | Au niveau de la tâche |
make -n | task --dry | Mode dry-run |
make -j4 | Parallèle par défaut | Via dépendances |
Exemple de migration :
# MakefileVERSION := $(shell git describe --tags)build:go build -ldflags="-X main.Version=$(VERSION)" -o bin/appversion: '3'
vars: VERSION: sh: git describe --tags 2>/dev/null || echo "dev"
tasks: build: cmds: - go build -ldflags="-X main.Version={{.VERSION}}" -o bin/appCas d’usage DevOps
Section intitulée « Cas d’usage DevOps »La théorie c’est bien, la pratique c’est mieux. Cette section présente des Taskfiles complets et testés que vous pouvez copier-coller dans vos projets. Chaque exemple suit les bonnes pratiques : variables dynamiques, dépendances explicites, et descriptions pour task --list.
Workflow Docker
Section intitulée « Workflow Docker »Un workflow Docker typique comprend trois étapes : construire l’image, la pousser vers un registry, et la lancer localement pour tester. Ce Taskfile automatise ces trois étapes avec un tag basé sur le dernier commit Git.
version: '3'
vars: REGISTRY: ghcr.io/myorg IMAGE_NAME: myapp VERSION: sh: git describe --tags 2>/dev/null || echo "dev" IMAGE: "{{.REGISTRY}}/{{.IMAGE_NAME}}:{{.VERSION}}"
tasks: docker:build: desc: Build l'image Docker cmds: - docker build -t {{.IMAGE}} .
docker:push: desc: Push l'image vers le registry deps: [docker:build] cmds: - docker push {{.IMAGE}}
docker:run: desc: Lance le conteneur localement cmds: - docker run --rm -p 8080:8080 {{.IMAGE}}Workflow Python avec Poetry
Section intitulée « Workflow Python avec Poetry »Pour un projet Python, le workflow classique enchaîne installation des dépendances, linting, tests, puis packaging. Ce Taskfile utilise Poetry (gestionnaire de dépendances moderne) et ruff (linter ultra-rapide). Les dépendances entre tâches garantissent que test lance d’abord lint, et que build lance d’abord test.
version: '3'
tasks: install: desc: Installe les dépendances cmds: - poetry install
lint: desc: Vérifie le code avec ruff cmds: - poetry run ruff check .
format: desc: Formate le code avec ruff cmds: - poetry run ruff format .
test: desc: Lance les tests avec pytest deps: [lint] cmds: - poetry run pytest -v
build: desc: Construit le package deps: [test] cmds: - poetry buildIntégration GitLab CI
Section intitulée « Intégration GitLab CI »L’avantage majeur de Task en CI/CD : les mêmes commandes fonctionnent partout. Fini les scripts de CI différents de ce que vous lancez en local. Si task test passe sur votre machine, il passera dans GitLab CI.
Voici comment intégrer Task dans un pipeline GitLab. Installez Task dans votre image Docker de base, puis appelez simplement les tâches :
stages: - test - build
test: stage: test script: - task test
build: stage: build script: - task docker:build - task docker:pushFonctionnalités avancées
Section intitulée « Fonctionnalités avancées »Les bases couvertes, explorons les fonctionnalités qui font de Task un outil puissant pour les projets complexes. Ces fonctionnalités ne sont pas indispensables pour démarrer, mais elles deviennent précieuses à mesure que votre projet grandit.
Includes : organiser en multi-fichiers
Section intitulée « Includes : organiser en multi-fichiers »Quand un Taskfile dépasse 200 lignes, il devient difficile à maintenir. La solution : découper en plusieurs fichiers, un par domaine (Docker, Kubernetes, tests…). Chaque fichier inclus reçoit un namespace — un préfixe qui évite les conflits de noms.
Exemple concret : un projet avec des tâches Docker et Kubernetes. Plutôt qu’un fichier monolithique, on sépare en trois fichiers :
version: '3'
includes: docker: taskfile: ./taskfiles/Docker.yml dir: . k8s: taskfile: ./taskfiles/Kubernetes.yml vars: NAMESPACE: productionversion: '3'
tasks: build: desc: Build Docker image cmds: - docker build -t myapp .Les tâches incluses sont appelées avec leur namespace en préfixe :
task docker:buildtask k8s:deployBoucles for
Section intitulée « Boucles for »Parfois vous devez exécuter la même commande sur plusieurs éléments : plusieurs fichiers à lint, plusieurs personnes à notifier, plusieurs services à redémarrer. Plutôt que de dupliquer les tâches, utilisez les boucles for.
Task supporte trois types de boucles :
- Sur une liste :
for: { var: MA_LISTE }— itère sur les mots d’une variable - Sur une matrice :
for: { matrix: ... }— génère toutes les combinaisons - Sur des fichiers : variable dynamique avec
findouls
Voici des exemples concrets :
version: '3'
tasks: greet: desc: Salue plusieurs personnes vars: NAMES: Alice Bob Charlie cmds: - for: { var: NAMES } cmd: echo "Bonjour, {{.ITEM}}!"
lint-configs: desc: Lint tous les fichiers YAML cmds: - for: { var: YAML_FILES } cmd: yamllint {{.ITEM}} vars: YAML_FILES: sh: find . -name "*.yml" -type fSortie de task greet :
task: [greet] echo "Bonjour, Alice!"Bonjour, Alice!task: [greet] echo "Bonjour, Bob!"Bonjour, Bob!task: [greet] echo "Bonjour, Charlie!"Bonjour, Charlie!Matrix : builds multi-plateforme
Section intitulée « Matrix : builds multi-plateforme »La matrice est une boucle sur toutes les combinaisons de plusieurs listes. C’est idéal pour les builds cross-platform : au lieu d’écrire 6 tâches pour linux/amd64, linux/arm64, darwin/amd64, darwin/arm64, windows/amd64, windows/arm64… vous définissez une matrice OS × ARCH et Task génère toutes les combinaisons.
Attention : le nombre de combinaisons explose vite. 3 OS × 2 architectures = 6 builds. Ajoutez 3 versions de Go = 18 builds. Utilisez avec parcimonie.
version: '3'
tasks: build-all: desc: Build pour toutes les plateformes cmds: - for: matrix: OS: [linux, darwin, windows] ARCH: [amd64, arm64] cmd: | echo "Building for {{.ITEM.OS}}-{{.ITEM.ARCH}}" GOOS={{.ITEM.OS}} GOARCH={{.ITEM.ARCH}} go build -o bin/app-{{.ITEM.OS}}-{{.ITEM.ARCH}}Cache intelligent avec sources/generates
Section intitulée « Cache intelligent avec sources/generates »Recompiler un projet qui n’a pas changé est une perte de temps. Task résout ce problème avec sources et generates : vous déclarez quels fichiers sont lus (sources) et quels fichiers sont produits (generates). Si aucune source n’a changé depuis la dernière exécution, Task affiche “Task is up to date” et ne fait rien.
Comment ça marche ? Task calcule un checksum des fichiers sources. Si le checksum n’a pas changé depuis la dernière exécution réussie, la tâche est considérée “à jour”. C’est plus fiable que la comparaison de dates de Make.
version: '3'
tasks: build: desc: Compile uniquement si les sources ont changé sources: - src/**/*.go - go.mod - go.sum generates: - bin/myapp cmds: - go build -o bin/myapp ./...Première exécution :
task build# → Compile le projetDeuxième exécution (sans modification) :
task build# → task: Task "build" is up to dateModification d’un fichier :
touch src/main.gotask build# → RecompileWatch mode
Section intitulée « Watch mode »Le mode watch surveille les fichiers sources et relance automatiquement la tâche quand quelque chose change. C’est l’équivalent de nodemon pour Node.js ou cargo watch pour Rust — mais intégré à Task.
Prérequis : la tâche doit avoir un attribut sources défini (sinon Task ne sait pas quoi surveiller).
task --watch buildTask reste en foreground et surveille les fichiers. Dès qu’un fichier source change (sauvegarde dans votre éditeur), Task relance la tâche. Appuyez sur Ctrl+C pour arrêter.
Cas d’usage typique : pendant le développement, lancez task --watch test dans un terminal. Chaque modification de code relance les tests automatiquement. Vous avez un feedback instantané sans quitter votre éditeur.
Préconditions
Section intitulée « Préconditions »Certaines tâches ne doivent s’exécuter que si des conditions sont remplies : fichier présent, variable définie, état Git propre… Plutôt que d’ajouter des if dans vos scripts, utilisez preconditions. Si une précondition échoue, Task affiche un message clair et s’arrête avant d’exécuter les commandes.
Analogie : c’est comme le contrôle de sécurité avant le décollage d’un avion. On vérifie tout avant de démarrer les moteurs, pas pendant le vol.
version: '3'
tasks: deploy: desc: Déploie en production preconditions: - sh: test -f bin/myapp msg: "Binaire non trouvé. Lancez 'task build' d'abord." - sh: '[ "$(git status --porcelain)" = "" ]' msg: "Working directory non propre. Committez vos changements." cmds: - ./deploy.shSi une précondition échoue :
task: Binaire non trouvé. Lancez 'task build' d'abord.task: Failed to run task "deploy": task: precondition not metVariables obligatoires (requires)
Section intitulée « Variables obligatoires (requires) »Parfois une tâche a besoin de certaines variables pour fonctionner — un nom de registry, un tag d’image, une clé API. Plutôt que d’échouer avec une erreur cryptique au milieu de l’exécution, déclarez ces variables comme obligatoires avec requires. Task vérifie leur présence avant de lancer la moindre commande.
version: '3'
tasks: docker:push: desc: Push vers le registry requires: vars: [REGISTRY, IMAGE_TAG] cmds: - docker push {{.REGISTRY}}/myapp:{{.IMAGE_TAG}}Appel sans les variables :
task docker:push# → task: Task "docker:push" cancelled because it is missing required variables: REGISTRY, IMAGE_TAGAppel correct :
REGISTRY=ghcr.io IMAGE_TAG=v1.0 task docker:pushCibler une plateforme
Section intitulée « Cibler une plateforme »Certaines tâches n’ont de sens que sur un OS spécifique : apt-get sur Linux, brew sur macOS, choco sur Windows. Avec platforms, vous déclarez où une tâche peut s’exécuter. Sur une autre plateforme, Task la skip silencieusement.
Cas d’usage : un Taskfile partagé entre développeurs Linux et macOS. Chacun peut lancer task install-deps — seules les commandes compatibles s’exécutent.
version: '3'
tasks: install-deps: platforms: [linux, darwin] cmds: - apt-get install -y build-essential || brew install gcc
windows-only: platforms: [windows] cmds: - choco install makeDépannage
Section intitulée « Dépannage »Même avec un outil simple comme Task, des problèmes peuvent survenir. Cette section recense les erreurs les plus fréquentes et leurs solutions. Si votre problème n’est pas listé, lancez task --verbose pour obtenir plus de détails.
| Problème | Cause probable | Solution |
|---|---|---|
task: command not found | Task pas dans le PATH | export PATH="$HOME/.local/bin:$PATH" |
task: No Taskfile found | Mauvais répertoire ou nom de fichier | Vérifiez : Taskfile.yml, taskfile.yml ou Taskfile.yaml |
yaml: line X: mapping values not allowed | Indentation YAML incorrecte | Utilisez 2 espaces, pas de tabulations |
Variable non remplacée ({{.VAR}} affiché tel quel) | Variable non définie | Vérifiez l’orthographe et la portée de la variable |
| Tâche exécutée alors qu’elle devrait être à jour | sources mal configuré | Vérifiez les patterns glob (ex: **/*.go) |
| Dépendances exécutées dans le mauvais ordre | deps exécute en parallèle | Utilisez cmds: [task: X] pour du séquentiel |
À retenir
Section intitulée « À retenir »Si vous ne devez retenir que l’essentiel de ce guide :
-
Task = Make moderne en YAML. Plus lisible, plus portable, conçu pour le DevOps.
-
task --initcrée un Taskfile minimal. Partez de là et enrichissez. -
Les variables utilisent la syntaxe Go templates :
{{.VAR}}. Dynamiques avecsh:. -
deps= parallèle,cmds: [task: X]= séquentiel. Choisissez selon votre besoin. -
sources/generatesactive le cache intelligent. Task skip les tâches si les sources n’ont pas changé. -
includesavec namespaces organise les gros projets :task docker:build,task k8s:deploy. -
--watchrelance automatiquement une tâche quand les fichiers changent. Idéal pour le dev. -
Même commande en local et en CI.
task testfonctionne partout — finies les surprises.