Skip to content

Instantly share code, notes, and snippets.

@igmrrf
Forked from trozdol/Mac App Installer.sh
Created April 30, 2025 09:25
Show Gist options
  • Save igmrrf/2eb017a400f85653d93f942ed95f1b85 to your computer and use it in GitHub Desktop.
Save igmrrf/2eb017a400f85653d93f942ed95f1b85 to your computer and use it in GitHub Desktop.
A Script to bulk install applications using Homebrew and the App Store.
# !/bin/bash
VERSION=0.0.1
NONINTERACTIVE=1 # for homebrew install
DRY_RUN=false
ENABLED_DEFAULT=true
TAPS_ENABLED=$ENABLED_DEFAULT
TAPS_ENABLED_ASK=true
FORMULAS_ENABLED=$ENABLED_DEFAULT
FORMULAS_ENABLED_ASK=true
CASKS_ENABLED=$ENABLED_DEFAULT
CASKS_ENABLED_ASK=true
APPS_ENABLED=$ENABLED_DEFAULT
APPS_ENABLED_ASK=true
IS_ZSH=false
IS_BASH=false
DOTFILE=$HOME/.zshrc # default for new macs
XCODE_SELECT_PATH=/Applications/Xcode.app/Contents/Developer
RUBY_VERSION="$(curl -s https://raw.githubusercontent.com/facebook/react-native/main/template/_ruby-version)"
NVM_DIR=$HOME/.nvm
IS_HOMEBREW_INSTALLED=$(command -v brew > /dev/null && echo "true" || echo "false" )
# Add Homebrew Repositories
#
taps=(
"homebrew/cask-versions" # add a repository for zulu
"homebrew/cask-fonts" # for fira-code font and spaceship
)
# Homebrew formulas (usually binaries)
# run `brew search <keyword>` to find out if you can install something
# via brew if it's found and shows under Casks section of results add it to
# the $casks=(...) list below
#
formulas=(
"mas" # used to install app store apps
"nvm" # node version manager
"rbenv" # (react native) ruby version manager
"ruby-build" # (react native) needed for cocoapods
"cocoapods" # (react native) kinda like npm for Xcode
# "watchman" # (react native) build watcher
# "nmap" # useful scanning tool
# "gh" # github cli
# "spaceship" # a nifty terminal prompt
)
# Homebrew casks (usually gui apps)
#
casks=(
# "slack" # chat
# "discord" # chat
"microsoft-teams" # chat
"microsoft-outlook" # email/calendar
"microsoft-edge" # browser
"google-chrome" # browser
"firefox" # browser
# "postman" # browser
# "github" # github desktop app
# "docker" # docker desktop for mac
# "zulu11" # (react native) java
# "reactotron" # (react native) debug tool
# "android-studio" # (react native) ide
# "visual-studio-code" # ide
# "mongodb-compass" # db client
# "mysqlworkbench" # db client
# "font-fira-code" # preferred font used by spaceship
# "intellij-idea" # (paid) ide
# "dropbox" # (paid) cloud storage
# "timing" # (paid) time tracking app
# "parallels" # (paid) virual machines
)
# App Store Apps (installed using appId found in app's url)
# low budget assoc array so when adding an app make sure
# there's two entries (id, name) per line.
#
apps=(
# "497799835" "Xcode" # (react native)
# "899247664" "TestFlight" # (react native)
# "1450874784" "Transporter" # (react native)
"1289197285" "Mind Node" # flow/diagrams
"668208984" "GIPHY Capture" # create screen recording gifs
"456609775" "Window Tidy" # (paid) window manager
# "409907375" "Apple Remote Desktop" # (paid) manage other macs
# "1006087419" "SnippetsLab" # (paid) code snippet manager
# "920404675" "Monodraw" # (paid) ascii art tool
)
function pre_install () {
print_title "Pre-Install:"
create_dotfile_if_not_exists &&
create_nvm_directory_if_not_exists
}
function install_homebrew_if_not_installed () {
IS_HOMEBREW_INSTALLED=$(command -v brew > /dev/null && echo "true" || echo "false" )
if $IS_HOMEBREW_INSTALLED; then
echo "> [✓] Homebrew is installed."
else
echo "> [x] Homebrew is NOT installed. Installing..."
if $DRY_RUN == 'true'; then
printf '\n\t /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" && \n\n'
printf '\n\t echo "# Set PATH, MANPATH, etc., for Homebrew." >> $DOTFILE && \n'
printf '\n\t echo "eval "$(/opt/homebrew/bin/brew shellenv)"" >> $DOTFILE && \n'
printf '\n\t eval "$(/opt/homebrew/bin/brew shellenv)" && \n'
printf '\n\t source $DOTFILE && \n'
printf '\n\t install_homebrew_if_not_installed \n\n';
else
NONINTERACTIVE=1 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" &&
echo '# Set PATH, MANPATH, etc., for Homebrew.' >> $DOTFILE &&
echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> $DOTFILE &&
eval "$(/opt/homebrew/bin/brew shellenv)" &&
source $DOTFILE &&
install_homebrew_if_not_installed
fi
fi
}
function create_dotfile_if_not_exists () {
if test -e $DOTFILE; then
echo "> [✓] $DOTFILE exists"
else
touch $DOTFILE
echo "> [✓] $DOTFILE created"
fi
}
function create_nvm_directory_if_not_exists () {
if test -e $HOME/.nvm; then
echo "> [✓] $HOME/.nvm exists"
else
mkdir -p $HOME/.nvm
echo "> [✓] $HOME/.nvm created"
fi
}
function post_install_xcode () {
print_title "Configuring Xcode and Commandline Tools:"
if test -e "/Applications/Xcode.app"; then
echo "> [✓] Xcode is installed"
if $DRY_RUN == 'true'; then
printf "\n\t sudo xcode-select --switch $XCODE_SELECT_PATH \n\n"
else
sudo xcode-select --switch $XCODE_SELECT_PATH
fi
echo "> [✓] xode-select path set to $XCODE_SELECT_PATH"
if $DRY_RUN == 'true'; then
printf "\n\t sudo xcodebuild -license accept \n\n"
else
sudo xcodebuild -license accept
fi
echo "> [✓] Accepting xcodebuild license"
else
echo "> [x] Xcode is not installed."
fi
}
function post_install_nvm () {
export NVM_DIR="$HOME/.nvm"
if test -e $NVM_DIR/nvm.sh; then
source $NVM_DIR/nvm.sh
else
nvm_installed=$(command -v nvm && echo "true" || echo "false" )
node_installed=$(command -v node && echo "true" || echo "false" )
if $nvm_installed; then
if ! $node_installed; then
echo "> [x] Node is not installed. Installing..." &&
printf "\n\n\t nvm install --latest-npm --default --lts \n\n"
fi
echo "> [✓] Node is installed!"
else
echo "> [x] NVM is not installed. Skipping Node install."
fi
fi
}
function post_install_test () {
print_title "Quick Check:"
if $DRY_RUN == 'true'; then
echo "DRY RUN: (No changes were made)"
fi
echo "brew ......................... $(command -v brew)"
echo "xcodebuild ................... $(command -v xcodebuild)"
echo "xcode-select ................. $(command -v xcode-select)"
echo "nvm .......................... $(command -v nvm)"
echo "node ......................... $(command -v node)"
echo "java ......................... $(command -v java)"
echo "rbenv ........................ $(command -v rbenv)"
echo "ruby ......................... $(command -v ruby)"
echo "cocoapods .................... $(command -v pod)"
echo "python3 ...................... $(command -v python3)"
}
function uninstall_app () {
local app=$1
if test -e "/Applications/$app.app"; then
echo "> [✓] $app is installed. Uninstalling..."
if $DRY_RUN == 'true'; then
printf "\n\t sudo rm -rf /Applications/$app.app \n\n"
else
sudo rm -rf "/Applications/$app.app"
fi
else
echo "> [x] $app is not installed."
fi
}
function uninstall() {
print_title "Uninstalling:"
for ((i=0;i<${#apps[@]};i+=2)); do
local app_name="${apps[i+1]}"
uninstall_app $app_name
done
if $DRY_RUN == 'true'; then
printf '\n\t /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" && '
printf "\n\t sudo rm -rf /opt/homebrew \n\n"
else
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" &&
sudo rm -rf /opt/homebrew
fi
echo "> [✓] Homebrew uninstalled."
exit 0;
}
function print_help () {
echo " "
echo "VERSION: v$VERSION"
echo " "
echo "USAGE:"
echo " "
echo " Setup a clean mac or modify the install lists to add additional software."
echo " "
echo " $0 --dry-run [options]"
echo " "
echo " $0 [options]"
echo " "
echo "OPTIONS: "
echo " "
echo " -h, --help This help information"
echo " -v, --version Prints version number"
echo " --dry-run Don't actually install anything, just print what it would be"
echo " doing. This can be handy for seeing if some apps are already"
echo " added or debugging the script."
echo " --taps Install Taps without prompting first"
echo " --formulas Install Formulas without prompting first"
echo " --casks Install Casks without prompting first"
echo " --apps Install Apps (Apple App Store) without prompting first"
echo " "
echo " --uninstall Uninstalls Homebrew and all installed packages"
echo " "
echo " If you see things you don't want or would like to add applications to be"
echo " installed, press ctrl + c then edit the lists and run again."
echo " "
echo " EXAMPLES:"
echo " "
echo " $0 -h"
echo " $0 --help"
echo " "
echo " $0 -v"
echo " $0 --version"
echo " "
echo " $0 # prompts for each taps, formulas, casks, and apps"
echo " $0 --dry-run # prompts for each taps, formulas, casks, and apps"
echo " "
echo " $0 --taps --casks --formulas --apps"
echo " "
echo " $0 --taps"
echo " $0 --formulas"
echo " $0 --casks"
echo " $0 --apps"
echo " "
echo " $0 --uninstall"
exit 0;
}
function print_title () {
echo ""
echo "==============================================================================="
echo " $1 ";
echo "==============================================================================="
}
function print_subtitle () {
echo " $1 ";
echo "-------------------------------------------------------------------------------"
}
function print_list () {
echo "-------------------------------------------------------------------------------"
print_subtitle $1;
shift;
for arg in "$@"; do
echo " - $arg"
done
echo ""
}
function print_apps_list () {
echo "-------------------------------------------------------------------------------"
print_subtitle $1;
shift;
for (( i=0; i<${#apps[@]}; i+=2 ))
do
key="${apps[i]}"
val="${apps[i+1]}"
echo " $key\t $val "
done
echo ""
}
# by default macOS uses zsh as the shell
#
# if [ $SHELL == "/bin/zsh" ]; then
# IS_ZSH=true
# DOTFILE=$HOME/.zshrc
# elif [ $SHELL == "/bin/bash" ]; then
# IS_BASH=true
# DOTFILE=$HOME/.bashrc
# elif [ $SHELL == "/bin/sh" ]; then
# IS_BASH=true
# DOTFILE=$HOME/.bashrc
# fi
# echo "> Sourcing $DOTFILE"
# test -e $DOTFILE && source $DOTFILE;
# echo "> Using shell: $SHELL"
# echo "> Using dotfile: $DOTFILE"
while test $# -gt 0; do
# echo "arg: $1";
case "$1" in
--version|-v)
echo $VERSION;
exit 0
;;
--dry-run)
export DRY_RUN=true
shift
;;
--uninstall)
uninstall;
;;
--taps)
export TAPS_ENABLED=true
export TAPS_ENABLED_ASK=false
shift
;;
--formulas)
export FORMULAS_ENABLED=true
export FORMULAS_ENABLED_ASK=false
shift
;;
--casks)
export CASKS_ENABLED=true
export CASKS_ENABLED_ASK=false
shift
;;
--apps)
export APPS_ENABLED=true
export APPS_ENABLED_ASK=false
shift
;;
--help|-h)
print_help;
exit 0;
;;
*)
break
;;
esac
done
# need sudo for some things.
sudo -v
# QUESTIONS:
# Ask user what they want to install
answer=
print_list "Taps" "${taps[@]}";
if $TAPS_ENABLED_ASK == 'true'; then
read -p " Install Taps? [y/N]: " answer;
case "$answer" in y|Y) answer=true ;; n|N|*) answer=false ;; esac
TAPS_ENABLED=$answer
else
TAPS_ENABLED=true
fi
print_list "Formulas" "${formulas[@]}";
if $FORMULAS_ENABLED_ASK == 'true'; then
read -p " Install Formulas? [y/N]: " answer;
case "$answer" in y|Y) answer=true ;; n|N|*) answer=false ;; esac
FORMULAS_ENABLED=$answer
else
FORMULAS_ENABLED=true
fi
print_list "Casks" "${casks[@]}";
if $CASKS_ENABLED_ASK == 'true'; then
read -p " Install Casks? [y/N]: " answer;
case "$answer" in y|Y) answer=true ;; n|N|*) answer=false ;; esac
CASKS_ENABLED=$answer
else
CASKS_ENABLED=true
fi
print_apps_list "Apps" "${apps[@]}";
if $APPS_ENABLED_ASK == 'true'; then
read -p " Install Apps? [y/N]: " answer;
case "$answer" in y|Y) answer=true ;; n|N|*) answer=false ;; esac
APPS_ENABLED=$answer
else
APPS_ENABLED=true
fi
# PRE-INSTALL:
pre_install;
# INSTALL:
IS_HOMEBREW_INSTALLED=$(command -v brew > /dev/null && echo "true" || echo "false" )
# Will recursivly re-run function if hombrew install is needed.
install_homebrew_if_not_installed &&
# Print a table of what the scripts configuration is set to do.
table="\n"
C="CAN INSTALL"
W="WILL INSTALL"
table+="CATEGORY\tCAN INSTALL\tWILL INSTALL\n";
table+="--------\t-----------\t------------\n";
CAN_INSTALL_TAPS=$( $IS_HOMEBREW_INSTALLED == "true" && echo "true" || echo "false" )
WILL_INSTALL_TAPS=$( $TAPS_ENABLED == "true" && $CAN_INSTALL_TAPS == "true" && echo "true" || echo "false" )
C=$( $CAN_INSTALL_TAPS == "true" && echo "Yes" || echo "No" )
W=$( $WILL_INSTALL_TAPS == "true" && echo "Yes" || echo "No" )
table+="Taps\t$C\t$W\n";
CAN_INSTALL_FORMULAS=$( $IS_HOMEBREW_INSTALLED == "true" && echo "true" || echo "false" )
WILL_INSTALL_FORMULAS=$( $FORMULAS_ENABLED == "true" && $CAN_INSTALL_FORMULAS == "true" && echo "true" || echo "false" )
C=$( $CAN_INSTALL_FORMULAS == "true" && echo "Yes" || echo "No" )
W=$( $WILL_INSTALL_FORMULAS == "true" && echo "Yes" || echo "No" )
table+="Formulas\t$C\t$W\n";
CAN_INSTALL_CASKS=$( $IS_HOMEBREW_INSTALLED == "true" && echo "true" || echo "false" )
WILL_INSTALL_CASKS=$( $CASKS_ENABLED == "true" && $CAN_INSTALL_CASKS == "true" && echo "true" || echo "false" )
C=$( $CAN_INSTALL_CASKS == "true" && echo "Yes" || echo "No" )
W=$( $WILL_INSTALL_CASKS == "true" && echo "Yes" || echo "No" )
table+="Casks\t$C\t$W\n";
CAN_INSTALL_APPS=$( $IS_HOMEBREW_INSTALLED == "true" && brew ls --versions mas > /dev/null && echo "true" || echo "false" )
WILL_INSTALL_APPS=$( $CAN_INSTALL_APPS == "true" && $APPS_ENABLED == "true" && echo "true" || echo "false" )
C=$( $CAN_INSTALL_APPS == "true" && echo "Yes" || echo "No" )
W=$( $WILL_INSTALL_APPS == "true" && echo "Yes" || echo "No" )
table+="Apps\t$C\t$W";
echo $table | awk \
-F'\t' \
'{ printf "%+9s %+13s %+14s \n", $1, $2, $3 }'
echo ""
#
# Start install process.
#
if [[ $TAPS_ENABLED == "true" ]]; then
print_title "Installing Homebrew Taps:"
for tap in "${taps[@]}"; do
if $DRY_RUN == 'true'; then
printf "\n\t brew tap $tap \n\n"
else
brew tap $tap
fi
echo "> [✓] Homebrew Tap $tap added."
done
else
echo "> [✓] Skipping Taps"
fi
if [[ $WILL_INSTALL_FORMULAS == "true" ]]; then
print_title "Installing Formulas with Homebrew:"
for app in "${formulas[@]}"; do
if brew ls --versions $app > /dev/null; then
echo "> [✓] Formula $app is already installed"
else
echo "> [x] Formula $app is not installed. Installing with brew"
if $DRY_RUN == 'true'; then
printf "\n\t brew install $app \n\n"
else
brew install $app
fi
echo "> [✓] Formula $app is installed"
fi
done
else
echo "> [✓] Skipping Formulas"
fi
if [[ $WILL_INSTALL_CASKS == "true" ]]; then
print_title "Installing Casks with Homebrew:"
for app in "${casks[@]}"; do
if brew ls --cask --versions $app > /dev/null;
then
echo "> [✓] Cask $app is already installed"
else
echo "> [x] Cask $app is not installed. Installing with brew"
if $DRY_RUN == 'true'; then
printf "\n\t brew install --cask $app \n\n"
else
brew install --cask $app
fi
fi
done
else
echo "> [✓] Skipping Casks"
fi
if [[ $WILL_INSTALL_APPS == "true" ]]; then
print_title "Installing Apps from App Store:"
for ((i=0;i<${#apps[@]};i+=2)); do
app_id="${apps[i]}"
app_name="${apps[i+1]}"
echo "> Installing '$app_name' from App Store"
if $DRY_RUN == 'true'; then
printf "\n\t mas install $app_id \n\n"
else
mas install $app_id
fi
echo "> [✓] $app_name installed"
done
else
echo "> [✓] Skipping App Store Apps"
fi
post_install_xcode &&
post_install_nvm &&
post_install_test &&
print_title "Done!"
# echo '
# -------------------------------------------------------------------------------
# To update your dotfile ('$DOTFILE') paste the content below into a
# new Terminal window.
# -------------------------------------------------------------------------------
#
# echo "
# # SHELL OPTIONS ###############################################################
#
# ## Enable history filtering by what you have already typed using up/down keys
# autoload -U history-search-end
# zle -N history-beginning-search-backward-end history-search-end
# zle -N history-beginning-search-forward-end history-search-end
# bindkey "^[[A" history-beginning-search-backward-end
# bindkey "^[[B" history-beginning-search-forward-end
#
# ## Load Spaceship Prompt
# source $(brew --prefix)/opt/spaceship/spaceship.zsh
#
# # ENVIRONMENT #################################################################
#
# ## Set PATH, MANPATH, etc., for Homebrew.
# eval "$(/opt/homebrew/bin/brew shellenv)"
#
# ## Ruby Environment
# eval "$(rbenv init - zsh)"
#
# # ALIASES #####################################################################
#
# alias reload="source '$DOTFILE'"
# alias ll="ls -lah"
# alias docker-compose="docker compose"
#
# # EXPORTS #####################################################################
#
# export AWS_DEFAULT_REGION="us-east-1"
# export AWS_PAGER=""
# export AWS_DEFAULT_PROFILE=""
#
# export JAVA_HOME=/Applications/Android\ Studio.app/Contents/jre/Contents/Home/
# export ANDROID_HOME=$HOME/Library/Android/sdk
# export ANDROID_SDK_ROOT=$ANDROID_HOME
#
# export PATH=$HOME/.sencha/Cmd:$PATH
# export PATH=$HOME/aws-cli/aws:$PATH
# export PATH=$PATH:$ANDROID_HOME/emulator
# export PATH=$PATH:$ANDROID_HOME/platform-tools
#
# export NVM_DIR=$HOME/.nvm
# [ -s "/opt/homebrew/opt/nvm/nvm.sh" ] && \. "/opt/homebrew/opt/nvm/nvm.sh" # This loads nvm
# [ -s "/opt/homebrew/opt/nvm/etc/bash_completion.d/nvm" ] && \. "/opt/homebrew/opt/nvm/etc/bash_completion.d/nvm" # This loads nvm bash_completion
#
# " >> '$DOTFILE'
#
# -------------------------------------------------------------------------------
# ';
exit 0;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment