<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title>ligne de commande - Tag - Jordan Chapuy – IA, tech et produit, simplement</title><link>https://jordanchapuy.com/tags/ligne-de-commande/</link><description>ligne de commande - Tag - Jordan Chapuy – IA, tech et produit, simplement</description><generator>Hugo -- gohugo.io</generator><language>fr</language><lastBuildDate>Wed, 11 Nov 2026 08:30:00 +0100</lastBuildDate><atom:link href="https://jordanchapuy.com/tags/ligne-de-commande/" rel="self" type="application/rss+xml"/><item><title>C'est quoi un CLI ? La machine à café sans boutons que votre agent IA adore</title><link>https://jordanchapuy.com/posts/2026/11/c-est-quoi-un-cli/</link><pubDate>Wed, 11 Nov 2026 08:30:00 +0100</pubDate><author>Jordan Chapuy</author><guid>https://jordanchapuy.com/posts/2026/11/c-est-quoi-un-cli/</guid><description><![CDATA[<p>Un écran noir, un curseur qui clignote, et des lignes de texte qui défilent. Dans les films, c&rsquo;est l&rsquo;écran du hacker. Dans la vraie vie, c&rsquo;est simplement un terminal. Il fait peur au commun des mortels, mais pourtant, il n&rsquo;a rien d&rsquo;insurmontable. Au lieu d&rsquo;avoir des boutons et des menus, on a seulement du texte.</p>
<p>Moi, j&rsquo;y passe pas mal de temps. En tant que développeur, j&rsquo;ai utilisé un tas d&rsquo;outils en ligne de commande. Quand on administre un serveur, par exemple, il n&rsquo;y a souvent aucune interface graphique : on s&rsquo;y connecte à distance (en <code>ssh</code>, je glisse un petit mot au passage pour la culture), et tout se fait en tapant des commandes.</p>
<p>Cet écran qui intimide tant est devenu le terrain de jeu préféré des agents IA. Dans <a href="https://jordanchapuy.com/posts/2026/11/c-est-quoi-un-mcp-model-context-protocol/" rel="">l&rsquo;article sur les MCP</a>, je disais d&rsquo;ailleurs que je passe désormais surtout par des <strong>CLI</strong>. Il est temps d&rsquo;expliquer ce que c&rsquo;est.</p>
<h2 id="pourquoi-connecter-son-agent-déjà-">Pourquoi connecter son agent, déjà ?</h2>
<p>Petit rappel, si vous arrivez directement ici.</p>
<p><a href="https://jordanchapuy.com/posts/2026/05/c-est-quoi-un-agent-ia/" rel="">Un agent IA</a> repose sur un modèle qui a appris énormément de choses sur le monde. Mais il ne sait rien de vous : ni vos mails, ni votre agenda, ni vos projets. En le branchant à vos outils, il travaille avec vos vraies données, et il peut agir à votre place.</p>
<p>C&rsquo;est le sujet de <a href="https://jordanchapuy.com/posts/2026/10/comment-connecter-son-agent-ia-a-ses-outils-gmail-notion-google-agenda/" rel="">l&rsquo;article qui ouvre cette série</a>, si vous voulez creuser.</p>
<h2 id="une-machine-à-café-sans-boutons">Une machine à café sans boutons</h2>
<p>CLI veut dire <em>Command Line Interface</em>, interface en ligne de commande en français. C&rsquo;est une façon d&rsquo;utiliser un programme en lui écrivant des ordres, plutôt qu&rsquo;en cliquant sur des boutons.</p>
<p>Pour l&rsquo;imaginer, prenez une machine à café multifonction, celle du bureau. Elle a un bel écran tactile, des boutons, des images de cappuccino, allongé, latte, déca, etc. C&rsquo;est pensé pour des humains : on regarde, on appuie, on boit et on discute avec les collègues du dernier bug en prod.</p>
<p>Maintenant, retirez l&rsquo;écran et les boutons. Il ne reste qu&rsquo;une fente, dans laquelle on glisse un ticket. Sur ce ticket, on écrit ce qu&rsquo;on veut :</p>
<div class="code-block open" style="counter-reset: code-block 0">
    <div class="code-header language-">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copier dans le presse-papiers"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><pre tabindex="0"><code>café faire --taille grand --sucre 1</code></pre></div>
<p>La machine lit le ticket, prépare le café, et ressort un petit ticket de réponse : « Grand café, 1 sucre. Bonne journée. »</p>
<p>On peut aussi lui glisser <code>café lister</code>, et elle imprime la liste de toutes les boissons qu&rsquo;elle sait faire. Ou <code>café --help</code>, et elle imprime sa notice : toutes les commandes possibles, avec leurs options.</p>
<p>Un CLI, c&rsquo;est cette fente à tickets. Le <strong>terminal</strong>, c&rsquo;est la fenêtre dans laquelle on écrit les tickets (le fameux écran noir). Et chaque service peut avoir sa propre machine : un CLI pour GitHub, un pour Google Workspace, etc.</p>
<p>Une vraie commande ressemble à ça :</p>
<div class="code-block open" style="counter-reset: code-block 0">
    <div class="code-header language-">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copier dans le presse-papiers"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><pre tabindex="0"><code>gh pr list</code></pre></div>
