Downgrade a homebrew package to a version no longer installable

 · 5 min · torgeir

Create you own local tap repository, extract an old formula and install it with brew.

Homebrew Pkgs Emacs Gnupg

Edit: So, it turns out still you can use with the -s parameter to install from source, with brew install -s <downloaded formulae>. That’s quite a bit simpler than the exploration that follows below. [2023-05-05 Fri]

Edit 2: I needed to do this again, so here it is, in more detail [2023-10-07 Sat]

wget https://raw.githubusercontent.com/Homebrew/homebrew-core/59edfe598541186430d49cc34f42671e849e2fc9/Formula/gnupg.rb
brew uninstall gnupg
brew unlink gnupg
brew install -s gnupg.rb
brew link [email protected]
brew pin [email protected]

Yesterday I ran brew upgrade. Shortly after, I noticed emacs org mode files would no longer save. Specifically the ones I encrypt with GPG. Org-mode transparently handles encryption of files annotated with a line pointing to the email associated with your GPG key

# -*- mode:org; epa-file-encrypt-to: ("[email protected]") -*-

But now the editor would just hang. As I have been living my life in plain text org mode files since some time back in 2015 this was kind of annoying.

I noticed gnupg, that bundles gpg, was upgraded from 2.4.0 to 2.4.1 during the brew upgrade and was immediately suspicous. Later, a fellow redditor suggested downgrading gnupg to 2.4.0 would resolve the issue. As a temporary solution this will suffice.

Turns out downgrading to 2.4.0 was not that easy, as only [email protected] and [email protected] are installable from the current homebrew repos. [email protected] will give you 2.4.1 and [email protected] a version all the way back to 2.2, which seems overkill.

Previously, you could install brew packages directly from a raw github formulae link, but that no longer works. The error message you get suggest another approach, that also no longer works. The gif was spot on, though..

Heck, I just wanted to save a file.

Downgrading to a specific SHA of a homebrew formulae

This homebrew discussion holds a solution that actually still works.

To downgrade a package on homebrew to a formulae version from a specific git SHA, you need to go through the following steps.

Remove the installed version

As the gnupg package has other dependencies on my system, brew needs a flag to believe you really mean to remove it

brew uninstall --ignore-dependencies gnupg
Uninstalling /usr/local/Cellar/gnupg/2.4.1... (140 files, 12.0MB)

Otherwise it will fail with

Error: Refusing to uninstall /usr/local/Cellar/gnupg/2.4.1
because it is required by gmime, gpgme and mu, which are currently installed.
You can override this and force removal with:
brew uninstall --ignore-dependencies gnupg

Create a your own local homebrew tap

This creates a git repository on your local machine, that will hold the formulae you are about to locate, so you can install it afterwards.

brew tap-new torgeir/local-gnupg
Initialized empty Git repository in /usr/local/Homebrew/Library/Taps/torgeir/homebrew-local-gnupg/.git/
[main (root-commit) 11ac64d] Create torgeir/local-gnupg tap
3 files changed, 90 insertions(+)
create mode 100644 .github/workflows/publish.yml
create mode 100644 .github/workflows/tests.yml
create mode 100644 README.md
==> Created torgeir/local-gnupg
/usr/local/Homebrew/Library/Taps/torgeir/homebrew-local-gnupg

When a pull request making changes to a formula (or formulae) becomes green
(all checks passed), then you can publish the built bottles.
To do so, label your PR as `pr-pull` and the workflow will be triggered.

As I sign my commits with GPG the above also did not work, as I had just removed GPG 🤦 It actually commits in the tap’s repo that is created, so I had to temporarily disable git commit signing to make this work..

Fetch the tap’s repo

Figure out that you don’t have the homebrew/core tap locally

file "$(brew --repo homebrew/core)"
/usr/local/Homebrew/Library/Taps/homebrew/homebrew-core: cannot open
  `/usr/local/Homebrew/Library/Taps/homebrew/homebrew-core' (No such file or directory)

You need this to get hold of the old version of the formulae.

brew tap homebrew/core
==> Tapping homebrew/core
Cloning into '/usr/local/Homebrew/Library/Taps/homebrew/homebrew-core'...
remote: Enumerating objects: 1479785, done.
remote: Counting objects: 100% (262040/262040), done.
remote: Compressing objects: 100% (657/657), done.
remote: Total 1479785 (delta 261473), reused 261395 (delta 261383), pack-reused 1217745
Receiving objects: 100% (1479785/1479785), 349.30 MiB | 12.69 MiB/s, done.
Resolving deltas: 100% (1053318/1053318), done.
Updating files: 100% (6999/6999), done.
Tapped 2 commands and 6648 formulae (7,010 files, 405.8MB).
==> Downloading https://formulae.brew.sh/api/formula.jws.json
####################################################################### 100.0%

Move into the repo and checkout the commit

I browsed the Homebrew/homebrew-core/Formula/gnupg.rb history to locate the version I wanted to roll back to. In my case 59edfe598541186430d49cc34f42671e849e2fc9 was the relevant commit.

cd /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core
git checkout 59edfe598541186430d49cc34f42671e849e2fc9
Updating files: 100% (3340/3340), done.
Note: switching to '59edfe598541186430d49cc34f42671e849e2fc9'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:

git switch -c <new-branch-name>

Or undo this operation with:

git switch -

Turn off this advice by setting config variable advice.detachedHead to false

HEAD is now at 59edfe59854 gnupg: update 2.4.0 bottle.

Extract the formulae to your local tap

Extract a named formulae from the visited repo into your newly created local tap repo. Give it a version number. I chose 2.4.0.

brew extract --version=2.4.0 gnupg torgeir/homebrew-local-gnupg
==> Searching repository history
==> Writing formula for gnupg from revision 59edfe5 to:
/usr/local/Homebrew/Library/Taps/torgeir/homebrew-local-gnupg/Formula/[email protected]

Install your downgraded package

Do this by installing the extracted formulae, like you normally would, just by refering the full formulae path instead.

brew install /usr/local/Homebrew/Library/Taps/torgeir/homebrew-local-gnupg/Formula/[email protected]
Error: Failed to load cask: /usr/local/Homebrew/Library/Taps/torgeir/homebrew-local-gnupg/Formula/[email protected]
Cask '[email protected]' is unreadable: wrong constant name #<Class:0x00007f8183146b38>
Warning: Treating /usr/local/Homebrew/Library/Taps/torgeir/homebrew-local-gnupg/Formula/[email protected] as a formula.
==> Fetching torgeir/local-gnupg/[email protected]
==> Downloading https://gnupg.org/ftp/gcrypt/gnupg/gnupg-2.4.0.tar.bz2
####################################################################### 100.0%
==> Installing [email protected] from torgeir/local-gnupg
==> ../configure --disable-debug --disable-dependency-tracking --prefix=/usr/l
==> make
==> make check
==> make install
🍺  /usr/local/Cellar/[email protected]/2.4.0: 150 files, 13.7MB, built in 8 minutes 28 seconds
==> Running `brew cleanup [email protected]`...
Disable this behaviour by setting HOMEBREW_NO_INSTALL_CLEANUP.
Hide these hints with HOMEBREW_NO_ENV_HINTS (see `man brew`).

On second thoughts, a simple brew install [email protected] would also probably work at this point.

It works, now pin it

You’re done.

gpg --version
gpg (GnuPG) 2.4.0

Pin it to prevent it from being upgraded on your next brew upgrade


Edit: