Skip to content

Instantly share code, notes, and snippets.

@claui
Created November 22, 2020 11:13
Show Gist options
  • Save claui/ada85e696029cfa8cba9b91723ce2e5b to your computer and use it in GitHub Desktop.
Save claui/ada85e696029cfa8cba9b91723ce2e5b to your computer and use it in GitHub Desktop.
Using Homebrew on macOS in a multi-user environment

I’ve had success using Homebrew in a multi-user environment the following way:

Create a shared Homebrew user

Create a new (non-GUI) user, group and home directory:

sudo /usr/sbin/sysadminctl -addUser brew \
  -fullName 'Homebrew' -admin -home /var/brew \
  -password - -UID 430 \
  && sudo createhomedir -c -u brew \
  && sudo dscl . -create /Users/brew IsHidden 1 \
  && sudo dseditgroup -o create -r 'Homebrew group' brew

Add myself and others to group

sudo dseditgroup -o edit -a "$(whoami)" -t user brew

Repeat the command line for others who should have access to the brew user, too.

Allow brew group members to sudo -u brew -s:

sudo bash -ex << 'EOF'
  cd "$(mktemp -d)"
  echo > brew_group 'Defaults: %brew env_keep -= "HOME MAIL"'
  echo >> brew_group
  echo >> brew_group 'Defaults: %brew env_keep += "BASH_SILENCE_DEPRECATION_WARNING"'
  echo >> brew_group
  echo >> brew_group '# Keep all HOMEBREW_* environment variables except for'
  echo >> brew_group '# HOMEBREW_BAT_CONFIG_PATH, HOMEBREW_CACHE, HOMEBREW_CLEANUP_MAX_AGE_DAYS,'
  echo >> brew_group '# HOMEBREW_LOGS and HOMEBREW_TEMP'
  echo >> brew_group 'Defaults: %brew env_keep += "all_proxy"'
  echo >> brew_group 'Defaults: %brew env_keep += "ftp_proxy"'
  echo >> brew_group 'Defaults: %brew env_keep += "HOMEBREW_ARCH"'
  echo >> brew_group 'Defaults: %brew env_keep += "HOMEBREW_ARTIFACT_DOMAIN"'
  echo >> brew_group 'Defaults: %brew env_keep += "HOMEBREW_AUTO_UPDATE_SECS"'
  echo >> brew_group 'Defaults: %brew env_keep += "HOMEBREW_BAT"'
  echo >> brew_group 'Defaults: %brew env_keep += "HOMEBREW_BINTRAY_KEY"'
  echo >> brew_group 'Defaults: %brew env_keep += "HOMEBREW_BINTRAY_USER"'
  echo >> brew_group 'Defaults: %brew env_keep += "HOMEBREW_BOTTLE_DOMAIN"'
  echo >> brew_group 'Defaults: %brew env_keep += "HOMEBREW_BREW_GIT_REMOTE"'
  echo >> brew_group 'Defaults: %brew env_keep += "HOMEBREW_BROWSER"'
  echo >> brew_group 'Defaults: %brew env_keep += "HOMEBREW_COLOR"'
  echo >> brew_group 'Defaults: %brew env_keep += "HOMEBREW_CORE_GIT_REMOTE"'
  echo >> brew_group 'Defaults: %brew env_keep += "HOMEBREW_CURL_RETRIES"'
  echo >> brew_group 'Defaults: %brew env_keep += "HOMEBREW_CURL_VERBOSE"'
  echo >> brew_group 'Defaults: %brew env_keep += "HOMEBREW_CURLRC"'
  echo >> brew_group 'Defaults: %brew env_keep += "HOMEBREW_DEVELOPER"'
  echo >> brew_group 'Defaults: %brew env_keep += "HOMEBREW_DISABLE_LOAD_FORMULA"'
  echo >> brew_group 'Defaults: %brew env_keep += "HOMEBREW_DISPLAY_INSTALL_TIMES"'
  echo >> brew_group 'Defaults: %brew env_keep += "HOMEBREW_DISPLAY"'
  echo >> brew_group 'Defaults: %brew env_keep += "HOMEBREW_EDITOR"'
  echo >> brew_group 'Defaults: %brew env_keep += "HOMEBREW_FAIL_LOG_LINES"'
  echo >> brew_group 'Defaults: %brew env_keep += "HOMEBREW_FORCE_BREWED_CURL"'
  echo >> brew_group 'Defaults: %brew env_keep += "HOMEBREW_FORCE_BREWED_GIT"'
  echo >> brew_group 'Defaults: %brew env_keep += "HOMEBREW_FORCE_HOMEBREW_ON_LINUX"'
  echo >> brew_group 'Defaults: %brew env_keep += "HOMEBREW_FORCE_VENDOR_RUBY"'
  echo >> brew_group 'Defaults: %brew env_keep += "HOMEBREW_GIT_EMAIL"'
  echo >> brew_group 'Defaults: %brew env_keep += "HOMEBREW_GIT_NAME"'
  echo >> brew_group 'Defaults: %brew env_keep += "HOMEBREW_GITHUB_API_PASSWORD"'
  echo >> brew_group 'Defaults: %brew env_keep += "HOMEBREW_GITHUB_API_TOKEN"'
  echo >> brew_group 'Defaults: %brew env_keep += "HOMEBREW_GITHUB_API_USERNAME"'
  echo >> brew_group 'Defaults: %brew env_keep += "HOMEBREW_INSTALL_BADGE"'
  echo >> brew_group 'Defaults: %brew env_keep += "HOMEBREW_MAKE_JOBS"'
  echo >> brew_group 'Defaults: %brew env_keep += "HOMEBREW_NO_ANALYTICS"'
  echo >> brew_group 'Defaults: %brew env_keep += "HOMEBREW_NO_AUTO_UPDATE"'
  echo >> brew_group 'Defaults: %brew env_keep += "HOMEBREW_NO_BOTTLE_SOURCE_FALLBACK"'
  echo >> brew_group 'Defaults: %brew env_keep += "HOMEBREW_NO_COLOR"'
  echo >> brew_group 'Defaults: %brew env_keep += "HOMEBREW_NO_COMPAT"'
  echo >> brew_group 'Defaults: %brew env_keep += "HOMEBREW_NO_EMOJI"'
  echo >> brew_group 'Defaults: %brew env_keep += "HOMEBREW_NO_GITHUB_API"'
  echo >> brew_group 'Defaults: %brew env_keep += "HOMEBREW_NO_INSECURE_REDIRECT"'
  echo >> brew_group 'Defaults: %brew env_keep += "HOMEBREW_NO_INSTALL_CLEANUP"'
  echo >> brew_group 'Defaults: %brew env_keep += "HOMEBREW_PRY"'
  echo >> brew_group 'Defaults: %brew env_keep += "HOMEBREW_SKIP_OR_LATER_BOTTLES"'
  echo >> brew_group 'Defaults: %brew env_keep += "HOMEBREW_SVN"'
  echo >> brew_group 'Defaults: %brew env_keep += "HOMEBREW_UPDATE_TO_TAG"'
  echo >> brew_group 'Defaults: %brew env_keep += "HOMEBREW_VERBOSE_USING_DOTS"'
  echo >> brew_group 'Defaults: %brew env_keep += "HOMEBREW_VERBOSE"'
  echo >> brew_group 'Defaults: %brew env_keep += "http_proxy"'
  echo >> brew_group 'Defaults: %brew env_keep += "https_proxy"'
  echo >> brew_group 'Defaults: %brew env_keep += "no_proxy"'
  echo >> brew_group
  echo >> brew_group '%brew ALL = (brew) NOPASSWD: ALL'
  chown root:wheel brew_group
  chmod 440 brew_group
  visudo -c -s -f brew_group
  mkdir -p /private/etc/sudoers.d
  SUDO_EDITOR="tee" visudo -f /private/etc/sudoers.d/brew_group < brew_group
  echo Success
