-
-
Notifications
You must be signed in to change notification settings - Fork 2
Description
This issue explores an experimental helper script (bin/brew-upgrade-safe) designed to reduce the risk of unexpected upgrade cascades in Homebrew. When widely-depended-on packages (like python, openssl, etc.) are upgraded, they can trigger upgrades of many other formulae.
The goal of this script is to intercept that situation by checking for outdated dependencies and reverse-dependents before proceeding. It prompts the user when an upgrade could cascade into multiple dependent upgrades, offering a chance to cancel instead of being surprised.
Over time, this could evolve into a more robust external command aligned with Homebrew’s extension practices.
As one motivation; I often like to review the CHANGELOGs when upgrading versions of things; and the current Homebrew method of 'upgrade all the things!' doesn't really play nicely with that a lot of the time. See the following issue for a more specific solution to this:
Current Local Hacky WIP
bin/brew-upgrade-safe:
#!/usr/bin/env zsh
# ChatGPT Sources:
# 4o : https://chatgpt.com/c/67abebae-df3c-8008-be03-f7e0a1de992c
# o3-mini-high : https://chatgpt.com/c/67abf2d3-306c-8008-bf21-edc1440ee692
# TODO: This script is currently a bit of a hacky WIP
# TODO: Potentially relevant tabs I had open:
# https://www.google.com/search?q=HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK.&oq=HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK.&gs_lcrp=EgZjaHJvbWUyBggAEEUYOdIBBzUzMGowajGoAgCwAgA&sourceid=chrome&ie=UTF-8
# https://github.com/Homebrew/brew/issues/9285
# https://apple.stackexchange.com/questions/422995/running-homebrew-upgrade-specific-formulae-upgraded-a-lot-more-why
# https://chatgpt.com/c/67abebae-df3c-8008-be03-f7e0a1de992c
# https://docs.brew.sh/External-Commands
# https://github.com/Homebrew/brew/blob/cf7def0c68903814c6b4e04a55fe8f3cb3f5605e/Library/Homebrew/cmd/update.sh#L1-L10
# TODO: Potentially relevant:
# ⇒ brew deps --installed --include-build yt-dlp
# ca-certificates
# certifi
# expat
# mpdecimal
# openssl@3
# pkgconf
# python@3.12
# python@3.13
# readline
# sqlite
# xz
# ⇒ brew uses -h
# Usage: brew uses [options] formula [...]
#
# Show formulae and casks that specify formula as a dependency; that is, show
# dependents of formula. When given multiple formula arguments, show the
# intersection of formulae that use formula. By default, uses shows all
# formulae and casks that specify formula as a required or recommended
# dependency for their stable builds.
#
# Note: --missing and --skip-recommended have precedence over --include-*.
#
# --recursive Resolve more than one level of dependencies.
# --installed Only list formulae and casks that are
# currently installed.
# --missing Only list formulae and casks that are not
# currently installed.
# --eval-all Evaluate all available formulae and casks,
# whether installed or not, to show their
# dependents.
# --include-implicit Include formulae that have formula as an
# implicit dependency for downloading and
# unpacking source files.
# --include-build Include formulae that specify formula as a
# :build dependency.
# --include-test Include formulae that specify formula as a
# :test dependency.
# --include-optional Include formulae that specify formula as an
# :optional dependency.
# --skip-recommended Skip all formulae that specify formula as a
# :recommended dependency.
# --formula, --formulae Include only formulae.
# --cask, --casks Include only casks.
# -d, --debug Display any debugging information.
# -q, --quiet Make some output more quiet.
# -v, --verbose Make some output more verbose.
# -h, --help Show this message.
#: * `upgrade-safe` <package>
#:
#: Check if a package has outdated dependencies and if upgrading it will trigger other upgrades.
#: If other installed packages depend on those outdated dependencies, prompt before upgrading.
#:
#: Examples:
#: brew safe-upgrade yt-dlp
#:
#: Options:
#: -h, --help Show this help message.
# Ensure a package name is provided
if [[ -z "$1" ]]; then
echo "Usage: $0 <package>"
exit 1
fi
package="$1"
echo "Checking if any dependencies of $package are outdated..."
deps_to_upgrade=($(brew deps --installed --include-build --include-test "$package" | xargs brew outdated --formula | awk '{print $1}'))
if [[ ${#deps_to_upgrade[@]} -eq 0 ]]; then
echo "No dependencies of $package are outdated. Safe to upgrade."
brew upgrade "$package"
exit 0
fi
echo "The following dependencies of $package are outdated and will be upgraded:"
printf ' - %s\n' "${deps_to_upgrade[@]}"
echo "Checking for installed packages that depend on these dependencies..."
affected_packages=($(brew uses --installed --recursive "${deps_to_upgrade[@]}" | sort -u))
if [[ ${#affected_packages[@]} -eq 0 ]]; then
echo "No other installed packages depend on the outdated dependencies. Safe to upgrade."
brew upgrade "$package"
exit 0
fi
echo "The following installed packages depend on outdated dependencies:"
printf ' - %s\n' "${affected_packages[@]}"
read -p "Do you still want to upgrade $package? (y/N) " choice
if [[ "$choice" =~ ^[Yy]$ ]]; then
brew upgrade "$package"
else
echo "Upgrade cancelled."
fi