guest@6admin.io:~$

mise: un seul binaire pour toutes les versions d'outils

Chaque projet traîne sa collection de versions: le repo A veut Node 20, le repo B tient encore sur Node 18, le C tourne sur Python 3.11 quand le D exige déjà 3.12 et terraform, hugo et go suivent la même loi. Installer ces versions à l’échelle de la machine revient à en concasser une par-dessus l’autre: la dernière gagne et tous les autres repos cassent en silence. J’ai pris ce problème par le bon bout en 2024 avec mise : un gestionnaire de versions d’outils par projet, tenu par un seul binaire Rust.

Ce que c’est

mise est ce qu’asdf aurait dû être dès le départ: un binaire statique, sans dépendance, qu’on dépose dans le PATH et qui gère les versions par dossier. Chaque repo déclare ses outils dans un fichier versionné et, quand vous entrez dans le dossier, la version déclarée s’active. Deux mécanismes au choix: les shims (un exécutable par outil dans un répertoire dédié, qui consulte le contexte au moment de l’exécution) ou l’activation du shell (une fonction qui réécrit le PATH à chaque changement de dossier). Dans les deux cas, la promesse est la même: le bon outil, la bonne version, au bon endroit, sans cérémonie.

La compatibilité asdf est le détail qui tue: mise lit les .tool-versions et l’écosystème de plugins asdf reste utilisable pour les outils exotiques. Comme le noyau est du Rust compilé, la résolution de versions est quasi instantanée et les outils courants sont gérés nativement par le core, installés depuis les releases officielles des projets.

Pourquoi pas asdf

asdf a inventé le modèle et je ne crache pas dessus: c’est le grand-père dont tout le monde s’inspire. Mais le modèle est né shell-scripté: un plugin par outil, chaque plugin en bash, chaque activation qui traverse la pile de plugins. Sur une machine chargée, le prompt payait la note à chaque ouverture de shell. mise garde la sémantique (un fichier de versions, des plugins, des shims) et la fait exécuter par un binaire compilé: la couche de plugins reste disponible, les outils courants ne passent plus par elle et l’activation ne coûte plus rien.

Dans un repo

Le fichier parle de lui-même:

# .tool-versions
node 20.17.0
python 3.12.4

Commité, il suit le repo: un clone neuf lance mise install et tombe sur les bonnes versions, un npm install pour la toolchain. Les défauts de la machine vivent dans la config globale de mise, les exigences du projet dans son propre fichier. La règle que j’applique: tout ce que le CI invoque doit être déclaré dans le fichier de versions du repo, jamais dans l’état global de la machine. Le fichier est le contrat, la machine n’est que le carnet d’exécution.

La part honnête

En septembre 2024, l’outil est jeune: il s’appelait rtx encore au printemps (le renommage date d’avril), les releases roulent vite et cassent parfois un cas limite, certains backends restent capricieux et la documentation est dense au point de frôler le labyrinthe. Le modèle shim a ses coins: un shim vers un outil non installé produit une erreur qui ne dit pas toujours son nom. Rien de rédhibitoire, mais c’est un outil qui bouge plus vite qu’il ne documente.

Le mot de la fin

  • Les versions d’outils sont de la donnée de projet, pas de l’état machine. Le fichier qui les déclare vit dans le repo.
  • Un binaire compilé bat une pile de scripts bash pour la même sémantique. Même modèle qu’asdf, sans la taxe de démarrage.
  • La compatibilité .tool-versions est le pont. L’écosystème asdf reste utilisable; on migre outil par outil.
  • Testez ce que la migration casse. Un renommage d’outil aussi frais amène son lot de surprises; réjouissez-vous qu’elles arrivent avant le CI.

[edit: 2026] Deux ans plus tard, mise est devenu bien plus qu’un gestionnaire de versions: le fichier mise.toml déclare aussi les variables d’environnement et les tâches (build, test, lint), le support Windows est natif, l’installation passe par scoop ou winget et c’est lui qui épine Hugo dans ce blog via le .tool-versions. L’histoire côté Windows est racontée dans une gestion de paquets propre sous Windows .