When did brew upgrade and reinstall start removing versions

I did this today:

$ brew update && brew upgrade
Updated Homebrew from 2062c5191 to 1920eecb7.
Updated 3 taps (homebrew/core, homebrew/cask and homebrew/cask-fonts).
==> Updated Formulae
node-build ✔        aws-okta            cryfs               ethereum            kakoune             mogenerator         ...
==> Upgrading 71 outdated packages:
vim 8.1.0450 -> 8.1.0850, tree 1.7.0 -> 1.8.0, ...
...
==> Upgrading curl
==> Downloading https://homebrew.bintray.com/bottles/curl-7.63.0.mojave.bottle.1.tar.gz
######################################################################## 100.0%
==> Pouring curl-7.63.0.mojave.bottle.1.tar.gz
==> Caveats
...
==> Summary
🍺  /usr/local/Cellar/curl/7.63.0: 446 files, 3.2MB
Removing: /usr/local/Cellar/curl/7.61.0... (433 files, 3.1MB)
Removing: /usr/local/Cellar/curl/7.61.1... (433 files, 3.1MB)

At this point I got very surprised. Why did it remove the old versions? When was this behaviour introduced? This is a pretty fundamental change that somehow has been introduced to my system without notice.

I didn’t notice that it was removing old versions until many old versions had been removed. It’s completely broken things like gems which are compiled against version specific libraries in kegs.

The command says nothing about removing old versions:

$ brew upgrade --help
Usage: brew upgrade [options] formula

Upgrade outdated, unpinned brews (with existing and any appended install options).

If formula are given, upgrade only the specified brews (unless they
are pinned; see pin, unpin).

    -d, --debug                      If brewing fails, open an interactive
                                     debugging session with access to IRB or a
                                     shell inside the temporary build directory
    -s, --build-from-source          Compile formula from source even if a
                                     bottle is available.
        --force-bottle               Install from a bottle if it exists for the
                                     current or newest version of macOS, even if
                                     it would not normally be used for
                                     installation.
        --fetch-HEAD                 Fetch the upstream repository to detect if
                                     the HEAD installation of the formula is
                                     outdated. Otherwise, the repository's HEAD
                                     will be checked for updates when a new
                                     stable or development version has been
                                     released.
        --ignore-pinned              Set a 0 exit code even if pinned formulae
                                     are not upgraded.
        --keep-tmp                   Don't delete the temporary files created
                                     during installation.
    -f, --force                      Install without checking for previously
                                     installed keg-only or non-migrated
                                     versions.
    -v, --verbose                    Print the verification and postinstall
                                     steps.
        --display-times              Print install times for each formula at the
                                     end of the run.
    -h, --help                       Show this message.

It also happens implicitly on reinstall, also very surprsing:

$ brew reinstall python
==> Reinstalling python
==> Downloading https://homebrew.bintray.com/bottles/python-3.7.2_1.mojave.bottle.1.tar.gz
######################################################################## 100.0%
==> Pouring python-3.7.2_1.mojave.bottle.1.tar.gz
==> /usr/local/Cellar/python/3.7.2_1/bin/python3 -s setup.py --no-user-cfg install --force --verbose --install-scripts=/usr/local/Cellar/python/3.7.2_1/bin --install-lib=/usr/loc
==> /usr/local/Cellar/python/3.7.2_1/bin/python3 -s setup.py --no-user-cfg install --force --verbose --install-scripts=/usr/local/Cellar/python/3.7.2_1/bin --install-lib=/usr/loc
==> /usr/local/Cellar/python/3.7.2_1/bin/python3 -s setup.py --no-user-cfg install --force --verbose --install-scripts=/usr/local/Cellar/python/3.7.2_1/bin --install-lib=/usr/loc
==> Caveats
Python has been installed as
  /usr/local/bin/python3

Unversioned symlinks `python`, `python-config`, `pip` etc. pointing to
`python3`, `python3-config`, `pip3` etc., respectively, have been installed into
  /usr/local/opt/python/libexec/bin

If you need Homebrew's Python 2.7 run
  brew install python@2

You can install Python packages with
  pip3 install <package>
They will install into the site-package directory
  /usr/local/lib/python3.7/site-packages

See: https://docs.brew.sh/Homebrew-and-Python
==> Summary
🍺  /usr/local/Cellar/python/3.7.2_1: 3,833 files, 59.3MB
Removing: /usr/local/Cellar/python/3.7.0... (4,824 files, 102.6MB)
Removing: /usr/local/Cellar/python/3.7.1... (8,432 files, 117.8MB)

and undocumented:

brew reinstall --help
Usage: brew reinstall [options] formula

Uninstall and then install formula (with existing and any appended install options).

    -d, --debug                      If brewing fails, open an interactive
                                     debugging session with access to IRB or a
                                     shell inside the temporary build directory
    -s, --build-from-source          Compile formula from source even if a
                                     bottle is available.
        --force-bottle               Install from a bottle if it exists for the
                                     current or newest version of macOS, even if
                                     it would not normally be used for
                                     installation.
        --keep-tmp                   Don't delete the temporary files created
                                     during installation.
    -f, --force                      Install without checking for previously
                                     installed keg-only or non-migrated
                                     versions.
    -v, --verbose                    Print the verification and postinstall
                                     steps.
        --display-times              Print install times for each formula at the
                                     end of the run.
    -h, --help                       Show this message.
