Distribuer ses propres scripts via Homebrew
(justin.searls.co)- Homebrew est un gestionnaire de paquets qui permet d’installer et de gérer facilement des outils CLI sur macOS, et aide les développeurs à configurer efficacement leur environnement système avec les outils qu’ils utilisent le plus souvent
- Ce guide explique comment distribuer des scripts CLI personnels avec Homebrew, et montre comment simplifier la maintenance grâce à l’intégration GitHub et à des workflows automatisés
- Le processus de distribution suit les étapes création du CLI → release GitHub → création d’un Tap → rédaction et mise à jour de la Formula, pour aboutir à une installation possible uniquement avec les commandes
brew tapetbrew install - Bien comprendre la terminologie et les bonnes pratiques de Homebrew permet de mettre en place une distribution fiable, avec une meilleure reproductibilité et une sécurité renforcée de la chaîne d’approvisionnement
- Le tout peut être automatisé via des workflows GitHub Actions, et une fois configuré, la distribution d’autres CLI devient extrêmement simple
Contexte et motivation
- Homebrew est le gestionnaire de paquets privilégié pour installer des outils CLI sur macOS, et il est largement utilisé par les développeurs
- Pourtant, quand on distribue un CLI que l’on a créé, on passe souvent par npm ou RubyGems, et la méthode de distribution via Homebrew peut sembler moins familière
- Le dépôt core officiel de Homebrew applique une politique selon laquelle l’équipe Homebrew hésite à accepter des outils artisanaux, si bien que les développeurs publient généralement via un tap et une formula séparés
- Ce guide s’appuie sur une expérience concrète de distribution d’un CLI simple en Ruby
Explication des termes
- Homebrew utilise une terminologie particulière inspirée de l’univers du brassage, et la comprendre aide à mieux saisir l’architecture du système
- Une Formula est un fichier de définition de paquet, qui contient les instructions pour installer un code source ou un binaire
- Un Tap est un dépôt Git de Formulas, utilisé pour gérer des paquets personnalisés au niveau d’un utilisateur ou d’une organisation
- Un Cask est un manifeste d’installation pour des applications GUI ou de gros binaires, proche d’une Formula mais destiné à des fichiers précompilés
- Une Bottle est un paquet binaire précompilé copié tel quel au lieu d’être compilé depuis les sources, ce qui accélère l’installation
- Le Cellar est le répertoire où se trouvent les Formulas installées, par exemple
/opt/homebrew/Cellar - Un Keg est le répertoire d’installation d’une Formula donnée, placé dans le Cellar et organisé par version
Vue d’ensemble
- Le dépôt core de Homebrew n’accepte pas les contenus de niche ou soumis à titre personnel, donc les utilisateurs doivent créer un dépôt Tap séparé pour distribuer leur CLI
- 1. Créer le CLI, le publier sur GitHub et faire une release taguée
- 2. Créer le Tap avec
brew tap-new, puis le pousser sur GitHub - 3. Générer la Formula avec
brew create(incluant l’URL du tarball et le SHA256) - 4. À chaque nouvelle version, mettre à jour la Formula afin que les utilisateurs puissent installer facilement avec
brew install
- Une fois la distribution prête, les utilisateurs peuvent installer le CLI avec deux commandes :
brew tap your_github_handle/tapetbrew install your_cool_cli- Ce guide laisse de côté le développement du CLI lui-même et se concentre sur la création du tap, la génération de la Formula et son cycle de mise à jour
- L’exemple utilisé est le CLI
imsg, qui crée une archive web interactive à partir de la base de données iMessage
Création du tap
- Il faut suivre le guide de création de tap de Homebrew en remplaçant le nom d’utilisateur ou d’organisation GitHub selon son cas
- Pour regrouper tous ses futurs outils CLI dans un seul tap, le nom
homebrew-tapest recommandé ; le préfixehomebrewfait l’objet d’un traitement spécial dans le CLI, ettapest un suffixe courant par convention
- Pour regrouper tous ses futurs outils CLI dans un seul tap, le nom
- Exécuter la commande de création du tap :
brew tap-new searlsco/homebrew-tap- Cela génère un squelette dans
/opt/homebrew/Library/Taps/searlsco/homebrew-tap - Créer ensuite le dépôt correspondant sur GitHub et y pousser le contenu généré :
cd /opt/homebrew/Library/Taps/searlsco/homebrew-tap,git remote add origin git@github.com:searlsco/homebrew-tap.git,git push -u origin main
- Cela génère un squelette dans
- Une fois propriétaire du tap, d’autres utilisateurs peuvent cloner le dépôt dans
/opt/homebrew/Library/Tapsavec la commandebrew tap searlsco/tap- Au départ, le dépôt n’a rien d’utile, mais cela permet déjà de vérifier que le mécanisme de base fonctionne
Création de la Formula
- Homebrew peut référencer directement un dépôt GitHub, mais recommande d’utiliser un tarball versionné et sa somme de contrôle afin de renforcer la reproductibilité et la sécurité de la chaîne d’approvisionnement open source
- GitHub héberge un tarball à URL prévisible lors d’un push de tag ; par exemple, dans le dépôt
imsg, aprèsgit tag v0.0.5puisgit push --tags, l’archivehttps://github.com/searlsco/imsg/archive/refs/tags/v0.0.5.tar.gzest créée
- GitHub héberge un tarball à URL prévisible lors d’un push de tag ; par exemple, dans le dépôt
- Commande de création de la Formula :
brew create https://github.com/searlsco/imsg/archive/refs/tags/v0.0.5.tar.gz --tap searlsco/homebrew-tap --set-name imsg --ruby- Le drapeau
--tapdésigne le tap personnalisé et place la Formula dans/opt/homebrew/Library/Taps/searlsco/homebrew-tap/Formula --set-name imsgdéfinit explicitement le nom de la Formula, à choisir de façon unique pour éviter les doublons (attention par exemple aux conflits avec des CLI existants comme TLDR ou standard)--rubyest un préréglage de template pour les CLI Ruby, l’une des nombreuses options qui simplifient la personnalisation
- Le drapeau
- La Formula générée peut ne pas fonctionner immédiatement ; l’auteur recommande alors de la corriger avec l’aide d’un LLM : exécuter
brew install --verbose imsg, copier l’erreur dans ChatGPT, puis itérer sur la Formula- Le fichier final Formula/imsg.rb peut servir de point de départ réutilisable pour distribuer d’autres CLI Ruby
- Distribuer via Homebrew plutôt que via un gestionnaire de paquets spécifique au langage facilite aussi les évolutions de l’implémentation sans perturber les mises à jour côté utilisateur
Points clés de la Formula
- Toutes les Formulas sont écrites en Ruby, car de nombreux outils de développement populaires avant l’ère de JavaScript ou de l’IA reposaient sur Ruby
- La méthode
headpermet de désigner un dépôt Git, même si son intérêt concret reste incertain - Ajouter
livechecka de la valeur, car cela facilite la mise à jour des versions de la Formula - Le test d’exécution du binaire peut être implémenté simplement en vérifiant l’affichage de l’aide ; il ne faut pas se laisser intimider par les commentaires générés
- La commande
brew style searlsco/tappermet de repérer les erreurs de style - Le
uses_from_macos "ruby"par défaut du template--rubyutilise la version 2.6.10 (une release antérieure au COVID et arrivée en fin de vie depuis 3 ans), donc il est recommandé de déclarerdepends_on "ruby@3"pour dépendre d’une Formula Ruby plus récente
- La méthode
- Une fois la Formula satisfaisante, un
git pushsuffit pour la publier en production, et les utilisateurs pourront l’installer avecbrew tap searlsco/tappuisbrew install imsg
Mise à jour de la Formula à chaque release du CLI
- Mettre à jour manuellement l’
urlet le hashsha256en tête de la Formula à chaque release est fastidieux, et même la création d’un tag ou d’une release GitHub finit par devenir pesante- On peut utiliser la commande
bump-formula-prde Homebrew ou une GitHub Action pour générer une PR, mais le processus fork + PR paraît inutilement complexe - Si l’on possède le tap, une méthode plus simple consiste à committer directement sur la branche main
- On peut utiliser la commande
- Pour éviter cela, il est recommandé d’ajouter un workflow GitHub au dépôt de la Formula afin de mettre à jour automatiquement le tap lors d’une release
- Le workflow d’exemple peut être repris tel quel
- Configuration nécessaire : créer un Personal Access Token GitHub avec le droit
Content→Writesur le dépôthomebrew-tap, puis l’enregistrer dans les Secrets du dépôt de la Formula sous le nomHOMEBREW_TAP_TOKEN - Définir le tap et la Formula via des variables d’environnement (par exemple lignes 13 à 15)
- Il est recommandé d’utiliser le compte bot GitHub pour les mises à jour :
GH_EMAIL: 41898282+github-actions[bot]@users.noreply.github.com,GH_NAME: github-actions[bot]
- Une fois la release créée, un
git push --tagsdéclenche la mise à jour automatique en quelques secondes, et les utilisateurs peuvent ensuite faire la mise à niveau avecbrew updatepuisbrew upgrade imsg
Le plus intéressant
- Le processus est complexe, mais une fois le tap configuré et un premier exemple de Formula en place, publier des CLI supplémentaires devient presque trivial
- Il devient pratique de publier une nouvelle Formula en quelques minutes
- Le processus officiel de Homebrew est un peu lourd, mais l’automatisation le rend beaucoup plus confortable
- Cela réduit les frictions entre la release d’un outil et sa distribution, tout en restant extensible à des CLI écrits dans différents langages
- Rien ne dit qu’un autre Formula sera réellement publiée, mais il est satisfaisant de savoir que cette possibilité existe
2 commentaires
L’option
--no-forkpermet de pousser directement sur une branche puis de fusionner, tout en offrant une fonction de mise à jour automatique.Avis Hacker News
Les conventions de nommage de Homebrew me semblent parfois un peu déroutantes, mais j’ai toujours le sentiment que c’est un outil vraiment utile dans l’ensemble
Je ne pensais pas non plus que le processus pour créer son propre tap et y distribuer des outils serait aussi simple
Je me demande en quoi c’est préférable à des gestionnaires de paquets propres à un langage (par ex. uv)
J’aimerais surtout savoir si c’est plus simple pour les personnes qui ne sont pas dans un écosystème donné, autrement dit s’il y a un avantage du point de vue de l’universalité
Merci pour le partage, les autres outils qui utilisent un registre de paquets demandent en général de créer un compte, d’activer l’authentification à deux facteurs, de passer par des étapes de signature, etc.
Homebrew est bien plus simple dans l’ensemble, car les conditions d’utilisation (ToS) de GitHub servent de base de confiance
L’équipe Homebrew peut ainsi réduire beaucoup de complexité grâce à cette approche
Si l’on parle des paquets Python, les tentatives pour tout empaqueter d’un coup avec quelque chose comme uv sont difficilement réalistes
En général, on installe donc uniquement des dépendances figées dans un environnement
venvOn peut voir un exemple concret avec cette formule
Concernant uv, j’ai essayé d’ajouter la prise en charge de paquets privés avec les outils officiels (
brew update-python-resources,homebrew-pypi-poet), mais cela n’a pas vraiment fonctionnéJ’ai donc créé moi-même uvbrew pour aider à générer les ressources
Il existe aussi la documentation officielle de référence pour écrire des formules Python dans Homebrew
Pour les développeurs Go, je recommande l’outil Goreleaser
Il permet de distribuer très facilement des binaires dans un tap personnel (cette méthode est interdite dans le core officiel)
C’est très utile pour gérer des projets dans différents langages
Personnellement, je pense qu’il est plus idéal de gérer directement les mises à jour côté tap
C’est globalement similaire à une mise à jour faite en amont par l’upstream
En regardant ce workflow, on peut mettre à jour facilement même des formules/casks qu’on ne possède pas
Avec la commande
brew bump, on peut tout scanner, créer une PR et même automatiser les tests avecbrew test-botOn peut voir un exemple de PR réelle ici
D’habitude, je n’ose pas me lancer parce que je trouve dommage de consommer du temps GitHub Actions, mais comme c’est gratuit pour l’open source, ce type d’usage me paraît tout à fait pertinent
J’ai moi-même écrit homebrew-bump-revision comme workflow pour automatiser le bump des versions dans mon Homebrew tap
Je l’utilise efficacement sur plusieurs projets personnels
Je ne m’y suis pas mis par flemme, mais c’est un bon outil
Il y a eu un épisode du podcast Ruby Rogues consacré à divers conseils pour distribuer une CLI avec Homebrew
On peut en entendre davantage dans l’épisode correspondant
J’ai découvert un point intéressant au sujet de l’empaquetage d’outils Python
Certains paquets Python ne sont pas compatibles avec Homebrew, car une boucle de dépendances peut se produire pendant le processus de build
Avec pip, il n’y a pas de problème parce qu’il télécharge des releases binaires, mais Homebrew compile lui-même toutes les dépendances, ce qui prend bien plus de temps
C’est pourquoi même un projet Python de taille moyenne peut demander plus d’une heure pour construire une « bottle »
Depuis que j’ai commencé à utiliser nix pour l’administration de mon système, je ne l’ai pas regretté une seule fois
Mon seul regret concerne le fait de devoir dépendre de windows à cause des jeux multijoueur