Je pense pouvoir maintenant remplacer Direnv par Mise đ€
Journal du jeudi 19 décembre 2024 à 17:09
Le 6 novembre 2024, j'ai publié la note "Le support des variables d'environments de Mise est limité, je continue à utiliser direnv".
L'issue indiquée dans cette note a été cloturée il y a 3 jours : Use mise tools in env template · Issue #1982.
Voici le contenu de changement de la documentation : https://github.com/jdx/mise/pull/3598/files#diff-e8cfa8083d0343d5a04e010a9348083f7b64035c407faa971074c6f0e8d0d940
Ce qui signifie que je vais pouvoir maintenant utiliser terraform installé via Mise dans le fichier .envrc :
[env]
_.source = { value = "./.envrc.sh", tools = true }
[tools]
terraform="1.9.8"
AprÚs avoir installé la derniÚre version de Mise, j'ai testé cette configuration dans repository suivant : install-and-configure-mise-skeleton.
Le fichier .envrc suivant a fonctionné :
export HELLO_WORLD=foo3
export NOW=$(date)
export TERRAFORM_VERSION=$(terraform --version | head -n1)
Exemple :
$ mise trust
$ echo $TERRAFORM_VERSION
Terraform v1.9.8
Je n'ai pas trouvé de commande Mise pour recharger les variables d'environnement du dossier courant, sans quitter le dossier. Avec direnv, pour effectuer un rechargement je lançais : direnv allow.
à la place, j'ai trouvé la méthode suivante :
$ source .envrc
Je pense pouvoir maintenant remplacer Direnv par Mise đ€.
Journaux liées à cette note :
Enlever des couches : mon chemin de Make vers de simples scripts Bash
Je profite d'une discussion entre deux amis au sujet de just et make pour partager mon point de vue et mes pratiques sur ce sujet.
Je tiens tout de suite Ă prĂ©ciser que c'est un sujet qui me tient Ă cĆur, parce qu'il m'irrite fortement : j'ai luttĂ© pendant des annĂ©es avec la mauvaise Developer eXperience de l'outil make dans mes projets, et je continue Ă voir tant de dĂ©veloppeurs s'entĂȘter Ă utiliser un outil dont la raison d'ĂȘtre est la rĂ©solution de dĂ©pendances basĂ©e sur les timestamps de fichiers â or, il me semble que cette fonctionnalitĂ© n'est probablement jamais utilisĂ©e, sauf dans les projets C ou C++.
Tout d'abord, je souhaite commencer par lister quelques éléments de complexité des makefile.
Quelques exemples de complexité accidentelle apportée par Make
- Chaque ligne est un sous-shell indépendant et ça c'est super pénible, exemple :
# Le "cd" n'a aucun effet sur la ligne suivante
broken-cd:
cd /tmp
ls # â liste le rĂ©pertoire original, pas /tmp
Une solution de contournement est d'utiliser des backslashes pour continuer la ligne, mais cela complique la lisibilité :
build:
cd /tmp && \
ls && \
echo "done"
- Par défaut, Make utilise
shet non pas bash ou zsh et ne supporte pas la construction[[ ]], les tableaux, etc qui cassent silencieusement. Exemple de code qui ne fonctionne pas :
check-env:
@if [[ -z "$(ENV)" ]]; then \
echo "ENV is not set"; \
exit 1; \
fi
@echo "ENV = $(ENV)"
- L'indentation du contenu des rules doit ĂȘtre une tabulation (pas des espaces)
- Les
$doivent ĂȘtre doublĂ©s pour le shell, sinon make l'interprĂšte, exemple :
greet:
@MSG="Hello $(APP_NAME)" && \ # $(APP_NAME) â rĂ©solu par make â
echo $$MSG # $$MSG â variable bash du sous-shell â
@echo $(MSG) # $(MSG) â make cherche "MSG" â vide ! â
# PiĂšge 3 : le $ doit ĂȘtre doublĂ© pour bash, sinon make l'interprĂšte
list:
@for i in 1 2 3; do echo $$i; done # â correct
@for i in 1 2 3; do echo $i; done # â make interprĂšte $i â vide
- Le préfixe "-" permet d'ignorer les erreurs d'une commande est une convention propre à makefile, sans équivalent dans Bash
clean:
-rm -rf build/ # sans "-", make s'arrĂȘte si build/ n'existe pas
-docker rmi $(APP_NAME)
- Par défaut, make affiche chaque commande avant de l'exécuter. Pour le supprimer, il faut préfixer chaque ligne avec
@:
build:
echo "Building..." # affiche : echo "Building..." puis : Building...
@echo "Building..." # affiche seulement : Building...
Du coup, dans la pratique, on se retrouve à préfixer toutes les lignes avec @ :
deploy:
@echo "Deploying..."
@docker build -t myapp .
@kubectl apply -f k8s/
- Nécessité d'ajouter des
.PHONY
Pourquoi tant de difficulté pour lancer de simples commandes ?
à chaque fois que je rencontrais des problÚmes avec make, je culpabilisais. Je me disais que c'était de ma faute, que tout le monde utilisait make et qu'il devait y avoir une bonne raison. Je voyais bien que mon expérience de développeur (DX) était mauvaise, que je n'avais pas besoin de résolution de dépendance⊠mais je me disais que je devais utiliser make, et que mon erreur était de ne pas avoir pris le temps de lire sa documentation.
Alors je replongeais rĂ©guliĂšrement dans les 16 chapitres de la documentation de make et je me demandais pourquoi je devais apprendre la syntaxe de make en plus de celle de bash. Et au final, je finissais mĂȘme par dĂ©tester Bash en plus de make.
Pourquoi tout cela était-il aussi compliqué, alors que je voulais seulement lancer de simples commandes et intégrer quelques conditions dans mes scripts ?
La recherche d'alternative
En 2018, la douleur des makefiles revenait souvent dans nos discussions en interne, au sein de mon équipe, et on cherchait réguliÚrement des alternatives. Parmi les pistes étudiées :
- Task, en Golang, apparu en 2017 â je l'ai testĂ© et ai fortement envisagĂ© de l'adopter
- Pydoit, en Python, démarré en 2008
- Rake, en Ruby, lancĂ© en 2003 â alors que je ne maĂźtrise pas le Ruby et que, par goĂ»t personnel, j'Ă©vite au maximum d'intĂ©grer ce type de projet dans mes stacks
- CMake, qu'un collÚgue avait exploré
Fin 2018, la prise de conscience
Fin 2018, je ne me souviens plus pour quelle raison, en parcourant le code source de Terraform, je suis tombé sur le dossier scripts/) de Terraform.
âââ ...
âââ Makefile
âââ ...
âââ scripts
â  âââ build.sh
â  âââ changelog-links.sh
â  âââ changelog.sh
â  âââ copyright.sh
â  âââ debug-terraform
â  âââ exhaustive.sh
â  âââ gofmtcheck.sh
â  âââ gogetcookie.sh
â  âââ goimportscheck.sh
â  âââ staticcheck.sh
â  âââ syncdeps.sh
â  âââ version-bump.sh
âââ ...
Et un fichier makefile minimaliste qui lance simplement des fichiers Bash :
$ cat Makefile
protobuf:
go run ./tools/protobuf-compile .
fmtcheck:
"$(CURDIR)/scripts/gofmtcheck.sh"
importscheck:
"$(CURDIR)/scripts/goimportscheck.sh"
staticcheck:
"$(CURDIR)/scripts/staticcheck.sh"
exhaustive:
"$(CURDIR)/scripts/exhaustive.sh"
[...snip...]
Et, ce jour-là , je me suis senti trÚs stupide d'avoir passé tant de temps à trouver une solution qui était en réalité trÚs simple, à portée de main !
Je pense aussi que le fait que cette méthode ait été utilisée par Mitchell Hashimoto en personne, dans Terraform, m'a probablement donné une sorte d'autorisation d'utiliser cette approche.
J'ai compris que je pouvais simplement me passer de make.
2019 Ă 2026 : utilisation de simples scripts Bash
Suite Ă ma prise de conscience de fin 2018, j'ai appliquĂ© un principe que je nomme "enlever des couches" : plutĂŽt que d'ajouter une technologie pour rĂ©soudre un problĂšme, rĂ©flĂ©chir Ă ce que peut enlever pour rĂ©duire la complexitĂ© â et, par la mĂȘme, peut-ĂȘtre supprimer le problĂšme lui-mĂȘme. C'est une vigilance consciente contre le biais cognitif du cargo cult : la tendance Ă reproduire des pratiques par habitude ou imitation, sans vraiment les comprendre ni les justifier.
En appliquant ce principe, il m'a semblĂ© que je pouvais simplement enlever make â sans avoir Ă le remplacer par un outil tel que Task qui aurait Ă©tĂ© une couche supplĂ©mentaire dont je n'avais probablement pas besoin.
J'ai mĂȘme pris conscience qu'en plaçant tous mes scripts dans un dossier ./scripts/, je bĂ©nĂ©ficiais nativement de l'autocomplĂ©tion de mes commandes par le filesystem â tout comme ce que proposait aussi make.
Par exemple :
make updevenait./scripts/up.shmake builddevenait./scripts/build.shmake cleandevenait./scripts/clean.sh- etc.
Et surtout, je pouvais désormais pleinement me concentrer sur ma maßtrise de Bash pour améliorer l'expérience de développeur (DX) de mes kits de développement.
L'astuce du cd automatique
Pour exécuter ces commandes sans se préoccuper du dossier courant, j'ai ajouté la ligne suivante au début de chaque script :
cd "$(dirname "$0")/../"
Cela permet de lancer ./scripts/up.sh depuis la racine du projet comme depuis un sous-répertoire (cd subproject && ../scripts/up.sh), et le script s'exécutera toujours depuis le dossier parent de scripts.
Voici le boilerplate code qu'utilise la quasi-totalité de mes scripts :
#!/usr/bin/env bash
set -e
cd "$(dirname "$0")/../"
...
Mais "make" est un standard ?
L'argument revient souvent : « make est un standard, tout le monde le connaßt, un nouveau contributeur saura immédiatement quoi faire. ».
Seulement voilĂ : la partie « standard » de make, celle que tout le monde utilise rĂ©ellement, c'est make <target> â et c'est exactement ce que fait ./scripts/<target>.sh, sans syntaxe supplĂ©mentaire, sans piĂšges de tabulations, sans rĂ©solution de dĂ©pendances par timestamps dont on ne veut probablement pas.
Il me semble que cet argument touche au cargo cult : on place un Makefile Ă la racine du projet par habitude, sans vraiment tirer parti des capacitĂ©s qui justifient l'existence mĂȘme de make.
De plus, si l'on parle de standard, bash est probablement au moins aussi universel que make. Et écrire un script bash est sans doute plus accessible pour un développeur que d'apprendre les subtilités du makefile ($ doublés, sous-shells, .PHONY, @, -, etc.).
Il me semble donc que l'argument du "standard" est lĂ©gitime â mais mon choix de ne plus utiliser make n'est pas un obstacle pour autant : si ./scripts/up.sh est clairement documentĂ© dans le README, je pense que n'importe quel dĂ©veloppeur comprendra sans difficultĂ© son usage et sa fonction. Pas besoin de connaĂźtre make pour exĂ©cuter un script bash dont le nom est explicite.
Retour d'expérience : 4 ans, de 2 à 10 développeurs
J'ai utilisé cette méthode avec succÚs pendant 4 ans, en passant de 2 à 10 développeurs, sans que j'aie constaté de friction. à ma connaissance, personne n'a eu de difficulté avec ce systÚme d'exécution des scripts et, il me semble, personne ne m'a suggéré de les remplacer par autre chose.
Et Just, alors ?
J'ai découvert just en 2022, puis je l'ai vu gagner en popularité à partir de 2023 (199 commentaires sur HackerNews) :

