Use Homebrew Bundle to manage software installation on macOS

dnsmichi

Michael Friedrich

Posted on November 30, 2020

Use Homebrew Bundle to manage software installation on macOS

Automation is iteration. First you'll find a way to make it work, and share your learnings - feedback and best practices help make it even better. When I first published my dotfiles - Document and automate your Macbook setup blog post, Nate suggested to use Homebrew bundle for a more clean management approach.

Homebrew bundle follows the same idea as known from Ruby bundle, has a Brewfile and specific keywords for packages to install.

First Steps with Homebrew Bundle

  • tap: Add more brew third-party repos
  • cask: Install application casks (Gimp, etc.)
  • brew: Install packages
  • mas: Install App store items (requires brew install mas)

You can also override the default cask installation path:

$ vim Brewfile

cask_args appdir: "/Applications"

tap "homebrew/cask"

cask "gimp"

brew "git"

mas "Slack", id: 803453959
Enter fullscreen mode Exit fullscreen mode

Run it to see how it works.

$ brew bundle
Using homebrew/cask
Using gimp
Using git
Using Slack
Homebrew Bundle complete! 4 Brewfile dependencies now installed.
Enter fullscreen mode Exit fullscreen mode

git from Homebrew is newer than the macOS Git binary.

$ git --version
git version 2.29.2

$ /usr/bin/git --version
git version 2.24.3 (Apple Git-128)
Enter fullscreen mode Exit fullscreen mode

Homebrew's Git is installed into /usr/local/bin which has a priority in the PATH environment variable.

$ which git
/usr/local/bin/git

$ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
Enter fullscreen mode Exit fullscreen mode

Existing Homebrew Setup? No Problem

bundle provides the dump sub command which writes a new Brewfile with your current setup.

brew bundle dump
Enter fullscreen mode Exit fullscreen mode

Homebrew automatically installs the bundle tap the first time the command is invoked, no extra preparations needed.

You can either use this file to continue working, or start fresh in your own Brewfile. You can also manually install software first and then analyse how a dump looks like.

Caveats with GNU tools (sed, tar, ...)

You can install GNU tools to avoid different parameters and behaviour with the native UNIX representation of sed, tar, etc. Homebrew provides binaries prefixed with the g character, so even an updated PATH variable won't help, requiring you to call gsed instead of sed. To keep compatibility with existing Linux scripts, I used symlinks created inside a brew setup script:

BREW_PREFIX=$(brew --prefix)
ln -s "${BREW_PREFIX}/bin/gsed" "${BREW_PREFIX}/bin/sed"
Enter fullscreen mode Exit fullscreen mode

This isn't possible inside a Brewfile. Luckily this can be automated in your PATH, as the Homebrew formulas provide these symlink overrides on their own:

$ brew info gnu-sed

GNU "sed" has been installed as "gsed".
If you need to use it as "sed", you can add a "gnubin" directory
to your PATH from your bashrc like:

    PATH="/usr/local/opt/gnu-sed/libexec/gnubin:$PATH"

$ ls -la /usr/local/opt/gnu-sed/libexec/gnubin
total 0
drwxr-xr-x  3 mfriedrich  staff   96 Jan 15  2020 .
drwxr-xr-x  5 mfriedrich  staff  160 Jan 15  2020 ..
lrwxr-xr-x  1 mfriedrich  staff   14 Jan 15  2020 sed -> ../../bin/gsed
Enter fullscreen mode Exit fullscreen mode

Collect the inventory from all GNU tools and render an update for your environment file. You can automate this task with this inventory snippet added to .oh-my-zsh/custom/path.zsh or .zshrc:

# GNU utils path overrides as default CLI tools
if type brew &>/dev/null; then
  HOMEBREW_PREFIX=$(brew --prefix)
  for d in ${HOMEBREW_PREFIX}/opt/*/libexec/gnubin; do export PATH=$d:$PATH; done
fi
Enter fullscreen mode Exit fullscreen mode

If you prefer to use symlinks, you can do so too instead of the export. Keep in mind that the PATH environment is generated on each new terminal, while symlinks can run stale.

A complete example

Follow the iteration in this merge request and open the dotfiles repository to learn more.

My Brewfile focusses on my work as Developer Evangelist at GitLab with a local Linux-ified CLI environment and Docker. Heavier workloads are shifted into cloud environments. Image manipulation tools help with automating tasks for blog posts and workshops.

I'm also not pinning specific versions for packages, this is not supported by Homebrew bundle either.

cask_args appdir: "/Applications"

# Tap Homebrew
tap "homebrew/bundle"
tap "homebrew/cask"
tap "homebrew/cask-fonts"
tap "homebrew/cask-versions"
tap "homebrew/core"
tap "homebrew/services"

cask "java"
cask "visual-studio-code"
cask "firefox"
cask "vlc"
cask "wireshark"
cask "gimp"
cask "inkscape"
cask "jitsi-meet"
cask "handbrake"
cask "vagrant"
cask "spotify"

# System
brew "mas"
brew "curl"
brew "wget"
brew "git"
brew "vim"
brew "openssl"
brew "coreutils"
brew "moreutils"
brew "findutils"
brew "binutils"
brew "rename"
brew "gnu-sed"
brew "gnu-tar"
brew "gawk"
brew "gnutls"
brew "gnu-indent"
brew "gnu-getopt"
brew "tree"
brew "htop"
brew "pidof"
brew "pstree"
brew "grep"
brew "openssh"
brew "rsync"
brew "ssh-copy-id"
brew "screen"
brew "gmp"
brew "nmap"
brew "socat"
brew "rlwrap"
brew "dnstracer"

# Images, Audio, Video
brew "imagemagick"
brew "gifsicle"
brew "gifify"
brew "ffmpeg"

# Archive & Git
brew "xz"
brew "p7zip"
brew "git"
brew "git-lfs"
brew "tig"
brew "hub"

# Extract rpm file content with rpm2cpio *.rpm | cpio -ivd
brew "rpm2cpio"

# JSON
brew "jq"
brew "jo"

# Dev
brew "ruby"
brew "yarn"
brew "rbenv"
brew "python"
brew "go"
brew "cmake"
brew "openjdk"
brew "kind"

# GitLab Pages
brew "hugo"

# App Store
mas "1Password 7", id: 1333542190
mas "Slack", id: 803453959
mas "Telegram", id: 747648890
mas "uBlock", id: 1385985095
Enter fullscreen mode Exit fullscreen mode
💖 💪 🙅 🚩
dnsmichi
Michael Friedrich

Posted on November 30, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related