<p><code>gh</code>, c&rsquo;est le CLI de GitHub (le service où les développeurs versionnent leur code). <code>pr list</code> lui demande la liste des demandes de revue en cours sur un projet. Il répond par quelques lignes de texte, une par demande.</p>
<p>Pour un humain, la machine sans boutons est clairement moins pratique. Il faut connaître les commandes, les écrire sans faute, et c&rsquo;est moins joli. Mais pour un agent ?</p>
<h2 id="pourquoi-les-agents-adorent-ça">Pourquoi les agents adorent ça</h2>
<p><strong>Écrire un ticket, c&rsquo;est exactement ce qu&rsquo;il sait faire.</strong> Un modèle de langage lit et écrit du texte. Lui faire appuyer sur des boutons dans une interface, c&rsquo;est possible, mais c&rsquo;est lent et laborieux. Lui faire écrire une commande, c&rsquo;est naturel. Et la réponse revient en texte, qu&rsquo;il lit sans effort.</p>
<p><strong>Il connaît déjà la machine.</strong> Les CLI répandus existent souvent depuis des années, bien avant les agents. <code>gh</code> en est un bon exemple. Leurs commandes apparaissent dans des tonnes de documentations, de tutoriels et de forums, donc dans ce que les modèles ont ingéré pendant leur entraînement. Pas besoin de tout leur expliquer : ils savent déjà s&rsquo;en servir.</p>
<p><strong>La notice ne se lit qu&rsquo;au besoin.</strong> Avec un <a href="https://jordanchapuy.com/posts/2026/11/c-est-quoi-un-mcp-model-context-protocol/" rel="">MCP</a>, l&rsquo;agent reçoit souvent dès le début de la conversation la description de tous les outils disponibles. C&rsquo;est comme si chaque machine vous imprimait sa carte complète au moment où vous entrez dans la pièce. Avec un CLI, l&rsquo;agent ne demande <code>--help</code> que lorsqu&rsquo;il en a besoin, et seulement pour la machine qui l&rsquo;intéresse. Résultat : beaucoup moins de tokens consommés, une mémoire de travail moins encombrée. Les comparaisons qu&rsquo;on trouve en ligne vont dans ce sens, même si l&rsquo;écart varie beaucoup d&rsquo;un test à l&rsquo;autre.</p>
<p><strong>Les erreurs aussi sont des tickets.</strong> Une commande mal écrite ? La machine ressort un ticket : « option inconnue », « projet introuvable ». L&rsquo;agent le lit, corrige, et réessaie. Pas besoin d&rsquo;interpréter une fenêtre d&rsquo;erreur rouge.</p>
<h2 id="dans-mon-cas">Dans mon cas</h2>
<p>Mes agents utilisent déjà mon terminal en permanence, pour des choses très basiques : chercher des fichiers, gérer l&rsquo;historique d&rsquo;un projet avec git, lancer un petit programme Python, vérifier la qualité du code. Ce sont des commandes que j&rsquo;utilise moi-même depuis longtemps.</p>
<p>Et j&rsquo;ai installé d&rsquo;autres CLI exprès pour eux. <code>gh</code>, par exemple. Avec lui, mon agent peut créer une demande de modification sur GitHub, la surveiller, voir les erreurs quand les vérifications automatiques échouent, et réagir pour les corriger.</p>
<p>Petit bonus : ce sont des outils que je sais utiliser aussi. L&rsquo;agent et moi parlons la même langue, avec les mêmes commandes.</p>
<h2 id="comment-on-en-ajoute-un">Comment on en ajoute un</h2>
<p><strong>Il faut un agent qui a accès à un terminal.</strong> C&rsquo;est le point de départ. <a href="https://jordanchapuy.com/posts/2026/07/ma-premiere-fois-avec-un-agent-ia-un-guide-accessible-pour-demarrer/" rel="">Les agents qui travaillent sur votre machine</a>, comme Claude Code ou Codex, en ont un. Le chat web de Claude ou ChatGPT, non. (Petit clin d&rsquo;œil au passage : Claude Code et Codex sont eux-mêmes des CLI.)</p>
<p><strong>On installe le CLI.</strong> En général, une commande copiée depuis la documentation du service. Ou, plus simple : on demande à son agent. « Installe le CLI de GitHub », et il s&rsquo;en occupe.</p>
<p><strong>On se connecte.</strong> Le CLI doit savoir pour qui il travaille. Souvent, une commande ouvre votre navigateur, vous vous connectez à votre compte, et c&rsquo;est réglé. Parfois, il réclame une <a href="https://jordanchapuy.com/posts/2026/11/c-est-quoi-une-cle-api/" rel="">clé d&rsquo;API</a>, le fameux badge d&rsquo;hôtel de l&rsquo;article précédent.</p>
<p><strong>L&rsquo;agent s&rsquo;en sert.</strong> S&rsquo;il connaît déjà le CLI, rien à faire. Sinon, il lit la notice. Certains CLI récents sont même pensés pour les agents dès le départ : ils peuvent être livrés avec des skills, des petits modes d&rsquo;emploi prêts à donner à votre agent.</p>
<h2 id="les-limites">Les limites</h2>
<p><strong>Pas de terminal, pas de CLI.</strong> Si vous utilisez l&rsquo;IA depuis le chat web ou l&rsquo;application mobile, cette voie vous est fermée. Les <a href="https://jordanchapuy.com/posts/2026/10/c-est-quoi-un-connecteur-ia-relier-claude-ou-chatgpt-a-vos-outils-en-quelques-clics/" rel="">connecteurs</a> ou les <a href="https://jordanchapuy.com/posts/2026/11/c-est-quoi-un-mcp-model-context-protocol/" rel="">MCP</a> distants restent alors la solution alternative.</p>
<p><strong>C&rsquo;est souvent très permissif.</strong> Un CLI fait en général tout ce que votre compte permet de faire. Lire, oui, mais aussi supprimer, publier, modifier des réglages. Et l&rsquo;agent l&rsquo;utilise avec vos droits. La plupart des agents demandent votre accord avant de lancer une commande, au moins au début : prenez le temps de lire ce que vous validez. Et quand le service le permet, limitez les droits de la connexion ou de la clé.</p>
<p><strong>Tous les services n&rsquo;en ont pas.</strong> Les outils de développeurs en ont presque tous. Pour d&rsquo;autres services plus grand public, c&rsquo;est moins systématique.</p>
<p><strong>Attention à ce que vous installez.</strong> Un CLI tourne sur votre machine, avec vos droits. Même réflexe que <a href="https://jordanchapuy.com/posts/2026/08/evitez-les-skills-ia-malveillants-auditez-les-automatiquement/" rel="">pour les skills</a> et les MCP : on préfère l&rsquo;outil officiel du service, et on vérifie la source avant d&rsquo;installer quoi que ce soit. L&rsquo;avantage ici, c&rsquo;est que la plupart des CLI existent depuis assez longtemps pour avoir été éprouvés et audités, bien avant l&rsquo;arrivée de l&rsquo;IA.</p>
<h2 id="cli-ou-mcp-">CLI ou MCP ?</h2>
<p>Pour le moment, quand j&rsquo;ai le choix, je prends le CLI. Il existait souvent avant, il a fait ses preuves, l&rsquo;agent sait déjà s&rsquo;en servir, c&rsquo;est rapide et ça consomme beaucoup moins de tokens.</p>
<p>Mais je ne les oppose pas. Rien n&rsquo;oblige à n&rsquo;utiliser que l&rsquo;un ou l&rsquo;autre, et chacun a ses bonnes raisons d&rsquo;exister :</p>
<ul>
<li>certains services n&rsquo;ont qu&rsquo;un MCP ;</li>
<li>on n&rsquo;a pas forcément envie de donner à un agent l&rsquo;accès à un terminal ;</li>
<li>le web et l&rsquo;app mobile ne permettent pas d&rsquo;utiliser des CLI ;</li>
<li>un MCP peut volontairement ne proposer que quelques actions, là où un CLI ouvre souvent tout.</li>
</ul>
<p>Ce dernier point, je l&rsquo;ai vécu. Dans <a href="https://jordanchapuy.com/posts/2026/11/c-est-quoi-un-mcp-model-context-protocol/" rel="">l&rsquo;article sur les MCP</a>, je racontais ce petit MCP que j&rsquo;avais fait pour un agent de <a href="https://jordanchapuy.com/posts/2026/03/mon-petit-homelab-proxmox-domotique-home-assistant/" rel="">mon homelab</a> : trois actions git, pas une de plus, et pas de terminal. Avec le CLI git, il aurait pu tout faire, y compris des bêtises. Là, il ne pouvait littéralement pas.</p>
<h2 id="pas-besoin-daimer-lécran-noir">Pas besoin d&rsquo;aimer l&rsquo;écran noir</h2>
<p>Un CLI, c&rsquo;est une machine sans boutons, qu&rsquo;on pilote en lui glissant des tickets. Peu engageant pour nous, parfait pour un agent qui ne fait qu&rsquo;écrire et lire du texte. Et vous n&rsquo;avez même pas besoin d&rsquo;écrire les tickets vous-même : c&rsquo;est son travail.</p>
<p>Jusqu&rsquo;ici, dans cette série, c&rsquo;est toujours l&rsquo;agent qui va chercher l&rsquo;information ou qui agit quand on le lui demande. Il est à l&rsquo;initiative de la demande, en début de chaîne. Mais si c&rsquo;était au service de le prévenir quand il se passe quelque chose ? Un mail qui arrive, un paiement reçu, un formulaire rempli. C&rsquo;est le rôle du <strong>webhook</strong>, et ce sera le prochain article.</p>
<hr>
<blockquote>
<p><strong>Connecter un agent IA à d&rsquo;autres services</strong> :</p>
<ul>
<li><a href="https://jordanchapuy.com/posts/2026/10/comment-connecter-son-agent-ia-a-ses-outils-gmail-notion-google-agenda/" rel="">Comment connecter son agent IA à ses outils (Gmail, Notion, Google Agenda&hellip;) ?</a></li>
<li><a href="https://jordanchapuy.com/posts/2026/10/c-est-quoi-un-connecteur-ia-relier-claude-ou-chatgpt-a-vos-outils-en-quelques-clics/" rel="">C&rsquo;est quoi un connecteur IA ? Relier Claude ou ChatGPT à vos outils en quelques clics</a></li>
<li><a href="https://jordanchapuy.com/posts/2026/11/c-est-quoi-un-mcp-model-context-protocol/" rel="">C&rsquo;est quoi un MCP ? Le port USB-C qui branche votre agent IA à vos outils</a></li>
<li><a href="https://jordanchapuy.com/posts/2026/11/c-est-quoi-une-cle-api/" rel="">C&rsquo;est quoi une clé d&rsquo;API ? Le badge d&rsquo;hôtel qui ouvre vos outils à votre agent IA</a></li>
<li><a href="https://jordanchapuy.com/posts/2026/11/c-est-quoi-un-cli/" rel="">C&rsquo;est quoi un CLI ? La machine à café sans boutons que votre agent IA adore</a></li>
<li>C&rsquo;est quoi un webhook ?</li>
</ul>
<p>Hors-série complémentaire :</p>
<ul>
<li><a href="https://jordanchapuy.com/posts/2026/11/c-est-quoi-une-api/" rel="">C&rsquo;est quoi une API (Application Programming Interface) ?</a></li>
</ul></blockquote>
]]></description></item><item><title>Mise : un outil pour les gouverner tous</title><link>https://jordanchapuy.com/posts/2026/07/mise-un-outil-pour-les-gouverner-tous/</link><pubDate>Sat, 11 Jul 2026 10:46:00 +0200</pubDate><author>Jordan Chapuy</author><guid>https://jordanchapuy.com/posts/2026/07/mise-un-outil-pour-les-gouverner-tous/</guid><description><![CDATA[<p>Il y a trois ans (wow, c&rsquo;est loin, je ne m&rsquo;y attendais pas), j&rsquo;écrivais sur les version managers comme <a href="https://jordanchapuy.com/posts/2023/05/des-version-manager-pour-faire-cohabiter-plusieurs-versions-dun-langage-sdk-et-framework/" rel="">un moyen de faire cohabiter plusieurs versions d&rsquo;un langage, SDK et framework</a>. C&rsquo;est encore d&rsquo;actualité, et je le recommande toujours. Là aujourd&rsquo;hui, j&rsquo;ai envie de m&rsquo;étendre un peu plus sur une possibilité que j&rsquo;avais discrètement glissée dans mon post sans aller plus loin : et s&rsquo;il existait <strong>un seul outil pour gérer tous les autres</strong> ?</p>
<p>La réponse est oui, et j&rsquo;en utilise un depuis bientôt deux ans maintenant. Il s&rsquo;appelle <a href="https://mise.jdx.dev/" target="_blank" rel="noopener noreffer "><strong>mise-en-place</strong></a>, ou <code>mise</code> pour les intimes. Au départ, je l&rsquo;ai adopté pour une raison très simple : j&rsquo;en avais marre de chercher un nouveau version manager pour chaque écosystème.</p>
<p><code>nvm</code> pour Node.js. <code>pyenv</code> pour Python. <code>fvm</code> pour Flutter. <code>rustup</code> pour Rust. <code>chruby</code> pour Ruby. Un autre pour je ne sais quel SDK qui arrive sur un projet. Vous avez la musique. Chaque outil est souvent très bien dans son coin, mais l&rsquo;ensemble finit par créer une petite fatigue. Rien de dramatique, on va s&rsquo;en remettre, mais le genre de friction qui revient souvent.</p>
<p>Installer. Mettre à jour. Changer de version. Expliquer à un collègue quoi installer. Retrouver où la version est déclarée. Se souvenir si cette commande s&rsquo;applique globalement ou seulement dans le projet. La promesse de <code>mise</code> est assez agréable : <strong>apprendre une seule façon de faire, puis l&rsquo;appliquer aux autres d&rsquo;outils</strong>.</p>
<h2 id="un-version-manager-généraliste">Un version manager généraliste</h2>
<p><code>mise</code> se place dans la famille des gestionnaires de versions généralistes, comme <code>asdf</code> ou <code>vfox</code>. L&rsquo;idée n&rsquo;est donc pas de gérer uniquement Node.js, Ruby ou Python, mais de fournir <strong>une interface commune pour tout un ensemble de langages, runtimes, SDK et outils de développement</strong>.</p>
<p>Dans un projet, je peux vouloir Python 3.12, Node.js 22, Flutter 3.32, Terraform, Bun, ou encore un outil CLI dont l&rsquo;équipe a besoin. Plutôt que d&rsquo;empiler les gestionnaires spécialisés, je peux demander à <code>mise</code> de s&rsquo;en occuper. Derrière, il sait s&rsquo;appuyer sur plusieurs sources et backends selon les outils. Honnêtement, au quotidien, je n&rsquo;ai pas besoin d&rsquo;y penser. Et c&rsquo;est plutôt bon signe.</p>
<p>Ce n&rsquo;est pas le seul outil capable de faire ça. J&rsquo;avais déjà repéré <code>asdf</code> dans <a href="%28/posts/2023/05/des-version-manager-pour-faire-cohabiter-plusieurs-versions-dun-langage-sdk-et-framework/%29" rel="">mon ancien article sur les version manager</a>, et <code>vfox</code> existe aussi dans le même esprit. Pourquoi j&rsquo;ai choisi <code>mise</code> ? Parce qu&rsquo;il m&rsquo;a semblé rapide, actif côté communauté, très fourni en outils supportés, et parce qu&rsquo;il ne s&rsquo;arrête pas au simple rôle de version manager. Je reviendrais dessus juste après.</p>
<p>Mais avant, commençons par le commencement. Installer mise. (Vérifiez tout de même le <a href="https://mise.jdx.dev/" target="_blank" rel="noopener noreffer ">site officiel</a> si la méthode a changé entre temps) :</p>
<div class="code-block open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copier dans le presse-papiers"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl https://mise.run <span class="p">|</span> sh</span></span></code></pre></div></div>
<p>Si je veux installer Node.js globalement (tout mon PC), la commande ressemble à ça :</p>
<div class="code-block open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copier dans le presse-papiers"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mise use -g node@26</span></span></code></pre></div></div>
<p>Si je veux installer Python pour un projet (un dossier et ses enfants) :</p>
<div class="code-block open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copier dans le presse-papiers"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mise use python@3.13</span></span></code></pre></div></div>
<p>À partir de là, quand j&rsquo;entre dans le dossier du projet, <code>python</code> pointe vers la bonne version. Je tape <code>python</code>, pas <code>mise python</code>, pas <code>pyenv exec python</code>, pas une commande enveloppée avec un préfixe qui me sort du geste naturel. C&rsquo;est un détail, mais je trouve que ça compte. Quand je lance <code>flutter test</code>, j&rsquo;ai envie d&rsquo;écrire <code>flutter test</code>, pas de me rappeler que sur ce projet précis je dois écrire <code>fvm flutter test</code>.</p>
<h2 id="le-fichier-misetoml">Le fichier <code>mise.toml</code></h2>
<p>Là où <code>mise</code> devient vraiment intéressant, c&rsquo;est avec le fichier <code>mise.toml</code>.</p>
<p>Au début, on peut le voir comme un simple équivalent plus généraliste des fichiers <code>.ruby-version</code>, <code>.node-version</code>, <code>.tool-versions</code>, etc. On déclare les outils et les versions attendues par le projet, puis on versionne ce fichier avec le code. Comme ça, <strong>toute l&rsquo;équipe partage la même configuration</strong>.</p>
<p>Un exemple très simple pourrait ressembler à ça :</p>
<div class="code-block open" style="counter-reset: code-block 0">
    <div class="code-header language-toml">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copier dans le presse-papiers"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">tools</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">node</span> <span class="p">=</span> <span class="s2">&#34;26&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">python</span> <span class="p">=</span> <span class="s2">&#34;3.13&#34;</span></span></span></code></pre></div></div>
