Maintaining a Homebrew Tap for Swift Projects

Max Howell
Feb 9 · 3 min read

I read somebody recently complaining that installing Swift projects was hard because SwiftPM has no install command. Avoiding going into my opinion on that (I don’t want SwiftPM to go there), I disagree that maintaining a Homebrew tap is hard. I do it for swift-sh, and I even have Travis set up to automatically update the tap for me so I don’t have to do anything.

For example installing swift-sh is this easy:

brew install mxcl/made/swift-sh

And Homebrew will keep it up-to-date.

Why Should I Do This?

I maintain that a super large hindrance to the success of your work is adoption pain. You need to reduce adoption pain as much as possible if you want your work to be successful, it’s that simple.

How To Make a Tap

  1. Tag a release for your project ✻
  2. Create a new repository on GitHub, name it homebrew-tap
  3. Add a file called your-project.rb, adapting the following:
class ProjectName < Formula
desc "Describe your project"
homepage ""
url ""
def install
system "swift", "build",
"--configuration", "release",
bin.install '.build/release/your-project'

4. Install it:brew install ./your-project.rb, you do this to test your formula, but also because brew will helpfully tell you the SHA256, which you then must add to the formula:

class ProjectName < Formula
desc "…"
homepage ""
url ""
sha256 "INSERT-SHA256-HERE" def install
# …

5. Commit and push

6. Try it: brew install your-github-username/tap/your-project --force

That’s it.

✻ SwiftPM requires that projects be tagged with a semantic version, eg.1.2.3 or v1.2.3.

† You can pick a different name than homebrew-tap, the homebrew prefix is required though.

--force is only required for you because you already installed your formula in step 4.

Automating Formula Updates

Maintaining metadata like this is tedious for every release, so automate it with the following Travis configuration (in your Swift project, not the tap):

os: osx
language: swift
osx_image: xcode10.2
provider: script
all_branches: true
condition: $TRAVIS_BRANCH =~ ^v?\d+\.\d+\.\d+$
- PROJ=$(basename $TRAVIS_REPO_SLUG)
- URL="$TRAVIS_SLUG/archive/$TRAVIS_TAG.tar.gz"
- ORIGIN="https://$$USER/homebrew-tap.git"
# grab the tap
- brew tap $USER/homebrew-tap
- cd /usr/local/Homebrew/Library/Taps/$USER/homebrew-tap

# update the formula URL
- sed -E -i '' 's~^ url ".+"~ url "'$URL\"~ ./swift-sh.rb
- brew fetch $PROJ || true
# ^^ fails because SHA is wrong, hence || true

# figure out the sha
- SHA256=$(shasum --algorithm 256 $(brew --cache --build-from-source $PROJ) | awk '{print $1}')

# update the formula SHA
- sed -E -i '' 's/^ sha256 ".+"/ sha256 "'$SHA256\"/ $PROJ.rb
# commit to the tap
- git remote set-url origin "$ORIGIN"
- git add $PROJ.rb
- git config "Travis"
- git config ""
- git commit -m "$PROJ $TRAVIS_TAG"
- git push origin master

You may need to adapt the variables at the top depending on how you named everything.

You also need to add a GitHub token to your Travis account (consumed above with $GITHUB_TOKEN).

Automatic Bottling

Bottles are brew’s binary downloads, which makes your users happier, but this is entirely optional for you.

Automating this took a lot of trial and error since the documentation for bottling is somewhat lacking. So please adapt what I did:

Also notably the bottles only work for Swift ≥ 5 and macOS ≥ 10.14.4 due to ABI being a thing. My formula detects if the bottle is ok to pour.

Optional Extras

You’ll see I do a little more work in my formula in order to statically link the standard library on macOS < 10.14.4, but this is optional if you only support Swift ≥ 5, and even then it’s optional (your users will need to reinstall the formula if they upgrade to Xcode 11, and probably only a small minority of people are not on the latest Xcode already).

Add Travis for Your Tap

If you’re feeling keen you can verify that your tap actually builds and that the bottle works. Here’s mine. You can see if your package works on Linux for example (yes, Homebrew supports Linux).

An Idea

Still, it’s work, and not everyone can be bothered. One idea would be for someone to do the above for all Swift projects that create binaries:

  1. Scan GitHub periodically for new Package.swift which build binaries
  2. Create formula for those repos (via a template obv.)
  3. Periodically scan for new releases for those repos and update the formula

Maybe you should build this idea?

Max Howell

Written by

Open source. Swift. Creator of Homebrew.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade