Automate updating custom Homebrew formulae with GitHub Actions
I have a few projects I use custom Homebrew taps with. I never remember to update the formulae versions when I update the projects. So, I finally decided to automate the process with GitHub Actions.
First, a quick detour on Homebrew taps. These additional repositories of
formulae you can install with Homebrew. You must name them with a
homebrew-
prefix. In the root of the repository, you need a Formula/
directory. This directory contains one or more .rb
files that specify how
Homebrew should install a piece of software.
Homebrew taps are useful if you want a way for other macOS users to quickly
install your software without having to download your main project repo or
curl
a bunch of files. Since they are under your control, you don’t need to
submit pull requests to the main Homebrew/homebrew-core
project. Personally,
I use them because none of my projects are all that popular.
With that out of the way, I’ll recap how I recently automated this for my
slack-notify
project.
Homebrew Tap Setup
First, I created a new repository itspriddle/homebrew-slack-notify. This
will contain the formula to install slack-notify
(via brew install
itspriddle/slack-notify/slack-notify
). The formula lives in
Formula/slack-notify.rb
and looks something like:
class SlackNotify < Formula
version "0.0.2"
homepage "https://github.com/itspriddle/slack-notify"
url "https://github.com/itspriddle/slack-notify/archive/v#{version}.tar.gz"
sha256 "2cfbed59688dbc74a1341b09f885216bf3bddd8302853cb3f4911f73373eafd4"
head "https://github.com/itspriddle/slack-notify.git"
def install
bin.install "bin/slack-notify"
man1.install "share/man/man1/slack-notify.1"
end
end
Whenever I release a new version of slack-notify
, the version number and
SHA256 checksum for the release need to be updated in the formula.
To handle updating the formula, I created this script in
.github/scripts/update-formula
in homebrew-slack-notify
:
#!/usr/bin/env bash
set -e
# Require VERSION to be set, and not blank. If missing, exit with the message
# "Must specify version".
: "${VERSION:?Must specify version}"
# Calculate the SHA256 checksum for the release. We fetch it straight from
# GitHub with `curl` and pipe the output to the `shasum` command to get a
# SHA256 checksum. That command outputs two words and we just need the first,
# so fetch it with `awk`
SHASUM=$(
curl -sL "https://github.com/itspriddle/slack-notify/archive/$VERSION.tar.gz" |
shasum -a 256 |
awk '{ print $1 }'
)
# This writes a new version of the Formula/slack-notify.rb file using the
# VERSION number and SHASUM we set above.
#
# `cat <<EOF` prints a string with the formula (i.e. the entire
# `SlackNotify` class definition down to the second `EOF` line). This is
# written to the slack-notify.rb file using `>` redirection.
# The `${0%/*}/../../Formula/slack-notify.rb` part finds the slack-notify.rb
# file relative to this update-formula file itself. `${0%/*}` is bash
# parameter expansion which deletes the first occurrence of `/*` (i.e.
# everything after the last slash) from the value of `$0`. Then we just use
# standard relative paths to go up 2 directories.
cat <<EOF > "${0%/*}/../../Formula/slack-notify.rb"
class SlackNotify < Formula
version "${VERSION#v}"
homepage "https://github.com/itspriddle/slack-notify"
url "https://github.com/itspriddle/slack-notify/archive/v#{version}.tar.gz"
sha256 "$SHASUM"
head "https://github.com/itspriddle/slack-notify.git"
def install
bin.install "bin/slack-notify"
man1.install "share/man/man1/slack-notify.1"
end
end
EOF
This script requires an environment variable to be set, VERSION
, which
specifies git tag name for the release. It fetches the release and calculates
the SHA256 checksum. Then the formula file is updated with those values.
It is run like:
VERSION=v0.0.2 .github/scripts/update-formula
Next, the actual GitHub Actions workflow
on:
workflow_dispatch:
inputs:
version:
description: 'Version'
required: true
type: string
jobs:
update-formula:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Update formula
run: |
mkdir -p Formula
VERSION='${{ github.event.inputs.version }}' ./.github/scripts/update-formula
git config --global user.name 'Joshua Priddle'
git config --global user.email 'jpriddle@me.com'
git add Formula/slack-notify.rb
git commit -m 'Updated slack-notify to ${{ github.event.inputs.version }}'
git push origin master
Note: I had to update the repository settings on GitHub to allow actions to write access.
This workflow gets triggered manually. Using Github CLI, I can run it like:
gh workflow run release.yml -f version=v0.0.2 -R itspriddle/homebrew-slack-notify
Since GitHub Actions includes GitHub CLI, the release workflow for
slack-notify
(not homebrew-slack-notify
) can trigger it. My
release.yml workflow looks like:
name: Upload zip on new tag
on:
push:
tags:
- "v*"
jobs:
release:
permissions:
actions: write
contents: write
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- name: Get version
id: version
run: echo "VERSION=${GITHUB_REF##refs/tags/}" >> "$GITHUB_ENV"
- name: Prepare zip archive
run: VERSION=${{ env.VERSION }} make archive
- name: Upload archive to release
uses: softprops/action-gh-release@v1
with:
files: pkg/slack-notify-${{ env.VERSION }}.zip
- name: Update homebrew tap
run: gh workflow run release.yml -f version=${{ env.VERSION }} -R itspriddle/homebrew-slack-notify
env:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
This workflow runs any time a tag with a prefix of v
is pushed. It creates a
zip of the release and uploads it to the GitHub release. Then it triggers the
homebrew-slack-notify
workflow with the version number of the release.
No more forgetting to update the formulae!