EOF

Transfer Homebrew ownership to the brew user

On Intel (or Apple Silicon with Rosetta 2):

sudo chown -R brew:admin /usr/local/*

On Apple Silicon:

sudo chown -R brew:admin /opt/homebrew

Customize settings for the brew user

The brew user needs a bit of customization so it’s easier to use.

Customize prompt

Customizing the prompt is convenient because it helps you figure out e.g. whether you are in the brew user or not.

For example, add the following lines to /var/brew/.bashrc or /var/brew/.zshrc:

function nonzero_return() {
  RETVAL=$?
  [ $RETVAL -ne 0 ] && echo "[$RETVAL]"
}

export -f nonzero_return

export PS1="[\u] \w \`nonzero_return\`"$'\xf0\x9f\x8d\xba'" "

Configure PATH

Add the following line to /var/brew/.bashrc or /var/brew/.zshrc:

(Caution: For a native Apple Silicon Homebrew installation, use /opt/homebrew instead of /usr/local/.)

export PATH='/usr/local/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/Apple/usr/bin'

Fix target directories for Cask

Add the following lines to /var/brew/.bashrc or /var/brew/.zshrc:

__homebrew_cask_config_path="${HOMEBREW_REPOSITORY:-$(
  brew --repository)}/Library/Homebrew/cask/config.rb"

if [ -e "${__homebrew_cask_config_path}" ]; then
  IFS=$'\n' read -a '__homebrew_cask_options' -d '' <<< "$(sed -n \
      -e 's#^ *\([a-z_]*dir\): *"~\(.*\)",#--\1="\2"#p' \
      "${__homebrew_cask_config_path}"
    )" || true

  HOMEBREW_CASK_OPTS=''
  for __homebrew_cask_option in "${__homebrew_cask_options[@]}"; do
    HOMEBREW_CASK_OPTS=`
      `"${HOMEBREW_CASK_OPTS:+${HOMEBREW_CASK_OPTS} }"
    HOMEBREW_CASK_OPTS=`
      `"${HOMEBREW_CASK_OPTS}${__homebrew_cask_option}"
  done

  unset __homebrew_cask_config_path __homebrew_cask_option \
    __homebrew_cask_options
  export HOMEBREW_CASK_OPTS
fi

Using the brew shell

Anyone who is in the brew group can now enter the brew shell without requiring a password:

sudo -u brew -s

While in the brew shell, you can use the brew command without restriction.

You can leave the brew shell again by pressing ^+D.

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