<p>Ce n&rsquo;est pas spectaculaire, mais c&rsquo;est très pratique. Le projet dit explicitement &ldquo;pour travailler ici, voici les outils et versions attendues&rdquo;. Le nouveau collègue qui arrive, la machine fraîchement réinstallée, le runner de CI, le moi du futur qui aura tout oublié&hellip; tout le monde lit la même chose.</p>
<p>C&rsquo;est la fin des discussions du style :</p>
<blockquote>
<ul>
<li>Tu es sur quelle version de Node ?</li>
<li>Attends, je croyais qu&rsquo;on était passé en 26 ?</li>
<li>Ah non, sur cette branche il faut encore la version 25.</li>
<li>Pourquoi ça marche chez toi et pas chez moi ?</li>
</ul></blockquote>
<p>Bon, je m&rsquo;emballe, cette dernière question ne disparaît jamais totalement malheureusement. Ce serait trop beau. Mais on passe tout de même <strong>d&rsquo;un réglage individuel en un élément de projet versionnée</strong>.</p>
<h2 id="bien-plus-quun-version-manager">Bien plus qu&rsquo;un version manager</h2>
<p>La surprise, pour moi, c&rsquo;est que le fichier <code>mise.toml</code> ne s&rsquo;arrête pas aux outils. On peut aussi y déclarer des variables d&rsquo;environnement, et même des tâches. C&rsquo;est là que <code>mise</code> a commencé à dépasser, chez moi, le rôle du &ldquo;truc qui gère les versions&rdquo; et où je me suis dis que j&rsquo;allais le garder.</p>
<p>Les <strong>variables d&rsquo;environnement</strong>, c&rsquo;est le genre de chose qui finit souvent dans un <code>.env</code>, parfois un fichier différent par outils, ou pire dans une note. Avec <code>mise</code>, on peut les ajouter dans la configuration du projet. Pas les secrets, évidemment. Mais les valeurs non sensibles, les chemins, les réglages utiles au développement, oui.</p>
<p>Et puis il y a les <strong>tâches</strong>. On peut définir des commandes nommées, un peu comme on le ferait avec un <code>Makefile</code> ou des scripts maison. Sauf qu&rsquo;elles vivent dans le même fichier que la déclaration des outils.</p>
<p>Par exemple :</p>
<div class="code-block open" style="counter-reset: code-block 0">
    <div class="code-header language-toml">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copier dans le presse-papiers"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">tasks</span><span class="p">.</span><span class="nx">setup</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">run</span> <span class="p">=</span> <span class="s2">&#34;mise install &amp;&amp; npm install&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">tasks</span><span class="p">.</span><span class="nx">test</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">run</span> <span class="p">=</span> <span class="s2">&#34;npm test&#34;</span></span></span></code></pre></div></div>
