Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save barseghyanartur/02ac4c3c2b2bd1dd0d896f9c8e13b9bd to your computer and use it in GitHub Desktop.

Select an option

Save barseghyanartur/02ac4c3c2b2bd1dd0d896f9c8e13b9bd to your computer and use it in GitHub Desktop.
blog: Managing Homebrew upgrades: The selective lazy method

Managing Homebrew upgrades: the selective lazy method

Date: 2026-05-07 09:46
category:Tech
tags:macos, homebrew, productivity
summary:Automated upgrade scripts are great until a critical tool breaks. Learn how to pin specific packages and use filtered commands to keep your environment stable without losing your "lazy" workflow.

The problem

Automating package updates is the dream for any developer who values their time. However, the "bleeding edge" can sometimes cut you. Recently, my preferred terminal, Tabby, broke following a series of macOS updates.

When a core tool in your workflow fails after an unattended upgrade, it halts productivity. While Homebrew allows you to "pin" standard formulae to prevent them from updating, there is currently no native "pin" or "hold" feature for Homebrew Casks. This means a blanket upgrade command will always try to pull the latest (and potentially broken) version of your GUI applications.

The (imperfect) solution

To keep using a specific version of a Cask while still upgrading everything else, you can use a filtered command. Instead of a blind global upgrade, you can list outdated casks and exclude the problematic one using grep.

For Tabby, the following command ensures everything else moves forward while the terminal remains untouched:

# Upgrade all casks except Tabby
brew upgrade --cask --greedy $(brew outdated --cask --greedy | grep -v "tabby")

For standard CLI tools (formulae), you should use the built-in pinning mechanism:

# Prevent a formula from being upgraded
brew pin <package_name>

Why this improves stability

The "stay lazy" workflow

Most of us use a "one-liner" to keep our systems current. My personal "lazy" script handles Homebrew, Python tools, and specific app fixes in one go:

brew update || true; brew upgrade || true; brew upgrade --cask --greedy $(brew outdated --cask --greedy | grep -v "tabby") || true; brew cleanup -s || true; uv tool upgrade --all || true; xattr -cr /Applications/Chromium.app
Targeted exclusion
By adding the grep -v logic to the middle of the chain, you maintain the automation. You don't have to remember to skip Tabby manually; the script handles the logic for you.
Granular control
This approach bridges the gap between the robust pin feature available for formulae and the lack of such a feature for Casks. It ensures that known-stable versions of GUI apps stay put until you are ready to manually verify a fix.

The perfect solution that does not yet exist

If only the Homebrew maintainers would implement a native brew cask pin command. Having a unified way to freeze versions for both command-line tools and applications would eliminate the need for complex bash one-liners and custom grep filters.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment