Managing All Software on Your Mac with Homebrew
Over the years I’ve used a Mac, the software I install has become increasingly messy. Some came as DMGs downloaded from official websites, some were installed from the App Store, some are command-line tools installed with Homebrew, and some were casually slipped in by some installer. Over time, I can no longer clearly say what is actually installed on my computer or where each piece of software came from.
Deleting software is also a hassle. Uninstalling an app on a Mac usually just means dragging it to the Trash, but under ~/Library, the configuration, caches, and various support files it leaves behind are not deleted along with it. So after a computer has been used for a long time, ~/Library accumulates a pile of leftovers from software that hasn't been used in ages. In the end, I could only use an expensive tool like CleanMyMac to clean it up, or go hunting for them one by one myself.
On top of that, I recently needed to move my environment to a new Apple Silicon Mac, and if I had to download and reinstall everything one by one from official websites as before, it would be too exhausting. So I decided that from now on, for any software on my Mac that Homebrew can install, I’ll install it all with Homebrew and then record everything in a Brewfile.
Installing Homebrew on Apple Silicon
Homebrew has natively supported Apple Silicon since 3.0, and the installation command is the same as before.
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
However, there is one difference from Intel Macs: on Apple Silicon, Homebrew is installed by default in /opt/homebrew, rather than the old /usr/local. Therefore, /opt/homebrew/bin is not in PATH by default, so after installation you need to add this line to ~/.zprofile.
eval "$(/opt/homebrew/bin/brew shellenv)"
brew shellenv outputs a set of environment variables, including PATH, MANPATH and the like, so running it with eval sets them all up.
Formulas and Casks
Software in Homebrew is divided into two types. Formulas are command-line tools and libraries, such as git, ripgrep, while casks are applications with graphical interfaces, such as Chrome and VS Code.
brew install ripgrep
brew install --cask visual-studio-code
In the past, installing a cask used brew cask install, but starting with 2.6.0 at the end of last year, all brew cask commands were deprecated and uniformly replaced with the brew command plus --cask, so the syntax in some older tutorials online no longer works.
Fonts can also be installed via cask, but you first need to add the homebrew/cask-fonts tap.
brew tap homebrew/cask-fonts
brew install --cask font-jetbrains-mono
Uninstalling More Cleanly
One big advantage of managing casks with Homebrew is uninstalling. A normal uninstall only deletes the app itself, much like dragging it to the Trash.
brew uninstall --cask some-app
But after adding --zap, Homebrew will also follow the zap rules defined in the cask and, for this app, under ~/Library, delete the preferences, caches, and support files it leaves behind.
brew uninstall --zap --cask some-app
Of course, not every cask defines zap rules, but most commonly used software does. At least it's much more reliable than me going through ~/Library one by one myself.
Brewfile
If I only used Homebrew to install software, I would still have to remember what I installed when migrating. Fortunately, Homebrew has a bundle subcommand that can export everything currently installed into a Brewfile.
brew bundle dump --describe --force --file=~/dotfiles/Brewfile
--describe adds a brief description above each line, so when I look back later, I won't stare at an unfamiliar name and be unable to remember what it does. --force overwrites the existing file. The exported Brewfile looks roughly like this.
tap "homebrew/bundle"
tap "homebrew/cask"
tap "homebrew/cask-fonts"
tap "homebrew/core"
# Clone of cat(1) with syntax highlighting and Git integration
brew "bat"
# Modern replacement for 'ls'
brew "exa"
# Simple, fast and user-friendly alternative to find
brew "fd"
# Command-line fuzzy finder written in Go
brew "fzf"
# GitHub command-line tool
brew "gh"
# Search tool like grep and The Silver Searcher
brew "ripgrep"
# Mac App Store command-line interface
brew "mas"
# Password manager that keeps all passwords secure behind one password
cask "1password"
cask "font-jetbrains-mono"
# Web browser
cask "google-chrome"
# Terminal emulator as alternative to Apple's Terminal app
cask "iterm2"
# Control your tools with a few keystrokes
cask "raycast"
# Messaging app with a focus on speed and security
cask "telegram"
# Open-source code editor
cask "visual-studio-code"
mas "Xcode", id: 497799835
The mas on the last line is the App Store command-line tool. Some software is only available on the App Store, such as Xcode; it can also be included in the Brewfile this way, and the number after it is its App Store ID.
I put this Brewfile in my dotfiles repository. Whenever I install or remove something, I dump it again and commit. That way, the repository always contains a complete list of the software currently on my computer.
Restoring on a New Computer
On the new computer, after installing Homebrew and cloning the dotfiles repository, one command can install everything in the Brewfile.
brew bundle --file=~/dotfiles/Brewfile
It will add taps, install formulas and casks in sequence, and skip anything already installed, so it's safe to rerun even if it gets interrupted. With many casks, the whole process takes a while, but you don't have to pay attention to it at all; it's far less hassle than downloading and installing everything one by one.
Cleaning Up in Reverse
A Brewfile can be used not only for installation but also for reverse cleanup. brew bundle cleanup lists things that are installed on the computer but not written into the Brewfile.
# 只列出来,不删除
brew bundle cleanup --file=~/dotfiles/Brewfile
# 确认没问题之后再真的删除
brew bundle cleanup --force --file=~/dotfiles/Brewfile
So sometimes I work in reverse: I delete unwanted software directly from the Brewfile, then run cleanup again, and it is removed from the computer as well. This way, the Brewfile and the state of the computer can stay consistent.
Also, brew leaves is very useful. It only lists formulas that I installed myself and that were not pulled in as dependencies of other formulas. When I want to see exactly which tools I installed, it is much clearer than brew list.
Some Habits
- If an app can be installed via cask, I no longer download its DMG from the official website.
- For apps available only on the App Store, I install them with
masand also write them into the Brewfile. - Every time I finish installing or removing software, I dump the Brewfile once and commit it.
- For updates, I use
brew upgradefor everything; casks are updated too, and only apps that update themselves need the extra--greedy.
Keeping this up means every piece of software on my computer has a known source, and switching computers only requires one repository plus one command. On the day the new computer arrived, from cloning the repository to having all the software installed, I was basically just waiting for downloads the whole time.