<p>Je simplifie volontairement, mais l&rsquo;idée est là. Sur un projet, il suffit de taper <code>mise setup</code> et cette commande prépare ce qu&rsquo;il faut. Elle installe les outils attendus et prépare le terrain. C&rsquo;est le genre de petite attention qui rend un projet plus accueillant. (et encore une fois, plus &ldquo;future-proof&rdquo; avec notre mémoire)</p>
<p>J&rsquo;ai commencé à le faire sur tout mes nouveaux projets. Je crée un <code>mise.toml</code>, je le source, et petit à petit j&rsquo;y mets les commandes que je rejoue souvent. Pas besoin de se demander si c&rsquo;est <code>make init</code>, <code>npm run setup</code>, <code>./scripts/bootstrap.sh</code> ou autre chose. Pour moi, de plus en plus, c&rsquo;est <code>mise run setup</code>.</p>
<p>Même pour ce blog, j&rsquo;ai une tâche <code>new</code> pour préparer un nouveau post. Créer le dossier, créer un fichier avec un template de post, mettre la date, puis l&rsquo;ouvrir dans VS Code. Rien de révolutionnaire, mais j&rsquo;aime beaucoup écrire de petits scripts pour éviter de répéter les mêmes opérations - <a href="https://jordanchapuy.com/posts/2020/10/soyez-flemmard-ecrivez-des-scripts/" rel="">je suis un flemmard</a>.</p>
<p>Je vous ai parlé des <strong>alias</strong> ? Je peux faire encore plus court en tapant seulement <code>setup</code> ou <code>new</code>. Et ça marche pour tout, pas seulement pour les tâches - ce sont simplement des alias après tout.</p>
<div class="code-block open" style="counter-reset: code-block 0">
    <div class="code-header language-toml">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copier dans le presse-papiers"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">shell_alias</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">new</span> <span class="p">=</span> <span class="s2">&#34;mise run new&#34;</span></span></span></code></pre></div></div>
