Maintaining a Homebrew Tap for Swift Projects
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
- Tag a release for your project ✻
- Create a new repository on GitHub, name it
homebrew-tap
† - Add a file called
your-project.rb
, adapting the following:
class ProjectName < Formula
desc "Describe your project"
homepage "https://github.com/your/project"
url "https://github.com/your/project/archive/1.2.3.tar.gz" def install
system "swift", "build",
"--configuration", "release",
"--disable-sandbox" bin.install '.build/release/your-project'
end
end
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
# …
end
end
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
env:
- HOMEBREW_NO_AUTO_UPDATE=1
- HOMEBREW_NO_INSTALL_CLEANUP=1deploy:
provider: script
on:
all_branches: true
condition: $TRAVIS_BRANCH =~ ^v?\d+\.\d+\.\d+$
script:
- USER=$(dirname $TRAVIS_REPO_SLUG)
- PROJ=$(basename $TRAVIS_REPO_SLUG)
- URL="https://github.com/$TRAVIS_SLUG/archive/$TRAVIS_TAG.tar.gz"
- ORIGIN="https://$GITHUB_TOKEN@github.com/$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 user.name "Travis"
- git config user.email "bot@travis-ci.com"
- 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:
- Scan GitHub periodically for new
Package.swift
which build binaries - Create formula for those repos (via a template obv.)
- Periodically scan for new releases for those repos and update the formula
Maybe you should build this idea?