DEV Community

Cover image for Use Homebrew Bundle to manage software installation on macOS
Michael Friedrich
Michael Friedrich

Posted on

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

Top comments (0)