Cómo distribuir tus propios scripts a través de Homebrew
(justin.searls.co)- Homebrew es un gestor de paquetes que permite instalar y administrar fácilmente herramientas CLI en macOS, y ayuda a los desarrolladores a configurar de forma eficiente su entorno del sistema con herramientas de uso frecuente
- Esta guía explica el proceso de distribuir scripts CLI personales con Homebrew y muestra cómo simplificar el mantenimiento mediante la integración con GitHub y flujos de trabajo automatizados
- El proceso de distribución sigue esta secuencia: crear la CLI → publicar un release en GitHub → crear un Tap → escribir y actualizar la Formula, y al final se puede instalar solo con los comandos
brew tapybrew install - Entender la terminología y las mejores prácticas de Homebrew permite una distribución estable con mayor reproducibilidad y seguridad de la cadena de suministro
- También puede automatizarse con flujos de trabajo de GitHub Actions, y una vez configurado, la gran ventaja es que luego distribuir otras CLI se vuelve muy sencillo
Contexto y motivación
- Homebrew es el gestor de paquetes preferido para instalar herramientas CLI en macOS y es usado por muchos desarrolladores
- Sin embargo, muchas veces las CLI creadas por uno mismo se distribuyen como npm o RubyGem, y la forma de distribución con Homebrew puede sentirse poco familiar
- El repositorio core oficial de Homebrew tiene la política de no aceptar el registro de herramientas caseras, por lo que los desarrolladores comunes distribuyen con un tap y una formula por separado
- Esta guía se basa en una experiencia sencilla de distribución de una CLI hecha en Ruby
Explicación de términos
- Homebrew usa una terminología propia inspirada en la elaboración de cerveza, así que entenderla facilita captar la estructura del sistema
- Formula es un archivo de definición de paquete que incluye instrucciones para instalar código fuente o binarios
- Tap es un repositorio Git de formulas, usado para administrar paquetes personalizados por usuario u organización
- Cask es un manifiesto de instalación para apps GUI o binarios grandes; es similar a Formula, pero maneja archivos precompilados
- Bottle es un paquete binario precompilado que se copia en lugar de compilar desde el código fuente, lo que acelera la instalación
- Cellar es el directorio donde se ubican las formulas instaladas; por ejemplo, puede estar en
/opt/homebrew/Cellar - Keg es el directorio de instancia instalada de una Formula específica, organizado por versión dentro del Cellar
Resumen general
- Como el repositorio core de Homebrew no acepta contenido de nicho o enviado de forma personal, los usuarios deben crear un repositorio tap separado para distribuir su CLI
- 1. Crear la CLI, subirla a GitHub y hacer un release con tag
- 2. Crear el Tap con
brew tap-newy hacer push a GitHub - 3. Crear la Formula con
brew create(incluyendo la URL del tarball y el SHA256) - 4. Actualizar la Formula en cada nueva versión para que los usuarios puedan instalarla fácilmente con
brew install
- Una vez terminada la distribución, los usuarios pueden instalar la CLI con dos comandos:
brew tap your_github_handle/tapybrew install your_cool_cli- Esta guía omite el desarrollo de la CLI y se enfoca en la creación del tap, la generación de la Formula y su actualización
- Como ejemplo, usa la CLI
imsg, que crea un archivo web interactivo a partir de una base de datos de iMessage
Crear el tap
- Se sigue la guía de creación de tap de Homebrew, reemplazando el nombre de usuario u organización de GitHub según corresponda
- Como se recomienda reunir todas las futuras herramientas CLI en un solo tap, se sugiere el nombre
homebrew-tap; el prefijohomebrewrecibe un tratamiento especial en la CLI y el sufijotapes una convención
- Como se recomienda reunir todas las futuras herramientas CLI en un solo tap, se sugiere el nombre
- Ejecutar el comando para crear el tap:
brew tap-new searlsco/homebrew-tap- Esto genera el scaffold en
/opt/homebrew/Library/Taps/searlsco/homebrew-tap - Luego se crea el repositorio correspondiente en GitHub y se hace push del contenido generado:
cd /opt/homebrew/Library/Taps/searlsco/homebrew-tap,git remote add origin git@github.com:searlsco/homebrew-tap.git,git push -u origin main
- Esto genera el scaffold en
- Una vez que se posee el tap, otros usuarios pueden clonar el repositorio en
/opt/homebrew/Library/Tapscon el comandobrew tap searlsco/tap- Al principio no habrá nada útil dentro, pero sí se puede confirmar el funcionamiento básico
Crear la Formula
- Aunque Homebrew puede referenciar directamente un repositorio de GitHub, recomienda usar un tarball versionado y su checksum para reforzar la reproducibilidad y la seguridad de la cadena de suministro de software libre
- GitHub aloja tarballs en URLs predecibles cuando se hace push de un tag; por ejemplo, en el repositorio
imsg, tras ejecutargit tag v0.0.5ygit push --tags, se generahttps://github.com/searlsco/imsg/archive/refs/tags/v0.0.5.tar.gz
- GitHub aloja tarballs en URLs predecibles cuando se hace push de un tag; por ejemplo, en el repositorio
- Comando para crear 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- La bandera
--tapespecifica el tap personalizado y coloca la Formula en/opt/homebrew/Library/Taps/searlsco/homebrew-tap/Formula --set-name imsgestablece explícitamente el nombre de la Formula; conviene elegir uno único para evitar duplicados (por ejemplo, tener cuidado con colisiones con CLIs existentes como TLDR o standard)--rubyes un preset de plantilla para CLI en Ruby, una de varias opciones que simplifican la personalización
- La bandera
- La Formula generada puede no funcionar al principio, así que se sugiere corregirla con ayuda de un LLM: ejecutar
brew install --verbose imsg, pasar el error a ChatGPT y repetir la actualización de la Formula- El archivo final Formula/imsg.rb puede copiarse como punto de partida para distribuir una CLI en Ruby
- Distribuir con Homebrew en vez de un gestor de paquetes específico del lenguaje permite que las actualizaciones para los usuarios sigan siendo fluidas incluso si cambia el lenguaje de implementación
Puntos destacados de la Formula
- Todas las Formulas están escritas en Ruby, porque muchas herramientas de desarrollo populares antes de JavaScript o la IA estaban basadas en Ruby
- El método
headpermite especificar un repositorio Git, aunque no está claro qué tan útil resulta en la práctica - Agregar
livecheckvale la pena porque facilita actualizar la versión de la Formula - La prueba de ejecución del binario puede implementarse de forma simple verificando la salida de ayuda; no hay que intimidarse por los comentarios generados
- Se pueden revisar errores de estilo con el comando
brew style searlsco/tap - El
uses_from_macos "ruby"predeterminado de la plantilla--rubyusa la versión 2.6.10 (un release previo al COVID y con EOL hace 3 años), por lo que se recomienda depender de la Formula más reciente condepends_on "ruby@3"
- El método
- Cuando la Formula ya esté lista, basta con
git pushpara publicarla en vivo, y los usuarios podrán instalarla conbrew tap searlsco/tapybrew install imsg
Actualizar la Formula en cada release de la CLI
- Actualizar manualmente la URL y el hash
sha256en la parte superior de la Formula en cada release es tedioso, y se señala que incluso crear tags o releases en GitHub puede volverse cansado- Es posible crear PRs con el comando
bump-formula-prde Homebrew o con acciones de GitHub, pero el proceso de fork y PR resulta innecesariamente complejo - Si se es dueño del tap, es preferible un método simple que haga commit directo a la rama main
- Es posible crear PRs con el comando
- Para evitarlo, se recomienda agregar un workflow de GitHub al repositorio de la Formula para actualizar automáticamente el tap al publicar un release
- Se puede copiar este ejemplo de workflow
- Configuración necesaria: al crear un token de acceso personal (PAT), otorgar permiso
Content→Writeal repositoriohomebrew-tap, y guardarlo en los Secrets del repositorio de la Formula comoHOMEBREW_TAP_TOKEN - Definir el tap y la Formula mediante variables de entorno (por ejemplo, en las líneas 13-15)
- Se recomienda usar la cuenta bot de GitHub para la actualización:
GH_EMAIL: 41898282+github-actions[bot]@users.noreply.github.com,GH_NAME: github-actions[bot]
- Después de crear el release y ejecutar
git push --tags, la actualización se hace automáticamente en unos segundos, y los usuarios pueden actualizar conbrew updateybrew upgrade imsg
Lo mejor de todo
- Aunque el proceso es complejo, una vez que se completa la configuración del tap y un ejemplo de Formula, publicar CLI adicionales se vuelve casi trivial
- Es muy práctico poder publicar una nueva Formula en solo unos minutos
- El proceso oficial de Homebrew es algo complicado, pero con automatización se vuelve cómodo
- Reduce la fricción entre el release y la distribución de cada herramienta, y puede ampliarse para dar soporte a CLI en distintos lenguajes
- No está claro si realmente se publicará otra Formula, pero resulta satisfactorio tener abierta esa posibilidad
2 comentarios
Existe la opción
--no-fork, que permite hacer push directamente a una rama y fusionar los cambios, además de ofrecer una función de actualización automática.Opiniones de Hacker News
A veces las reglas de nombres de Homebrew se sienten algo confusas, pero sigo notando que en general es una herramienta realmente útil.
Además, no sabía que el proceso de crear tu propio tap para distribuir herramientas fuera tan simple.
Me pregunto en qué aspectos es mejor frente a gestores de paquetes por lenguaje, como uv.
Sobre todo, quiero saber si es más fácil para gente que no está dentro de un ecosistema específico, es decir, si tiene ventaja desde el punto de vista de la universalidad.
Gracias por compartirlo; otras herramientas que usan un registro de paquetes por lo general requieren crear una cuenta, autenticación de dos factores, procesos de firma, etc.
Homebrew es mucho más simple en general porque los Términos de Servicio (ToS) de GitHub sirven como base de confianza.
Gracias a ese enfoque, el equipo de Homebrew puede evitar mucha complejidad.
Hablando de paquetes de Python, en la práctica es difícil intentar empaquetarlo todo de una sola vez, como hace uv.
Por eso, normalmente se usa un enfoque de instalar solo dependencias fijadas dentro de un entorno
venv.Como ejemplo concreto, se puede consultar esta fórmula.
Sobre uv, intenté usar las herramientas oficiales (
brew update-python-resources,homebrew-pypi-poet) para dar soporte a paquetes privados, pero no funcionó bien,así que hice uvbrew para ayudar con la generación de recursos.
También está la documentación oficial de referencia para escribir fórmulas de Python en Homebrew.
Si eres desarrollador de Go, recomiendo la herramienta Goreleaser.
Hace muy fácil distribuir binarios dentro de un tap personal (es una forma prohibida en el core oficial).
Tiene bastante utilidad para la gestión de proyectos en distintos lenguajes.
Personalmente, creo que lo ideal es gestionar las actualizaciones directamente del lado del tap.
En general, se parece a la forma en que se actualiza en upstream.
Si revisas este workflow, puedes actualizar fácilmente incluso fórmulas o casks que no te pertenecen.
Con el comando
brew bumppuedes escanear todo, crear PRs y hasta automatizar las pruebas conbrew test-bot.Puedes ver un ejemplo real de PR aquí.
Normalmente ni lo consideraba porque sentía que el tiempo de uso de GitHub Actions era un recurso demasiado valioso, pero como en open source es gratis, este uso sí suena razonable.
Yo mismo escribí homebrew-bump-revision como workflow para hacer bump automático de versiones en mi propio Homebrew tap.
Lo he estado usando bien en varios proyectos personales.
No lo intenté por flojera, pero parece una buena herramienta.
Hubo un episodio del podcast Ruby Rogues que cubría varios consejos para distribuir CLIs con Homebrew.
Se puede escuchar más en este episodio relacionado.
Encontré algo interesante sobre el empaquetado de herramientas de Python.
Algunos paquetes de Python no son compatibles con Homebrew porque durante el proceso de build se generan bucles de dependencias.
Con pip no hay problema porque descarga lanzamientos binarios, pero Homebrew compila directamente incluso todas las dependencias, así que el proceso tarda mucho más.
Por eso, incluso un proyecto de Python de tamaño medio puede tardar más de una hora en hacer un build de "bottle".
Desde que empecé a usar nix para la administración del sistema, no me he arrepentido ni una sola vez.
Lo único que extraño es esa única parte en la que todavía dependo de Windows por los juegos multijugador.