<p>Attendez, ce n&rsquo;est pas fini. On peut aller plus loin avec des <strong>hooks</strong> : des commandes qui se lanceront à chaque fois qu&rsquo;on entre/sort du projet, avant ou après une installation, ou même faire du <em>watch</em> ! Quand je vous disais que cet outil allait les gouverner tous. <em>Mon précieux, mon mise</em>.</p>
<h2 id="ce-qui-se-passe-derrière">Ce qui se passe derrière</h2>
<p>Sans rentrer dans un tutoriel complet, ça vaut le coup de comprendre grossièrement comment <code>mise</code> s&rsquo;intègre au terminal.</p>
<p>Le principe de base est assez classique : <code>mise</code> installe les outils dans un de ses dossiers, puis modifie dynamiquement le contenu du <code>PATH</code>. Quand je suis dans un dossier avec un <code>mise.toml</code>, l&rsquo;environnement actif change, et la commande <code>node</code>, <code>python</code> ou <code>flutter</code> pointe vers la version déclarée.</p>
<p>C&rsquo;est aussi pour ça que l&rsquo;expérience reste <strong>très rapide</strong>. En fait, elle est même inchangée. On ne lance pas un gros proxy à chaque commande. Le shell sait directement où chercher les exécutables.</p>
<p>Il existe aussi un mode avec des <em>shims</em> si on en a besoin, c&rsquo;est-à-dire des petits exécutables intermédiaires qui redirigent vers la bonne version. Selon les environnements ou IDE, ça peut être utile. J&rsquo;ai dû passer par les <em>shims</em> pour l&rsquo;extension Flutter de VS Code par exemple.</p>
<p>Je trouve cette partie intéressante parce qu&rsquo;elle répond à une inquiétude que j&rsquo;avais avec certains version managers : est-ce que je vais ralentir mon terminal ? C&rsquo;était par exemple le cas avec <a href="https://jordanchapuy.com/posts/2023/12/mon-terminal-et-zsh-sont-long-a-demarrer-blame-nvm/" rel="">nvm qui est long à démarrer</a>, même si on ne l&rsquo;utilise pas.</p>
<h2 id="bref">Bref</h2>
<p>Au départ, je cherchais surtout un version manager généraliste. Je voulais arrêter d&rsquo;accumuler un outil par langage. <code>mise</code> m&rsquo;a donné ça : une manière commune d&rsquo;installer, mettre à jour et changer de version. Et il est même allé là où je ne l&rsquo;attendais pas avec les variables d&rsquo;environnement et les tâches.</p>
<p>Aujourd&rsquo;hui, je n&rsquo;installe quasiment plus de langage ou de SDK à la main. Flutter ? Via <code>mise</code>. Python ? Via <code>mise</code>. Les outils autour d&rsquo;<a href="https://jordanchapuy.com/posts/2026/06/maintenir-et-reinstaller-son-homelab-proxmox-avec-ansible-sans-tout-refaire-a-la-main/" rel="">Ansible</a> dont je parlais récemment ? Via <code>mise</code> aussi.</p>
<p>Quand je démarre un projet, mon premier réflexe est de créer et compléter le <code>mise.toml</code> au fur et à mesure pour réduire les frictions. À force, ça devient moins un outil de gestion de versions qu&rsquo;un petit <strong>chef d&rsquo;orchestre</strong> pour mon environnement de développement.</p>
]]></description></item><item><title>Mon terminal et zsh sont long à démarrer (blame nvm)</title><link>https://jordanchapuy.com/posts/2023/12/mon-terminal-et-zsh-sont-long-a-demarrer-blame-nvm/</link><pubDate>Fri, 29 Dec 2023 15:44:00 +0100</pubDate><author>Jordan Chapuy</author><guid>https://jordanchapuy.com/posts/2023/12/mon-terminal-et-zsh-sont-long-a-demarrer-blame-nvm/</guid><description><![CDATA[<p>Le terminal, c&rsquo;est une sorte de couteau suisse pour les développeurs. Il permet de faire énormément de choses - presque tout d&rsquo;ailleurs - et on s&rsquo;en sert très souvent. Alors quand le temps de lancement du terminal s&rsquo;allonge, je ne vous cache pas que c&rsquo;est très embêtant.</p>
<p>C&rsquo;était mon cas avec iTerm2 + oh-my-zsh, et on va voir comment trouver les problèmes à la source.</p>
<h1 id="identifier-les-causes">Identifier les causes</h1>
<p>Je n&rsquo;ai pas forcément fait attention au moment exact où mon terminal a commencé à être capricieux pour se lancer. J&rsquo;ai l&rsquo;impression que ça a commencé la semaine marquée par l&rsquo;arrivée d&rsquo;un nouveau super pouvoir du MDM sur nos machines pro. Il analysait toutes les commandes exécutées pour vérifier les bons privilèges à appliquer.</p>
<p>Une belle coïncidence, c&rsquo;était le suspect parfait. Je l&rsquo;ai un peu blâmé sans trop chercher plus loin. Et puis un jour, il est arrivé ce fameux imprévu qui permet au héros de se lancer dans l&rsquo;aventure.</p>
<p>J&rsquo;étais en pair avec un collègue pour l&rsquo;aider sur une erreur autour de cocoapods et iOS. Je lui fais installer un <a href="https://jordanchapuy.com/posts/2023/05/des-version-manager-pour-faire-cohabiter-plusieurs-versions-dun-langage-sdk-et-framework/" rel="">version manager</a> pour Ruby, on modifie son <code>.zshrc</code>, et on veut supprimer le <code>Podfile.lock</code> pour réinstaller proprement les packages. Mais dans la rapidité de l&rsquo;action, il tape <code>rm .zshrc</code>. Aïe. Coup dur.</p>
<p>À ce moment-là, mon collègue est un peu dégoûté - je l&rsquo;aurais été aussi. Avec <code>rm</code>, ça ne part pas à la corbeille, on ne récupère pas facilement ce que l&rsquo;on a supprimé. Ça l&rsquo;embête parce qu&rsquo;il avait effectué plusieurs configurations dedans, et qu&rsquo;il avait passé une heure avec la DSI pour optimiser le temps de chargement. Hop hop hop, attends. Quoi ?!</p>
<p>Lui aussi avait son terminal qui était long au lancement, et de mémoire, ça venait en partie de l&rsquo;initialisation de <code>nvm</code>. Me voilà avec une piste intéressante. Je l&rsquo;ai gardée dans un coin de tête, jusqu&rsquo;à hier soir où j&rsquo;ai fouillé les internets avec une idée de direction plus précise.</p>
<h2 id="mesurer-le-temps">Mesurer le temps</h2>
<p>À l&rsquo;aide de deux instructions, on peut obtenir un rapport avec les différentes étapes exécutées au lancement de <code>zsh</code> et le temps associé.</p>
<p>Il suffit de modifier le fichier <code>.zshrc</code> pour y ajouter <code>zmodload zsh/zprof</code> au tout début, à la première ligne. On quitte le terminal, on le relance et on tape la commande <code>zprof</code>.</p>
<div class="code-block open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copier dans le presse-papiers"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">num  calls                <span class="nb">time</span>                       self            name
</span></span><span class="line"><span class="cl">-----------------------------------------------------------------------------------
</span></span><span class="line"><span class="cl"> 1<span class="o">)</span>    <span class="m">2</span>        3047,39  1523,69   43,08%   2046,60  1023,30   28,93%  nvm
</span></span><span class="line"><span class="cl"> 2<span class="o">)</span>    <span class="m">1</span>        4998,69  4998,69   70,66%   1951,31  1951,31   27,58%  nvm_auto
</span></span><span class="line"><span class="cl"> 3<span class="o">)</span>    <span class="m">1</span>         821,18   821,18   11,61%    665,29   665,29    9,40%  nvm_ensure_version_installed
</span></span><span class="line"><span class="cl"> 4<span class="o">)</span>    <span class="m">1</span>         563,64   563,64    7,97%    563,64   563,64    7,97%  is_update_available
</span></span><span class="line"><span class="cl"> 5<span class="o">)</span>    <span class="m">1</span>         743,74   743,74   10,51%    180,11   180,11    2,55%  /.oh-my-zsh/tools/check_for_upgrade.sh
</span></span><span class="line"><span class="cl"> 6<span class="o">)</span>    <span class="m">1</span>         176,44   176,44    2,49%    174,59   174,59    2,47%  nvm_die_on_prefix
</span></span><span class="line"><span class="cl"> 7<span class="o">)</span>    <span class="m">1</span>         155,88   155,88    2,20%    155,88   155,88    2,20%  nvm_is_version_installed
</span></span><span class="line"><span class="cl"> 8<span class="o">)</span>    <span class="m">1</span>          93,04    93,04    1,32%     51,05    51,05    0,72%  compinit</span></span></code></pre></div></div>
<p>Dans ce tableau, on retrouve le nom des étapes, le temps, le pourcentage du temps par rapport à l&rsquo;ensemble, et c&rsquo;est même classé dans l&rsquo;ordre du plus long au plus rapide.</p>
<p>Sur ma machine, je vois clairement que <code>nvm</code> est dans le haut du panier. Plusieurs noms d&rsquo;étapes semblent liées à <code>nvm</code> et elles prennent énormément de temps. Je vais donc optimiser le lancement de <code>nvm</code>, le reste me semble anecdotique. Je le ferais ou pas un jour. Sûrement jamais. :)</p>
<h1 id="améliorer-linitialisation-de-nvm">Améliorer l&rsquo;initialisation de nvm</h1>
<p>J&rsquo;ai trouvé plusieurs moyens pour pallier la lenteur de <code>nvm</code>.</p>
<h2 id="supprimer-nvm-du-zshrc">Supprimer nvm du .zshrc</h2>
<p>Quand on installe <code>nvm</code>, il y a des instructions à ajouter dans notre configuration <code>.zshrc</code>. Leurs rôles sont de charger <code>nvm</code>, de s&rsquo;assurer qu&rsquo;il est à jour, de préparer l&rsquo;auto-complétion, etc. C&rsquo;est ce qui ralentit le lancement de notre terminal. Ces instructions ressemblent à ça :</p>
<div class="code-block open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copier dans le presse-papiers"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">NVM_DIR</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$HOME</span><span class="s2">/.nvm&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="o">[</span> -s <span class="s2">&#34;</span><span class="nv">$NVM_DIR</span><span class="s2">/nvm.sh&#34;</span> <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="se">\.</span> <span class="s2">&#34;</span><span class="nv">$NVM_DIR</span><span class="s2">/nvm.sh&#34;</span>  <span class="c1"># This loads nvm</span>
</span></span><span class="line"><span class="cl">    <span class="o">[</span> -s <span class="s2">&#34;</span><span class="nv">$NVM_DIR</span><span class="s2">/bash_completion&#34;</span> <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="se">\.</span> <span class="s2">&#34;</span><span class="nv">$NVM_DIR</span><span class="s2">/bash_completion&#34;</span>  <span class="c1"># This loads nvm bash_completion</span></span></span></code></pre></div></div>
<p>Bon. Eh bien de manière pragmatique, on peut supprimer ces instructions de notre configuration <code>.zshrc</code> et le temps de lancement sera grandement amélioré. Si on n&rsquo;utilise plus <code>nvm</code>, <code>node</code> et compagnie, ou très peu souvent, on n&rsquo;a pas envie de pourrir l&rsquo;expérience nominale de notre terminal.</p>
<h2 id="lazy-loading-de-nvm">Lazy loading de nvm</h2>
<p>Une autre solution serait d&rsquo;initialiser <code>nvm</code> uniquement lorsque l&rsquo;on en a besoin. Quand on tapera une commande avec <code>nvm</code>, <code>node</code> ou <code>npm</code> par exemple. C&rsquo;est du lazy loading. Ce serait l&rsquo;idéal si on veut conserver <code>nvm</code> et que l&rsquo;on veut éviter de ralentir le lancement du terminal.</p>
<p>Il est possible de le faire soi-même en créant quelques fonctions bash comme l&rsquo;explique <a href="https://broken-by.me/lazy-load-nvm/" target="_blank" rel="noopener noreffer ">cet article</a>. On peut aussi utiliser le système de plugin de <code>zsh</code>. Luke en a créé un pour nvm : <a href="https://github.com/lukechilds/zsh-nvm" target="_blank" rel="noopener noreffer ">zsh-nvm</a>. J&rsquo;ai opté pour cette solution.</p>
<ul>
<li>La première étape est de supprimer les éventuelles instructions <code>nvm</code> de notre fichier <code>.zshrc</code> (cf. partie précédente).</li>
<li>On installe ensuite le plugin <a href="https://github.com/lukechilds/zsh-nvm?tab=readme-ov-file#installation" target="_blank" rel="noopener noreffer ">zsh-nvm</a>.</li>
<li>On modifie le fichier <code>.zshrc</code> pour ajouter <code>zsh-nvm</code> dans la liste des plugins.
<ul>
<li>La ligne ressemble à <code>plugins=(.......)</code>.</li>
</ul>
</li>
<li>On indique au plugin que l&rsquo;on veut du lazy loading.
<ul>
<li>On ajoute <code>export NVM_LAZY_LOAD=true</code> juste avant la liste des plugins.</li>
</ul>
</li>
<li>On peut ajouter l&rsquo;auto-complétion si nécessaire.
<ul>
<li>En ajoutant également <code>export NVM_COMPLETION=true</code> avant la liste des plugins.</li>
</ul>
</li>
</ul>
<h2 id="changer-de-version-manager-pour-node">Changer de version manager pour node</h2>
<p>Vu que <code>nvm</code> est le fautif et qu&rsquo;il existe d&rsquo;autres version manager pour node, on peut se demander s&rsquo;il en existe qui sont plus rapides.</p>
<p>Ça semble être le cas pour <a href="https://github.com/Schniz/fnm" target="_blank" rel="noopener noreffer ">fnm (Fast Node Manager)</a>. Son nom est revenu plusieurs fois pendant mes recherches et dans les commentaires de personnes qui proposaient des alternatives au lazy loading de <code>nvm</code>. Je ne l&rsquo;ai pas essayé.</p>
<p>Il y a également <a href="https://asdf-vm.com/" target="_blank" rel="noopener noreffer ">asdf</a>, un outil unique qui propose de gérer les versions de multiples autres outils (node, Ruby, python, &hellip;). J&rsquo;en parlais dans mon article sur <a href="https://jordanchapuy.com/posts/2023/05/des-version-manager-pour-faire-cohabiter-plusieurs-versions-dun-langage-sdk-et-framework/" rel="">les version manager</a>. Il serait plus rapide que <code>nvm</code> d&rsquo;après ce que j&rsquo;ai lu. À tester. Je pense que je finirais par me passer de mes autres version manager pour tout regrouper avec <code>asdf</code>.</p>
<hr>
<p>Quelques références de contenu que j&rsquo;ai parcouru :</p>
<ul>
<li><a href="https://dev.to/thraizz/fix-slow-zsh-startup-due-to-nvm-408k" target="_blank" rel="noopener noreffer ">https://dev.to/thraizz/fix-slow-zsh-startup-due-to-nvm-408k</a></li>
<li><a href="https://blog.mattclemente.com/2020/06/26/oh-my-zsh-slow-to-load/" target="_blank" rel="noopener noreffer ">https://blog.mattclemente.com/2020/06/26/oh-my-zsh-slow-to-load/</a></li>
<li><a href="https://broken-by.me/lazy-load-nvm/" target="_blank" rel="noopener noreffer ">https://broken-by.me/lazy-load-nvm/</a></li>
<li><a href="https://stackoverflow.com/questions/47009776/how-to-run-nvm-in-oh-my-zsh" target="_blank" rel="noopener noreffer ">https://stackoverflow.com/questions/47009776/how-to-run-nvm-in-oh-my-zsh</a></li>
</ul>
]]></description></item><item><title>Automatically run tests when a change occurs</title><link>https://jordanchapuy.com/posts/2020/06/automatically-run-tests-when-a-change-occurs/</link><pubDate>Thu, 04 Jun 2020 09:00:00 +0200</pubDate><author>Jordan Chapuy</author><guid>https://jordanchapuy.com/posts/2020/06/automatically-run-tests-when-a-change-occurs/</guid><description><![CDATA[<p>I would like to talk to you about unit tests and how you can automatically run them as soon as you modify code. I regularly use the Test-Driven Development approach when I write code for a project or when doing a code kata. Sometimes I write a little bit too much before running tests again, and when tests turn red, I’m telling myself “Oh! But since when was I wrong?”.</p>
<p>I am jealous of the experience Visual Studio offers with C# and the <a href="https://docs.microsoft.com/en-us/visualstudio/test/live-unit-testing" target="_blank" rel="noopener noreffer ">Live Unit Testing</a> feature. Each line of the code is decorated with a symbol indicating if it is covered by passing tests or failing tests, in real-time. Awesome. I need something like that.</p>
<p>I started by searching a similar tool for the languages I use. Then I created a simple script, which runs a command after each file change. Finally, I found a really nice tool for file change monitoring. It is powerful, cross-platform, and open-source. And that’s what I want to talk to you about.</p>
<figure>
</figure>