2 Likes

Trying to figure out what’s going on, this is just great:

$ vim /usr/local/Homebrew/Library/Homebrew/cmd/upgrade.rb
dyld: Library not loaded: /usr/local/opt/ruby/lib/libruby.2.5.dylib
  Referenced from: /usr/local/bin/vim
  Reason: image not found
zsh: abort      vim /usr/local/Homebrew/Library/Homebrew/cmd/upgrade.rb

This is brew installed vim and ruby.

What does brew doctor show for you? Because this will be the default behavior in homebrew 2.0

Edit (it’s in the release notes on brew.sh/blog)

I am observing the same thing (issue?):
when upgrading the old versions are removed automatically and I am not talking about performing cleanup every 30 days, it just happens every time when I am performing brew upgrade.

Example of what’s happening:

➜  ~ brew upgrade
==> Upgrading 2 outdated packages:
cmake 3.13.4 -> 3.14.0, glib 2.60.0 -> 2.60.0_1
==> Upgrading cmake 
==> Downloading https://homebrew.bintray.com/bottles/cmake-3.14.0.mojave.bottle.
######################################################################## 100.0%
==> Pouring cmake-3.14.0.mojave.bottle.tar.gz
==> Caveats
Emacs Lisp files have been installed to:
  /usr/local/share/emacs/site-lisp/cmake
==> Summary
🍺  /usr/local/Cellar/cmake/3.14.0: 5,680 files, 53.2MB
Removing: /usr/local/Cellar/cmake/3.13.4... (5,543 files, 50.4MB)
==> Upgrading glib 
==> Downloading https://homebrew.bintray.com/bottles/glib-2.60.0_1.mojave.bottle
######################################################################## 100.0%
==> Pouring glib-2.60.0_1.mojave.bottle.tar.gz
🍺  /usr/local/Cellar/glib/2.60.0_1: 432 files, 15.3MB
Removing: /usr/local/Cellar/glib/2.60.0... (432 files, 15.3MB)
==> Caveats
==> cmake
Emacs Lisp files have been installed to:
  /usr/local/share/emacs/site-lisp/cmake

This prevents me from switching to the old versions of the packages. Is it the normal behaviour right now? As said, it happens on every upgrade, not only every 30 days, when the automatic brew cleanup is triggered.

Yes. This happened as-of 2.0.0. man brew can tell you how to opt-out of this behaviour.

1 Like

Thanks! If someone is looking for this it is the HOMEBREW_NO_INSTALL_CLEANUP environmental variable.

So it would be simply: export HOMEBREW_NO_INSTALL_CLEANUP=1 or any other value.

Having the auto cleanup as the default option is definitely a good idea and it’s nice that it’s possible to easily disable it if someone (like me) likes to handle it manually.

1 Like

Thanks for understanding!

1 Like

Sorry I do not understand it. Are there any benefits to doing this without notifying the user?

This is a terrible default behaviour! It should ask “Are you sure you want to delete all your configuration, etc.”. It has deleted all the configuration that I added in various directories. If the directory is user modified it should NOT delete it!!

I also do a lot of testing against various versions of software. It is very difficult to get back other versions.

One other reason not to do it is that some installs are broken. So you are deleting a working version and replacing it with a broken one. Not good.

Regards
Nick

1 Like

Sorry I do not understand it. Are there any benefits to doing this without notifying the user?

It’s in the output of the upgrade command. What other notifications do you expect?

It has deleted all the configuration that I added in various directories.

Configuration is meant to live in $HOMEBREW_PREFIX/etc and that directory should be left intact. If it isn’t you can file a bug report.

I also do a lot of testing against various versions of software. It is very difficult to get back other versions.

Homebrew will explicitly only ever support the latest version that is in the repository. Any copies of older versions should be made in a tap since Homebrew does not support this configuration.

One other reason not to do it is that some installs are broken. So you are deleting a working version and replacing it with a broken one. Not good.

That should be reported upstream and isn’t really up to homebrew to keep track of.

For you? Maybe. That’s why our documentation (man brew) tells you how to opt-out of this behaviour. We’ve certainly had more people happy with this behaviour than unhappy.

I’d suggest for your first post on our Discourse you could have considered a more positive or constructive tone.

It’s in the output of the upgrade command. What other notifications do you expect?

It’d be nice if the user got some kind of Y/N prompt and a mention of the HOMEBREW_NO_INSTALL_CLEANUP environment variable.

RTFM isn’t sufficient IMO.

I’m very grateful for the Homebrew ecosystem as it streamlines my workflow

However I do have to add that this is very toxic to user experience as I just had an issue with that.
RTFM is simply not enough in this specific case and it goes against any kind of normal intuitions.
Please fix this with a prompt or change the default behavior during brew upgrade

No-one has told anyone to RTFM. We have specified that this was pre-announced behaviour in release notes and is documented in the documentation.

What you consider “toxic to the user experience” many users over the years felt the opposite (cleanup was never run automatically so Homebrew slowly filled their entire disk).

Choosing defaults is hard and the very nature of them is that you may not agree but it’s our job to decide what best fits the majority of our users. We’ve provided you with a documented option to change this behaviour: I suggest using it.

brew never prompts the user for input, it’s simply not how we (or many other CLIs) do things. That’s not something we’re going to change now, sorry.

1 Like