Bokeh

Making sense of the mess in my head

Rolling back a brew formula

Introduction

I find Homebrew super convenient to install and keep up to date the large collection of tools I rely on. But sometimes, things do not work out the way you plan.

Warning

At the time of this writing, a malware site masquerading as the Homebrew site is being spread through Google Ads, see this post on X by Ryan Chenkie for more details.

Some weeks ago, I was cleaning up the code for an Embedded Swift project before publishing it on GitHub, and as a last check, I wanted to verify everything still worked fine with the latest versions of the various components: latest Swift snapshot toolchain, nRF Connect SDK 2.9.0 and latest cmake version.
Since I had installed cmake using brew, I did a brew upgrade and this updated cmake from 3.31.1 to 3.31.2.

Suddenly, things began to fall apart. I could not complete a successful build after those updates.
I first rolled back nRF Connect SDK as this was probably where the biggest changes did occur. This did not help.
Next I tried with a previous Swift toolchain, which did not help either.
This pointed to cmake as the most probable culprit, so I wanted to roll it back.
And that proved to be more difficult than I thought.

TL;DR

In the rest of this post, I will outline my research process, the challenges I encountered, and the steps I followed to resolve them.

If you are not interested in the whole story, the following commands should work to install a specific version of a helm formula

brew tap-new $USER/local-cmake
brew tap homebrew/core --force
brew extract --version=3.31.1 cmake $USER/local-cmake
brew install $USER/local-cmake/cmake@3.31.1

Trying to find an easy way

I opened a terminal and typed brew --help, hoping to find a revert/rollback command but I did not find any such option, only a full uninstall command.

So I turned to Google; and found several posts indicating how to install a specific version of a package, but most of those posts were outdated and referenced commands and ways to proceed that do not work anymore. For instance:

  • brew versions does not work, as the versions command is not available anymore.
  • same for brew switch
  • brew list --versions <package name> does work but always returns the latest, currently installed version for me
  • using the <package name>@<version> as done when using npm does not work (it happens that some package names use this format e.g. postgresql@14, but this is their naming convention, not a generic mechanism used by brew)

And indeed, brew --help install does not indicate any way to install a specific version.

Going the complex route

I found several posts indicating the possibility to install a specific version (using the install command) by forging a specific URL, referencing the formula using a git commit hash, so it would point to the desired version.
Something like brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/4cfc96448e261e9b16d9b51dc6d563c717003bfd/Formula/c/cmake.rb.

However, this method is no longer supported and returns the error message

Error: Non-checksummed download of cmake formula file from an arbitrary URL is unsupported! brew extract or brew create and brew tap-new to create a formula file in a tap on GitHub instead.

So I searched online for the suggested commands and tried

 brew tap-new $USER/local-cmake
 brew extract --version=3.31.1 cmake $USER/local-cmake

The first line worked OK and created a local tap, essentially a local git repository, located under /opt/homebrew/Library/Taps, to store formulas. See How to Create and Maintain a Tap — Homebrew Documentation for some background on this command.
The second failed though, with the message

Error: No available formula with the name "homebrew/core/cmake".
Please tap it and then try again: brew tap homebrew/core

Executing the command that was suggested gave another error

brew tap homebrew/core

Error: Tapping homebrew/core is no longer typically necessary.
Add --force if you are sure you need it for contributing to Homebrew.

That error message leaves a door open. Although it does not make it super clear when you would really need to do it, the use of “typically” suggests this is still sometimes required.

I considered this was the case here

brew tap homebrew/core --force
==> Tapping homebrew/core
Cloning into '/opt/homebrew/Library/Taps/homebrew/homebrew-core'...

did indeed work, cloning the (pretty large) homebrew core repo locally.

I could now get back to the extract from above

brew extract --version=3.31.1 cmake $USER/local-cmake
==> Searching repository history
==> Writing formula for cmake at 3.31.1 from revision 4cfc964 to:
/opt/homebrew/Library/Taps/ebariaux/homebrew-local-cmake/Formula/cmake@3.31.1.rb

which indeed, extracted a specific version of the cmake formula in the local git repo that I initially created.

And with that, I was finally able to install the desired version of the formula

brew install $USER/local-cmake/cmake@3.31.1

Conclusion

In this post, I explained how to install a specific version of a brew formula. This comes in handy when performing a general brew upgrade and discovering that certain commands no longer function as expected.