To make your package installable via Homebrew, you put it in a GitHub repo and create a release. Then you create a tap, a repo containing at least one formula. The formula is a Ruby file pointing to a downloadable tarball of the package, with instructions on how to build and install it.
When a user downloads your package, the tarball is saved to
/Library/Caches/Homebrew. It gets unzipped and, depending on the formula, parts of the package are copied to
/usr/local/Cellar/<pkg>/<version>. This directory, in the user’s cellar, is called a keg.
To make writing formulae easier, you can use variables for directory locations. For example,
bin.install foo will create
chmod to make sure foo is executable, and create a symlink from
/usr/local/bin/foo to foo in the cellar.
They say the right example is worth 1000 lines of documentation. Enough beating around the bush — here is notes’ directory tree, courtesy of tree:
. ├── LICENSE ├── README.md ├── _completions │ ├── c.bash │ ├── c.zsh │ └── init.sh ├── _config │ └── env.sh ├── _helpers │ └── helpers.sh └── bin └── notes
And here’s the formula for installing notes, i.e. building the notes keg:
class Notes < Formula desc "..." homepage "https://github.com/kylebebak/notes" url "https://github.com/kylebebak/notes/archive/1.0.0.tarball.gz" version "1.0.0" sha256 "e17405adc655824dec3ca6e2a9a4b199a715743ed5f0948df58f6bb369267aa3" def install bin.install "bin/notes" prefix.install Dir["_completions"] prefix.install Dir["_helpers"] prefix.install Dir["_config"] end end
install method creates a keg with the same directory tree as the one in source code, while ignoring metadata like
LICENSE, and tests or CI scripts, if I had any. The
prefix.install method copies the directories into the cellar without polluting the executable namespace under
/usr/local/bin. The only symlink created by this formula is
Once you have your tarball release and your tap on GitHub, users can install your program with two shell commands:
brew tap kylebebak/notes brew install notes
Tab completion is crucial to notes’ usability. Enabling it was the trickiest part of writing notes; I explain how in this post.