<hr>
<h1 id="watch-my-changes-">Watch my changes! 🔍</h1>
<p>One way to improve the feedback given by our tests is to know immediately if a change breaks a test. To achieve this goal, we have to continually run tests. The more often we run the tests, the smaller the failing piece of code will be. Ideally, tests should be run after each change. But we can easily forget, or it can be cumbersome.</p>
<p>So we need a way to monitor files and automatically execute a command if there is a change. Some languages and tools offer a built-in way to do that. For example:</p>
<ul>
<li>Kotlin through Gradle &gt; <code>gradle test —-continuous</code></li>
<li>Elm with elm-test &gt; <code>elm-test --watch</code></li>
</ul>
<p>But what if the programming language and tool we are using do not have a watch option? This is where <em>fswatch</em> comes in.</p>
<hr>
<h1 id="fswatch-">fswatch 📡</h1>
<p>Open-source and cross-platform, <em>fswatch</em> is a great tool to monitor file change. It provides recursive directory monitoring, filtering, and supports many OS-specific APIs such as <em>kevent</em>, <em>inotify</em>, and <em>FSEvents</em>.</p>
<p>By using these low-level APIs, <em>fswatch</em> is able to detect any changes in your specified paths and to send informations to another command. The tool is good enough to throttle the execution of the command: it will wait for the user to stop editing the code.</p>
<p>For more information about features or how to install it, please take a look at the GitHub repository.</p>
<p><a href="https://github.com/emcrisostomo/fswatch" target="_blank" rel="noopener noreffer ">https://github.com/emcrisostomo/fswatch</a></p>
<h2 id="usage">Usage</h2>
<p>In most cases, you simply want to fire a command as soon as a change is detected in a folder. It’s as simple as this line:</p>
<div class="code-block open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copier dans le presse-papiers"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">fswatch -or YOUR_PATHS <span class="p">|</span> xargs -n1 -I<span class="o">{}</span> YOUR_COMMAND</span></span></code></pre></div></div>
<p><em>fswatch</em> accepts a list of paths to monitor. The <code>o</code> option asks the tool to group changes by batch, and the <code>r</code> option stands for watching subdirectories recursively. Events are then sent through <code>xargs</code> to your command. Here is an example for swift, observing one folder:</p>
<div class="code-block open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copier dans le presse-papiers"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">fswatch -or ./Project <span class="p">|</span> xargs -n1 -I<span class="o">{}</span> swift test</span></span></code></pre></div></div>
<p>Note that <em>fswatch</em> will run until you stop it, by pressing <code>Control + Z</code> on macOS or <code>Control + C</code> on Linux for example. But that’s what we want, run indefinitely regardless of the test results.</p>
<figure><figcaption>
      <p><em>Screenshot from a terminal executing fswatch.</em></p>
    </figcaption>
