I currently have a function called updateLocal that runs
the outdated function for homebrew,
pip and yarn/npm. This runs pretty
well and does its job. Recently, homebrew has updated
it’s bottle policy to enable Mojave bottles that are built
against cpu hardware instructions that aren’t supported by my computer.
We need a new strategy.
Homebrew has support for the last three operating
systems going back to Sierra. Bottles made on the previous two
OS’s work just fine on this computer and thusly will work for the next
two years. In order to make this work we need to do some work as there
doesn’t appear to be an option to specify which bottle to use by
default. I propose a new function called upgradeLocal.
The function will run like so: get the list of outdated binaries,
loop through each one and uninstall, use brew info --json
to get the download location of other bottles, use jq to
parse the json, brew install -f with the url
to the bottle. It looks like this:
brew uninstall --ignore-dependencies libuv; brew install -f "$( brew info --json libuv | jq '.[0].bottle.stable.files.high_sierra.url' | sed s/\"//g )"; brew cleanupIt seems we need to use sed at the end to remove
quotation marks in order for the command to function. We also need to
use --ignore-dependencies as there is a good chance that
homebrew will complain otherwise. Let’s write it.
while read line; do
brew uninstall --ignore-dependencies "$line";
brew install -f "$( brew info --json $line | jq '.[0].bottle.stable.files.high_sierra.url' | sed s/\"//g )"
done < <(brew outdated)This looks reasonable but I don’t know what will happen when new
dependencies need to be installed. We could suppress any dependencies
being installed then list the dependencies and install the correct
bottle. But what if the dependency requires more dependencies that
require correct bottles? We need to check if a dependency exists first
then run a function to get the correct bottle. Gosh, wouldn’t this be
easier if we could simply specify bottle=high_sierra?
Anyway we are just dealing with lists so this should be a simple case of
recursion. This snippet should check for previously installed
binaries:
if brew ls --versions myformula > /dev/null; then
# The package is installed
else
# The package is not installed
fiLastly! We also need a means to install new bottles so adapt the upgrade script to allow the user to specify the binary to install and then run through the same procedure.
It seems like a pain to run all these checks and recursive loops to check all dependencies. It also means that our backup procedure will break as dependencies are installed as separate binaries rather than listed as dependencies. In this case, I think we should simply write a function that runs the uninstall/install process and take our chances with dependencies breaking due to cpu issues.
brinstall() {
brew uninstall --ignore-dependencies "$1"
brew install -f "$( brew info --json $1 | jq '.[0].bottle.stable.file.high_sierra.url' | sed s/\"//g )"
}
broutdated() {
while read line; do
brinstall "$line"
done < <(brew outdated)
}That seems reasonable. Funnily enough, in the two days I’ve been pondering how to do this I haven’t come across any outdated binaries to test. Still we could test the install function first with a new install. I doubt the nature of the first bottle will materially effect that of the subsequent dependency bottles…
In the main, this method worked ok. Until it didn’t work. One can
never be sure what will break and where the breakage occurs. A recent
update to node seemingly broke yarn
but node was installed with the High Sierra
bottle. Hmm, a dependency issue. Dependencies did indeed turn out to be
problematic but they weren’t the only issue. Some packages aren’t a
single binary and the uninstall command also breaks. The solution was
actually very simple but still annoying. Digging around the
homebrew folders yielded the solution. In the file
brew.sh is a variable that checks for the operating system
version in the form “10.14.3”. I merely hardcoded that variable to the
last version of High Sierra. And that did it. The last
remaining issue is the obvious one: what happens when
homebrew updates itself? Homebrew
updates ALL THE TIME.
When attempting to install something, Homebrew
updates before installing and stashes the changes made to the
repository. This is solved by adding the variable
set -gx HOMEBREW_NO_AUTO_UPDATE 1 to your shell config
file. The next step is to reapply the stashed changes after an
update.
function modBrew --description 'Reapply an homebrew edit after an update'
cd /usr/local/Homebrew && git stash pop && cd -
endFor my own sanity I wrote this quick (fish) function
that I run whenever there’s a message that my changes have been stashed
which is exclusively when I run updateLocal.
Now all bottles are of the High Sierra flavour and run like a charm. Huzzah.