Como distribuir seus próprios scripts via Homebrew
(justin.searls.co)- O Homebrew é um gerenciador de pacotes que facilita a instalação e o gerenciamento de ferramentas de CLI no macOS, permitindo que desenvolvedores configurem o ambiente do sistema de forma eficiente com ferramentas usadas com frequência
- Este guia explica o processo de distribuir scripts pessoais de CLI usando o Homebrew e mostra como simplificar a manutenção com integração com GitHub e fluxos de trabalho automatizados
- O processo de distribuição segue criação da CLI → release no GitHub → criação de Tap → criação e atualização da Formula e, no fim, permite a instalação apenas com os comandos
brew tapebrew install - Entender a terminologia e as boas práticas do Homebrew permite fazer uma distribuição estável com mais reprodutibilidade e segurança da cadeia de suprimentos
- Também é possível automatizar com GitHub Actions e, depois de configurar uma vez, a principal vantagem é que distribuir outras CLIs fica muito mais simples
Contexto e motivação
- O Homebrew é o gerenciador de pacotes preferido para instalar ferramentas de CLI no macOS e é usado por muitos desenvolvedores
- Porém, ao distribuir uma CLI criada por você, é comum usar npm ou RubyGems, e o processo de distribuição via Homebrew pode parecer pouco familiar
- O repositório core oficial do Homebrew evita registrar ferramentas autorais, então desenvolvedores em geral distribuem por meio de um tap e uma formula separados
- Este guia se baseia em uma experiência simples de distribuição de uma CLI em Ruby
Explicação dos termos
- O Homebrew usa uma terminologia própria inspirada no tema de fabricação de cerveja, e entendê-la ajuda a compreender a estrutura do sistema
- Formula é o arquivo de definição do pacote, contendo instruções para instalar código-fonte ou binários
- Tap é um repositório Git de Formulas, usado para gerenciar pacotes customizados por usuário ou organização
- Cask é um manifesto de instalação para apps com GUI ou binários grandes; é parecido com Formula, mas lida com arquivos pré-compilados
- Bottle é um pacote binário pré-compilado copiado em vez de compilado do código-fonte, acelerando a instalação
- Cellar é o diretório onde ficam as Formulas instaladas, por exemplo em
/opt/homebrew/Cellar - Keg é o diretório de uma instância instalada de uma Formula, organizado por versão dentro do Cellar
Visão geral
- Como o repositório core do Homebrew não aceita conteúdos de nicho ou enviados por indivíduos, os usuários precisam criar um repositório tap separado para distribuir sua CLI
- 1. Criar a CLI, enviar ao GitHub e fazer um release com tag
- 2. Criar um Tap com
brew tap-newe fazer push para o GitHub - 3. Criar a Formula com
brew create(incluindo URL do tarball e SHA256) - 4. A cada nova versão, atualizar a Formula para que os usuários possam instalar facilmente com
brew install
- Depois da distribuição, o usuário pode instalar a CLI com dois comandos:
brew tap your_github_handle/tapebrew install your_cool_cli- Este guia não aborda o desenvolvimento da CLI em si; o foco está na criação do tap, na criação da Formula e no processo de atualização
- Como exemplo, é usada a CLI
imsg, que cria um arquivo web interativo a partir de um banco de dados do iMessage
Criação do tap
- Siga o guia de criação de tap do Homebrew, substituindo pelo seu nome de usuário ou organização no GitHub
- Para reunir todas as CLIs futuras em um único tap, recomenda-se o nome
homebrew-tap; o prefixohomebrewrecebe tratamento especial na CLI e o sufixotapé convencional
- Para reunir todas as CLIs futuras em um único tap, recomenda-se o nome
- Execute o comando de criação do tap:
brew tap-new searlsco/homebrew-tap- Isso cria o scaffold em
/opt/homebrew/Library/Taps/searlsco/homebrew-tap - Crie o repositório correspondente no GitHub e faça push do conteúdo gerado:
cd /opt/homebrew/Library/Taps/searlsco/homebrew-tap,git remote add origin git@github.com:searlsco/homebrew-tap.git,git push -u origin main
- Isso cria o scaffold em
- Depois que o tap estiver sob seu controle, outros usuários poderão clonar o repositório com
brew tap searlsco/tappara colocá-lo em/opt/homebrew/Library/Taps- No início, ele ainda não terá nada útil, mas já permite verificar o funcionamento básico
Criação da Formula
- O Homebrew pode apontar diretamente para um repositório GitHub, mas recomenda usar um tarball versionado com checksum, aumentando a reprodutibilidade e a segurança da cadeia de suprimentos de open source
- O GitHub hospeda tarballs em URLs previsíveis quando uma tag é enviada; por exemplo, no repositório
imsg, após executargit tag v0.0.5egit push --tags, será geradohttps://github.com/searlsco/imsg/archive/refs/tags/v0.0.5.tar.gz
- O GitHub hospeda tarballs em URLs previsíveis quando uma tag é enviada; por exemplo, no repositório
- Comando para criar a Formula:
brew create https://github.com/searlsco/imsg/archive/refs/tags/v0.0.5.tar.gz --tap searlsco/homebrew-tap --set-name imsg --ruby- A flag
--tapespecifica o tap customizado e coloca a Formula em/opt/homebrew/Library/Taps/searlsco/homebrew-tap/Formula --set-name imsgdefine explicitamente o nome da Formula; escolha um nome único para evitar duplicações (por exemplo, cuidado com conflitos com a CLI TLDR ou standard já existentes)--rubyé um preset de template para CLIs em Ruby e é uma das várias opções que simplificam a customização
- A flag
- A Formula gerada pode não funcionar de início, então vale usar um LLM para ajustar: execute
brew install --verbose imsg, envie os erros ao ChatGPT e repita a atualização da Formula- O arquivo final Formula/imsg.rb pode ser copiado como ponto de partida para distribuir CLIs em Ruby
- Distribuir via Homebrew, em vez de usar um gerenciador de pacotes específico de linguagem, permite trocar a linguagem de implementação sem atrito para os usuários na hora de atualizar
Principais destaques da Formula
- Todas as Formulas são escritas em Ruby, já que muitas ferramentas de desenvolvimento populares antes da era do JavaScript ou da IA eram baseadas em Ruby
- É possível especificar um repositório Git com o método
head, embora o efeito real seja incerto - Adicionar
livecheckvale a pena porque facilita a atualização de versão da Formula - O teste de execução do binário pode ser implementado de forma simples verificando a saída de ajuda; não se intimide com os comentários gerados automaticamente
- Use o comando
brew style searlsco/tappara verificar erros de estilo - O padrão
uses_from_macos "ruby"do template--rubyusa a versão 2.6.10 (um release anterior à COVID e com EOL há 3 anos), então é recomendável depender da Formula mais atual comdepends_on "ruby@3"
- É possível especificar um repositório Git com o método
- Quando a Formula estiver satisfatória, faça
git pushpara publicá-la; então os usuários poderão instalar combrew tap searlsco/tapebrew install imsg
Atualização da Formula a cada release da CLI
- Atualizar manualmente a
urle o hashsha256no topo da Formula a cada release é trabalhoso, e o texto observa que até criar tags ou releases no GitHub acaba sendo cansativo- É possível usar o comando
bump-formula-prdo Homebrew ou GitHub Actions para gerar um PR, mas o processo de fork e PR é desnecessariamente complexo - Se você é dono do tap, o ideal é um método simples que faça commit direto na branch main
- É possível usar o comando
- Para evitar isso, recomenda-se adicionar um workflow do GitHub ao repositório da Formula para atualizar o tap automaticamente no momento do release
- Você pode copiar o exemplo de workflow
- Configuração necessária: ao criar um token de acesso pessoal (PAT) do GitHub, conceda permissão
Content→Writeao repositóriohomebrew-tape salve-o comoHOMEBREW_TAP_TOKENnos Secrets do repositório da Formula - Defina o tap e a Formula com variáveis de ambiente (por exemplo, nas linhas 13–15)
- Recomenda-se usar a conta de bot do GitHub para as atualizações:
GH_EMAIL: 41898282+github-actions[bot]@users.noreply.github.com,GH_NAME: github-actions[bot]
- Depois de criar um release e executar
git push --tags, a atualização acontece automaticamente em poucos segundos, e os usuários podem atualizar combrew updateebrew upgrade imsg
A melhor parte
- O processo parece complexo, mas depois de configurar o tap e concluir um exemplo de Formula, distribuir CLIs adicionais se torna quase trivial
- É conveniente poder publicar uma nova Formula em apenas alguns minutos
- O processo oficial do Homebrew é um pouco complicado, mas a automação o torna muito mais confortável
- Isso reduz o atrito entre o release da ferramenta e sua distribuição, além de permitir expandir o suporte para CLIs em várias linguagens
- Não se sabe se outra Formula será publicada de fato, mas é satisfatório ter essa possibilidade aberta
2 comentários
Existe a opção
--no-fork, que permite fazer push direto para a branch e realizar o merge, além de oferecer um recurso de atualização automática.Comentários do Hacker News
Às vezes acho as convenções de nomenclatura do Homebrew um pouco confusas, mas continuo sentindo que, no geral, é uma ferramenta realmente útil
Também não imaginava que o processo de criar seu próprio tap para distribuir ferramentas fosse tão simples
Fico curioso sobre em que aspectos isso é melhor em comparação com gerenciadores de pacotes específicos por linguagem, como o uv
Queria saber especialmente se isso é mais fácil para quem não está dentro de um ecossistema específico, ou seja, se leva vantagem do ponto de vista da generalidade
Agradeço por mencionar isso; outras ferramentas que usam registros de pacotes geralmente exigem criação de conta, autenticação em dois fatores, processo de assinatura etc.
O Homebrew é muito mais simplificado no geral porque os Termos de Serviço (ToS) do GitHub funcionam como base de confiança
A equipe do Homebrew consegue reduzir bastante a complexidade graças a esse modelo
Falando em pacotes Python, tentativas como o uv, de empacotar tudo de uma vez, são difíceis na prática
Por isso, em geral, usa-se a abordagem de instalar apenas dependências fixadas em um ambiente
venvComo exemplo concreto, dá para consultar esta fórmula
Sobre o uv, tentei usar as ferramentas oficiais (
brew update-python-resources,homebrew-pypi-poet) para dar suporte a pacotes privados, mas não funcionou direito,então acabei criando o uvbrew para ajudar na geração de recursos
Há também a documentação oficial para consultar ao escrever fórmulas Python no Homebrew
Se você é desenvolvedor Go, recomendo a ferramenta Goreleaser
Ela facilita muito a distribuição de binários em um tap pessoal (esse método é proibido no core oficial)
Tem bastante utilidade na gestão de projetos de diferentes linguagens
Pessoalmente, acho mais ideal gerenciar as atualizações diretamente do lado do tap
Em geral, é parecido com a forma como se atualiza no upstream
Consultando este workflow, dá para atualizar facilmente fórmulas/casks que você nem possui
Com o comando
brew bump, é possível escanear tudo, abrir PRs e até automatizar os testes combrew test-botUm exemplo real de PR pode ser visto aqui
Normalmente eu nem considerava isso porque achava ruim gastar tempo de uso do GitHub Actions, mas como em open source é grátis, esse tipo de uso parece bem válido
Cheguei a escrever meu próprio workflow automático de bump de versão para tap do Homebrew, o homebrew-bump-revision
Tenho usado isso bem em vários projetos pessoais
Eu não tentei por pura preguiça, mas é uma boa ferramenta
Houve um episódio do podcast Ruby Rogues que cobriu várias dicas para distribuir CLIs com Homebrew
Dá para ouvir mais no link do episódio relacionado
Descobri um ponto interessante sobre empacotamento de ferramentas Python
Alguns pacotes Python acabam criando ciclos de dependência durante o processo de build, o que os torna incompatíveis com o Homebrew
O pip não sofre com isso porque baixa releases binárias, mas o Homebrew compila diretamente até as dependências, então esse processo leva bem mais tempo
Por isso, até um projeto Python de porte médio pode levar mais de uma hora para gerar uma "bottle"
Desde que comecei a usar nix para administração do sistema, nunca me arrependi nem uma vez
A única coisa de que sinto falta é que ainda preciso depender do Windows por causa de jogos multiplayer