J'ai failli me laisser tenter. Mais je n'avais aucune douleur avec mes scripts, j'Ă©tais pleinement satisfait â et conformĂ©ment au principe d'enlever des couches, ajouter une couche supplĂ©mentaire n'avait aucun intĂ©rĂȘt.
D'autre part, just est riche en fonctionnalités et sa documentation est déjà importante : il me semble que c'est beaucoup à apprendre pour un outil dont je n'ai pas besoin.
Et puis j'ai craqué pour Mise Tasks
Je suis un grand utilisateur de Mise et derniĂšrement ce projet a ajoutĂ© la fonctionnalitĂ© Tasks. Et au grand dĂ©sespoir de mon ami Alexandre â qui me fait rĂ©guliĂšrement remarquer cette contradiction â, j'ai craquĂ©, j'ai commencĂ© Ă utiliser cette fonctionnalitĂ© en janvier 2026. Je n'ai pas d'argument solide Ă avancer ; sans doute un mĂ©lange de curiositĂ© et d'affection pour Mise.
Contrairement Ă just, la fonctionnalitĂ© task de Mise reste minimaliste et est compatible avec mon paradigme : le if dans l'exemple ci-dessous est du Bash standard â pas besoin de $$, de \, de sous-shells par ligne. J'Ă©cris du Bash et rien d'autre.
D'autre part, Mise est dĂ©jĂ au cĆur de mes development kit, je l'utilise Ă depuis 2023 Ă la place de Asdf pour installer du tooling de dĂ©veloppement. Depuis 1 an, j'ai remplacĂ© direnv par Mise. Par consĂ©quent, ce n'est pas une dĂ©pendance en plus Ă ajouter Ă mes projets.
Mise task supporte trois syntaxes pour définir des tasks.
Dans le fichier .mise.toml, en ligne simple :
[tasks.build]
run = "pnpm run build"
Ou en bloc multiligne :
[tasks.clean]
run = """
if [ "$1" = "--with-lint" ]; then
mise run lint
fi
pnpm run test
"""
Ou alors, via des scripts dans le dossier mise-tasks/, par exemple mise-tasks/build :
#!/usr/bin/env bash
#MISE description="Build the web application"
pnpm run build
Voici un extrait de Mise tasks mises en Ćuvre dans un vrai projet :
$ mise task
Name Description
build-cli Build the sklein-devbox CLI application
build-image Build the sklein-devbox container image
[...snip...]
up Start the devbox container
Le code source est consultable ici : https://github.com/stephane-klein/sklein-devbox/blob/main/.mise.toml
C'est important pour moi de prĂ©ciser que j'ai bien conscience que Mise Tasks est une couche de plus â et que ça contredit ma doctrine « enlever des couches ».
Dans un projet d'Ă©quipe, je partirais par dĂ©faut sur des scripts Bash simples, sans Mise task. Je n'intĂ©grerais Mise task que s'il y a un consensus fort de l'Ă©quipe â et je ne l'imposerais pas.
Remerciements
Je remercie mes deux amis de m'avoir motivĂ© Ă Ă©crire cette note â c'est un sujet que je souhaitais traiter depuis 2019 (j'avais mĂȘme créé une issue Ă ce sujet dans mon ancien backlog).
Journal du mardi 25 février 2025 à 14:20
Alexandre vient de partager ce thread : « Asdf Version Manager Has Been Re-Written in Golang »
Je découvre que Asdf n'est pas mort ! La version 0.16.0 publié le 30 janvier 2025 a été réécrite en Golang !
La raison principale semble ĂȘtre une volontĂ© d'amĂ©lioration de la vitesse de Asdf :
With improvements ranging from 2x-7x faster than asdf version 0.15.0!
Depuis cette date, Mise a publié un benchmark qui compare la vitesse d'exécution de Asdf et Mise : https://mise.jdx.dev/dev-tools/comparison-to-asdf.html#asdf-in-go-0-16.
Comme mon ami Alexandre, certains utilisateurs sont inquiets de voir Mise faire trop de choses :
I tried mise a while back, and the main reason I went away from it is like you said, it does too much. It tries to be asdf, direnv and a task runner. I just want a tool manager, and is the reason why I switched to aquaproj/aqua.
J'ai migré de Asdf vers Mise en novembre 2023 et pour le moment, je n'ai pas envie, ni de raison pratique particuliÚre pour retourner à Asdf.
De plus, je suis plutĂŽt satisfait d'avoir remplacĂ© direnv par Mise, voir Je pense pouvoir maintenant remplacer Direnv par Mise đ€.
Le support des variables d'environments de Mise est limité, je continue à utiliser direnv
J'ai décidé d'utiliser la fonctionnalité "mise
env._source" pendant quelques semaines pour me faire une opinion.-- (from)
J'ai déjà rencontré un problÚme : il n'est pas possible de lancer des outils installés via Mise dans un script lancé par _.source.
Voici un exemple, j'ai ce fichier .envrc.sh :
export SERVER1_IP=$(terraform output --json | jq -r '.server1_public_ip | .value')
Terraform est installé avec Mise :
[env]
_.source = "./.envrc.sh"
[tools]
terraform="1.9.8"
Quand je lance mise set, j'ai cette erreur :
$ mise set
.envrc.sh: line 6: terraform: command not found
J'ai trouvé une issue pour traiter cette limitation : "Use mise tools in env template".
En attendant que cette issue soit implémentée, j'ai décidé de continuer d'utiliser direnv.
2024-12-19 : suite Ă la cloture de l'issue indiquĂ©e dans cette note, j'ai publiĂ© la note Je pense maintenant pouvoir remplacer Direnv par Mise đ€.