9 puntos por GN⁺ 2025-09-18 | 2 comentarios | Compartir por WhatsApp
  • 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 tap y brew 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-new y 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/tap y brew 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 prefijo homebrew recibe un tratamiento especial en la CLI y el sufijo tap es una convención
  • 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
  • Una vez que se posee el tap, otros usuarios pueden clonar el repositorio en /opt/homebrew/Library/Taps con el comando brew 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
  • 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 --tap especifica el tap personalizado y coloca la Formula en /opt/homebrew/Library/Taps/searlsco/homebrew-tap/Formula
    • --set-name imsg establece 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)
    • --ruby es un preset de plantilla para CLI en Ruby, una de varias opciones que simplifican la personalización
  • 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 head permite especificar un repositorio Git, aunque no está claro qué tan útil resulta en la práctica
    • Agregar livecheck vale 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 --ruby usa 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 con depends_on "ruby@3"
  • Cuando la Formula ya esté lista, basta con git push para publicarla en vivo, y los usuarios podrán instalarla con brew tap searlsco/tap y brew install imsg

Actualizar la Formula en cada release de la CLI

  • Actualizar manualmente la URL y el hash sha256 en 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-pr de 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
  • 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 ContentWrite al repositorio homebrew-tap, y guardarlo en los Secrets del repositorio de la Formula como HOMEBREW_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 con brew update y brew 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

 
lamanus 2025-09-18

Es posible crear un PR con el comando bump-formula-pr de Homebrew o con una acción de GitHub, pero el proceso de fork y PR es innecesariamente complejo

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.

 
GN⁺ 2025-09-18
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).

    • Me enteré hace poco de que Goreleaser ahora ya no solo soporta Go, sino también Rust, TypeScript, Python, Zig y varios lenguajes más.
      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 bump puedes escanear todo, crear PRs y hasta automatizar las pruebas con brew test-bot.
    Puedes ver un ejemplo real de PR aquí.

    • Me parece una buena idea.
      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.

    • Se ve muy bien.
      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.