Last active
August 26, 2021 16:58
-
-
Save toonetown/48101686e509fda81335 to your computer and use it in GitHub Desktop.
Bootstraps a Homebrew development environment, with optional packages (taps, brews, casks, and services)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
########### | |
# Installs and/or updates homebrew and various components | |
# | |
#!/bin/bash | |
: ${HOMEBREW_INST_URL:="https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh"} | |
: ${HB_BOOTSTRAP_GIST_URL:="https://gist.githubusercontent.com/toonetown/48101686e509fda81335/raw"} | |
if [ $# -eq 0 ]; then | |
echo "Usage: ${0} [--fetch-only] <PACKAGES...>" | |
echo "" | |
echo "The format for package is one of:" | |
echo " t:user/repository (for taps - prefix tap with an asterisk for pinned tap, i.e. t:*user/repository)" | |
echo " b:brew-name (for brew formulas)" | |
echo " c:cask-name (for casks)" | |
echo " s:service-name (for services - prefix with an asterisk to run at boot, i.e. s:*service-name)" | |
echo " n:npm-package (for node packages)" | |
echo "" | |
echo "Packages will be installed (or updated if they are already installed and are outdated) one at a time in the" | |
echo "order listed above (taps first, then brews, then casks, then services, then node packages)." | |
echo "" | |
echo "If you specify a cask, then the tap for homebrew-cask will be added. If you specify a service, then the" | |
echo "tap for homebrew/services will be added. If you specify a node package, then the node brew will be added." | |
echo "" | |
echo "To run this directly from curl (i.e. to bootstrap a system), you can execute:" | |
echo " curl -sSL ${HB_BOOTSTRAP_GIST_URL} | bash -s -- <PACKAGES...>" | |
echo "" | |
exit 1 | |
fi | |
install_xcode() { | |
xcode-select -p >/dev/null 2>&1 || { | |
echo "" | |
echo "===========================" | |
echo "Installing Developer Tools..." | |
echo "===========================" | |
INPROGRESS_FLAG="/tmp/.com.apple.dt.CommandLineTools.installondemand.in-progress" | |
touch "${INPROGRESS_FLAG}" | |
trap 'rm -f "${INPROGRESS_FLAG}"' EXIT | |
while ! ( xcode-select -p &>/dev/null ); do softwareupdate -i -a || exit $?; done | |
rm -f "${INPROGRESS_FLAG}" | |
} | |
} | |
check_homebrew() { | |
echo "" | |
echo "===========================" | |
echo "Checking Homebrew installation..." | |
echo "===========================" | |
(mkdir -p "$(brew --cache)" &>/dev/null && chmod 700 "$(brew --cache)") || return $? | |
brew doctor || return $? | |
for d in cache cellar; do | |
[ -d "$(brew --${d})" ] || mkdir -p "$(brew --${d})" || return $? | |
done | |
} | |
install_homebrew() { | |
install_xcode || return $? | |
which brew >/dev/null && { | |
echo "" | |
echo "===========================" | |
echo "Updating Homebrew..." | |
echo "===========================" | |
brew update || return $? | |
} || { | |
echo "" | |
echo "===========================" | |
echo "Installing Homebrew..." | |
echo "===========================" | |
if [ "$(id -u)" == "0" ]; then | |
if [ "${FORCE_HOMEBREW_ROOT_INSTALL}" == "YES" ]; then | |
/bin/bash -c "$(curl -fsSL "${HOMEBREW_INST_URL}" \ | |
| grep -v 'Process.uid == 0' \ | |
| sed -e 's/EOABORT unless `dsmemberutil/EOABORT if `dsmemberutil/g')" || return $? | |
else | |
echo "Not installing homebrew as root - set FORCE_HOMEBREW_ROOT_INSTALL" | |
echo "to \"YES\" (case-sensitive) to override this behavior" | |
exit 1 | |
fi | |
else | |
r/bin/bash -c "$(curl -fsSL "${HOMEBREW_INST_URL}")" || return $? | |
fi | |
} | |
} | |
add_tap() { | |
TAP_NAME="${1}" | |
[ -n "${TAP_NAME}" ] || { echo "Missing tap name"; return 1; } | |
brew tap | grep -q "^${TAP_NAME}$" || { | |
echo "" | |
echo "===========================" | |
echo "Tapping ${TAP_NAME}..." | |
echo "===========================" | |
brew tap ${TAP_NAME} | |
} | |
return $? | |
} | |
pin_tap() { | |
TAP_NAME="${1}" | |
[ -n "${TAP_NAME}" ] || { echo "Missing tap name"; return 1; } | |
add_tap "${TAP_NAME}" || return $? | |
brew tap-info "${TAP_NAME}" | grep -q "^${TAP_NAME}: pinned" || { | |
echo "" | |
echo "===========================" | |
echo "Pinning tap ${TAP_NAME}..." | |
echo "===========================" | |
brew tap-pin ${TAP_NAME} | |
} | |
return $? | |
} | |
fetch_brews() { | |
if [ $# -gt 0 ]; then | |
echo "" | |
echo "===========================" | |
echo "Fetching brews and dependencies..." | |
echo "===========================" | |
brew fetch --formula --deps "$@" || return $? | |
fi | |
return 0 | |
} | |
fetch_casks() { | |
if [ $# -gt 0 ]; then | |
echo "" | |
echo "===========================" | |
echo "Fetching casks..." | |
echo "===========================" | |
brew fetch --cask "$@" || return $? | |
fi | |
return 0 | |
} | |
install_brew() { | |
BREW_NAME="${1}" | |
[ -n "${BREW_NAME}" ] || { echo "Missing brew name"; return 1; } | |
if echo "${BREW_NAME}" | grep -q "\/"; then | |
LIST_OPT="--full-name" | |
else | |
LIST_OPT="" | |
fi | |
brew list --formula ${LIST_OPT} 2>&1 | grep -q "^${BREW_NAME}$" && { | |
brew outdated --formula "${BREW_NAME}" &>/dev/null || { | |
echo "" | |
echo "===========================" | |
echo "Upgrading brew ${BREW_NAME}..." | |
echo "===========================" | |
brew upgrade --cleanup ${BREW_NAME} | |
} | |
} || { | |
echo "" | |
echo "===========================" | |
echo "Installing brew ${BREW_NAME}..." | |
echo "===========================" | |
brew install --formula ${BREW_NAME} | |
} | |
return $? | |
} | |
install_cask() { | |
CASK_NAME="${1}" | |
[ -n "${CASK_NAME}" ] || { echo "Missing cask name"; return 1; } | |
brew list --cask 2>&1 | grep -q "^${CASK_NAME}$" && { | |
if brew outdated --cask 2>&1 | grep -q "^${CASK_NAME}$"; then | |
echo "" | |
echo "===========================" | |
echo "Upgrading cask ${CASK_NAME}..." | |
echo "===========================" | |
brew upgrade --cask ${CASK_NAME} | |
fi | |
} || { | |
echo "" | |
echo "===========================" | |
echo "Installing cask ${CASK_NAME}..." | |
echo "===========================" | |
brew install --cask ${CASK_NAME} | |
} | |
return $? | |
} | |
start_boot_service() { | |
SERVICE_NAME="${1}" | |
[ -n "${SERVICE_NAME}" ] || { echo "Missing service name"; return 1; } | |
sudo -p "Password for managing boot services:" brew services list | grep -q "^${SERVICE_NAME} \+started" && { | |
echo "" | |
echo "===========================" | |
echo "Restarting boot service ${SERVICE_NAME}..." | |
echo "===========================" | |
sudo brew services restart ${SERVICE_NAME} | |
} || { | |
echo "" | |
echo "===========================" | |
echo "Starting boot service ${SERVICE_NAME}..." | |
echo "===========================" | |
sudo brew services start ${SERVICE_NAME} | |
} | |
if [ $? -ne 0 ]; then | |
echo "Boot service ${SERVICE_NAME} failed to start" >&2 | |
fi | |
return 0 | |
} | |
start_service() { | |
SERVICE_NAME="${1}" | |
[ -n "${SERVICE_NAME}" ] || { echo "Missing service name"; return 1; } | |
brew services list | grep -q "^${SERVICE_NAME} \+started" && { | |
echo "" | |
echo "===========================" | |
echo "Restarting service ${SERVICE_NAME}..." | |
echo "===========================" | |
brew services restart ${SERVICE_NAME} | |
} || { | |
echo "" | |
echo "===========================" | |
echo "Starting service ${SERVICE_NAME}..." | |
echo "===========================" | |
brew services start ${SERVICE_NAME} | |
} | |
if [ $? -ne 0 ]; then | |
echo "Service ${SERVICE_NAME} failed to start" >&2 | |
fi | |
return 0 | |
} | |
install_npm_pkg() { | |
NPM_PACKAGE_NAME="${1}" | |
[ -n "${NPM_PACKAGE_NAME}" ] || { echo "Missing node package name"; return 1; } | |
npm -g ls "${NPM_PACKAGE_NAME}" &>/dev/null && { | |
if npm -g outdated 2>&1 | grep -q "^${NPM_PACKAGE_NAME} "; then | |
echo "" | |
echo "===========================" | |
echo "Upgrading npm ${NPM_PACKAGE_NAME}..." | |
echo "===========================" | |
npm -g -s install ${NPM_PACKAGE_NAME}@latest | |
fi | |
} || { | |
echo "" | |
echo "===========================" | |
echo "Installing npm ${NPM_PACKAGE_NAME}..." | |
echo "===========================" | |
npm -g -s install ${NPM_PACKAGE_NAME} | |
} | |
return $? | |
} | |
TAP_LIST="" | |
PRE_CASK_LIST="" | |
BREW_LIST="" | |
CASK_LIST="" | |
SERVICE_LIST="" | |
NPM_LIST="" | |
if [ "${1}" == "--fetch-only" ]; then DO_FETCH_ONLY="yes"; shift; else DO_FETCH_ONLY="no"; fi | |
for i in $@; do | |
case "${i:0:2}" in | |
"t:") | |
TAP_LIST="${TAP_LIST} ${i:2}" | |
;; | |
"b:") | |
if [ "${i:2}" == "brew-cask" ]; then | |
TAP_LIST="${TAP_LIST} homebrew/cask" | |
elif [ "${i:2}" != "brew" ]; then | |
BREW_LIST="${BREW_LIST} ${i:2}" | |
fi | |
;; | |
"c:") | |
if [ "${i:2}" == "xcode" -a -d /Applications/Xcode.app ]; then | |
brew list --cask 2>&1 | grep -q "^xcode$" || { | |
echo "Appstore Xcode already installed" | |
} | |
else | |
TAP_LIST="${TAP_LIST} homebrew/cask" | |
if [ "${i:2:4}" == "java" ]; then | |
PRE_CASK_LIST="${PRE_CASK_LIST} ${i:2}" | |
else | |
CASK_LIST="${CASK_LIST} ${i:2}" | |
fi | |
fi | |
;; | |
"s:") | |
SERVICE_LIST="${SERVICE_LIST} ${i:2}" | |
TAP_LIST="${TAP_LIST} homebrew/services" | |
;; | |
"n:") | |
BREW_LIST="${BREW_LIST} node" | |
NPM_LIST="${NPM_LIST} ${i:2}" | |
;; | |
*) | |
echo "Invalid package '${i}'" | |
exit 1 | |
;; | |
esac | |
done | |
install_homebrew || exit $? | |
for i in $(ruby -e "puts \"${TAP_LIST}\".split(/\s+/).sort.uniq"); do | |
if [ "${i:0:1}" == "*" ]; then | |
pin_tap ${i:1} || exit $? | |
else | |
add_tap ${i} || exit $? | |
fi | |
done | |
check_homebrew || exit $? | |
if [ "${DO_FETCH_ONLY}" == "yes" ]; then | |
fetch_brews $(ruby -e "puts \"${BREW_LIST}\".split(/[\s\*]+/).sort.uniq") || exit $? | |
fetch_casks $(ruby -e "puts \"${PRE_CASK_LIST} ${CASK_LIST}\".split(/[\s\*]+/).sort.uniq") || exit $? | |
else | |
for i in $(ruby -e "puts \"${PRE_CASK_LIST}\".split(/\s+/).sort.uniq"); do | |
install_cask ${i} || exit $? | |
done | |
for i in $(ruby -e "puts \"${BREW_LIST}\".split(/\s+/).sort.uniq"); do | |
for j in $(brew deps --formula --skip-optional --skip-build ${i} 2>/dev/null | grep -v '^ruby$'); do | |
install_brew ${j} || exit $? | |
done | |
install_brew ${i} || exit $? | |
done | |
for i in $(ruby -e "puts \"${CASK_LIST}\".split(/\s+/).sort.uniq"); do | |
install_cask ${i} || exit $? | |
done | |
for i in $(ruby -e "puts \"${SERVICE_LIST}\".split(/\s+/).sort.uniq"); do | |
if [ "${i:0:1}" == "*" ]; then | |
start_boot_service ${i:1} || exit $? | |
else | |
start_service ${i} || exit $? | |
fi | |
done | |
if [ -n "${NPM_LIST}" ]; then | |
install_npm_pkg npm || exit $? | |
for i in $(ruby -e "puts \"${NPM_LIST}\".split(/\s+/).sort.uniq"); do | |
install_npm_pkg ${i} || exit $? | |
done | |
fi | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
A helpful set of defaults are:
curl -sSL https://gist.githubusercontent.com/toonetown/48101686e509fda81335/raw | bash -s -- \ b:brew-bootstrap \ t:toonetown/extras b:toonetown-extras s:toonetown-extras \ b:rmate b:pretty-prompt b:scm-prompt \ b:bash-completion b:bash-completion-env \ b:mas b:swup