</figure>

<hr>
<h1 id="going-further-">Going further 🚀</h1>
<p>So, you got the basics. Now we will see how to improve it.</p>
<h2 id="choose-carefully-the-files-to-monitor">Choose carefully the files to monitor</h2>
<p>You may want to not run your tests for every kind of file changed. A change in an image or in a <em>temp</em> folder probably does not interest you. Observing the right files allows you to run tests only if an impacting change occurs. It can also prevent you from going into an infinite loop.</p>
<p>I had a case where build files were generated after each test, so that triggered the watch, and run tests again, and changes appear, and… this story will never end. The easiest solution for me was to specify multiple sub-directories as input to fswatch` instead of the whole project folder. Remember that you can set multiple paths.</p>
<div class="code-block open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copier dans le presse-papiers"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">fswatch -or ./Sources ./Tests <span class="p">|</span> xargs -n1 -I<span class="o">{}</span> swift test</span></span></code></pre></div></div>
<p>The other way is to include or exclude paths matching a regex. According to the <em>fswatch</em> manual, you can set options like <code>-i, --include</code> and <code>-e, --exclude</code> for that.</p>
<h2 id="create-a-script">Create a script</h2>
<p>Sometimes, the command you want to fire for each change is long and requires a lot of parameters. It may even be composed of multiple commands. In that case, I recommend you to decompose this step and to create a script to run your tests.</p>
<div class="code-block open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copier dans le presse-papiers"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">fswatch -or ./Sources ./Tests <span class="p">|</span> xargs -n1 -I<span class="o">{}</span> run-tests.sh</span></span></code></pre></div></div>
<p>Better, isn’t it? But we can still improve that. We can avoid typing that long line every time we start working. You see me coming, we’ll make a second script that will do it.</p>
<p><code>live-tests.sh</code></p>
<h2 id="keep-it-visible">Keep it visible</h2>
<p>Now that we have a real-time feedback, we want to see it. It would be a shame to have it hidden in the background. Good news, it is quite easy to keep a terminal in the foreground. Perhaps your favorite editor has already a panel with a terminal, and you just have to show it. Visual Studio Code, for instance.</p>
<p>Some editors like Sublime Text do not have a terminal panel by default, but you can add it with a plugin or extension. And if that’s not possible, you still have the option to put the terminal on a second screen, if you have one, or to reduce your editor window and put a terminal at the left, right, or bottom of it.</p>
<figure><figcaption>
      <p><em>Screenshot from Visual Studio Code with a terminal panel at the bottom.</em></p>
    </figcaption>
</figure>

<hr>
<p>You have everything you need to get started. Monitoring files is at your hands. Enjoy the continuous and real-time feedback of your unit tests.</p>
<p>If you write Swift, <a href="https://jordanchapuy.com/posts/2020/05/playgrounds-speed-up-your-tests-feedback-in-swift/" rel="">Playgrounds are another way to speed up that feedback</a>.</p>
]]></description></item></channel></rss>