Skip to content

Instantly share code, notes, and snippets.

@erdaltoprak
Last active May 22, 2025 15:37
Show Gist options
  • Save erdaltoprak/df9959b0610a56d782d5fb636403b660 to your computer and use it in GitHub Desktop.
Save erdaltoprak/df9959b0610a56d782d5fb636403b660 to your computer and use it in GitHub Desktop.
Automated macOS setup with homebrew, system & shell configuration
#!/usr/bin/env bash
###############################################################################
# Mac Bootstrap – Preferences, Homebrew, MAS apps
# Works with the stock /bin/bash 3.2 on macOS (no mapfile required)
# (c) 2025 Erdal • MIT License
###############################################################################
###############################################################################
# Colours / log helpers
###############################################################################
GREEN=$'\033[0;32m'; BLUE=$'\033[0;34m'
RED=$'\033[0;31m'; YELLOW=$'\033[1;33m'; NC=$'\033[0m'; BOLD=$'\033[1m'
log_info() { printf '%sℹ️ %s%s\n' "$BLUE" "$1" "$NC"; }
log_success() { printf '%s✅ %s%s\n' "$GREEN" "$1" "$NC"; }
log_warning() { printf '%s⚠️ %s%s\n' "$YELLOW" "$1" "$NC"; }
log_error() { printf '%s❌ %s%s\n' "$RED" "$1" "$NC"; }
###############################################################################
# Robust shell behaviour & cleanup
###############################################################################
set -Eeuo pipefail
cleanup() {
tput cnorm || true
}
trap cleanup EXIT
trap 'log_error "Interrupted"; exit 1' INT HUP TERM
trap 'log_error "Line $LINENO (exit $?) – $BASH_COMMAND"; exit 1' ERR
###############################################################################
# Progress-bar helpers
###############################################################################
BAR_W=30
show_bar() { # no trailing newline
local pct=$1 msg=$2
local done=$(( BAR_W * pct / 100 ))
local todo=$(( BAR_W - done ))
printf '\r\033[K%s┃%s' "$BLUE" "$NC"
printf '%*s' "$done" '' | tr ' ' ''
printf '%*s' "$todo" '' | tr ' ' ''
printf '%s┃ %3d%% %s%s' "$BLUE" "$pct" "$msg" "$NC"
}
newline_below_bar() { printf '\n'; }
###############################################################################
# Welcome
###############################################################################
welcome() {
echo "======================================================"
log_info "🎯 Mac Setup Script – Preferences & Apps"
echo "======================================================"
log_warning "You'll be prompted for your password when needed."
echo "1. Sign in to the Mac App Store"
echo "2. Review this script"
echo "3. Ensure a stable internet connection"
echo
read -p "Press RETURN to continue or CTRL-C to quit…"
}
###############################################################################
# Preferences (abridged)
###############################################################################
configure_system() {
# https://macos-defaults.com
log_info "Configuring System Preferences…"
defaults write com.apple.dock orientation -string bottom
defaults write com.apple.dock autohide -bool true
mkdir -p "$HOME/Documents/Screenshots"
defaults write com.apple.screencapture location -string "$HOME/Documents/Screenshots"
defaults write com.apple.dock "static-only" -bool "true"
defaults write com.apple.dock "show-recents" -bool "false"
defaults write com.apple.Safari "ShowFullURLInSmartSearchField" -bool "true"
defaults write com.apple.universalaccess "showWindowTitlebarIcons" -bool "true"
defaults write com.apple.finder "ShowPathbar" -bool "true"
defaults write com.apple.menuextra.clock "DateFormat" -string "\"EEE d MMM HH:mm:ss\""
defaults write com.apple.TimeMachine "DoNotOfferNewDisksForBackup" -bool "true"
defaults write com.apple.dock "mru-spaces" -bool "false"
# https://dev.to/darrinndeal/setting-mac-hot-corners-in-the-terminal-3de
defaults write com.apple.dock wvous-bl-corner -int 10
defaults write com.apple.dock wvous-bl-modifier -int 1048576
defaults write com.apple.dock wvous-tl-corner -int 5
defaults write com.apple.dock wvous-tl-modifier -int 1048576
killall Dock Finder Safari SystemUIServer 2>/dev/null || true
log_success "System Preferences applied"
}
###############################################################################
# Homebrew – install or update/upgrade
###############################################################################
brew_bootstrap() {
if ! command -v brew &>/dev/null; then
log_info "Homebrew not found → installing…"
bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
else
log_info "Homebrew found → updating & upgrading…"
brew update; brew upgrade; brew cleanup
fi
[[ $(uname -m) == arm64 ]] &&
{ eval "$(/opt/homebrew/bin/brew shellenv)";
grep -q "/opt/homebrew" ~/.zprofile 2>/dev/null ||
echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zprofile; }
}
###############################################################################
# Lists
###############################################################################
APPS=(
# Casks
"another-redis-desktop-manager:cask"
"appcleaner:cask"
"balenaetcher:cask"
"discord:cask"
"docker:cask"
"firefox:cask"
"firefox@developer-edition:cask"
"github:cask"
"chromium:cask:no-quarantine"
"handbrake:cask"
"iina:cask"
"pgadmin4:cask"
"rectangle:cask"
"tradingview:cask"
"transmission:cask"
"visual-studio-code:cask"
"cursor:cask"
"windsurf:cask"
"vlc:cask"
"zed:cask"
"cleanshot:cask"
"font-sf-mono:cask"
"obs:cask"
"sf-symbols:cask"
"thunderbird:cask"
"opensuperwhisper:cask"
"zen:cask"
"cleanshot:cask"
"screen-studio:cask"
# Formulae
"wget:formula"
"gnupg:formula"
"tmux:formula"
"mas:formula"
"kubernetes-cli:formula"
"openshift-cli:formula"
"zsh-autosuggestions:formula"
"zsh-syntax-highlighting:formula"
"zsh-history-substring-search:formula"
"powerlevel10k:formula"
"macmon:formula"
"huggingface-cli:formula"
)
MAS_APPS=(
"1440147259:AdGuard for Safari"
"497799835:Xcode"
"409183694:Keynote"
"408981434:iMovie"
"640199958:Developer"
"1616822987:Affinity Photo 2"
"899247664:TestFlight"
"1543920362:Displaperture"
"310633997:WhatsApp"
"425264550:Blackmagic Disk Speed Test"
"409201541:Pages"
"747648890:Telegram"
"937984704:Amphetamine"
"409203825:Numbers"
"1475387142:Tailscale"
"1433648537:Passepartout"
"1579902068:xSearch"
"424389933:Final Cut Pro"
"434290957:Motion"
"424390742:Compressor"
)
###############################################################################
# Portable helper – fill array with command output (no mapfile needed)
###############################################################################
cmd_to_array() { # $1 = array-name $2 = cmd…
local _line
eval "$1=()"
while IFS= read -r _line; do
eval "$1+=(\"\$_line\")"
done < <(eval "$2")
}
###############################################################################
# Install Brew items (skip already installed)
###############################################################################
install_brew_items() {
INST_FORMULAE=()
INST_CASKS=()
cmd_to_array INST_FORMULAE "brew list --formula"
cmd_to_array INST_CASKS "brew list --cask 2>/dev/null || true"
local total=${#APPS[@]} current=0 kind name pct
show_bar 0 "starting…"; newline_below_bar
for entry in "${APPS[@]}"; do
current=$((current+1)); pct=$(( current * 100 / total ))
IFS=':' read -r name kind flag <<< "$entry"
if [[ $kind == cask ]] && [[ " ${INST_CASKS[@]-} " == *" $name "* ]] ||
[[ $kind == formula ]] && [[ " ${INST_FORMULAE[@]-} " == *" $name "* ]]; then
show_bar "$pct" "✓ already installed $name"; newline_below_bar; continue
fi
show_bar "$pct" "$name"; newline_below_bar
if [[ $kind == cask ]]; then
if [[ $flag == "no-quarantine" ]]; then
brew install --cask --no-quarantine "$name"
else
brew install --cask "$name"
fi
else
brew install "$name"
fi
show_bar "$pct" "✔︎ $name"; newline_below_bar
done
brew upgrade; brew cleanup
}
###############################################################################
# Install MAS items – tolerant of the "command not supported" issue
###############################################################################
install_mas_items() {
# make sure mas exists
command -v mas >/dev/null || brew install mas
# try to capture the list of IDs already on the machine
if mas list 1>/tmp/mas_installed 2>/dev/null; then
# success → build INST_IDS array from the tmp file
INST_IDS=($(awk '{print $1}' /tmp/mas_installed))
else
# macOS 14+ (or similar) where mas can't read the account
INST_IDS=() # pretend nothing is installed
log_warning "Cannot read App Store account status; installs may prompt or fail."
fi
rm -f /tmp/mas_installed
local total=${#MAS_APPS[@]} current=0 id name pct
show_bar 0 "starting…"; newline_below_bar
for entry in "${MAS_APPS[@]}"; do
current=$((current+1)); pct=$(( current * 100 / total ))
id=${entry%%:*}; name=${entry#*:}
if [[ " ${INST_IDS[@]-} " == *" $id "* ]]; then
show_bar "$pct" "✓ already installed $name"; newline_below_bar; continue
fi
show_bar "$pct" "$name"; newline_below_bar
if mas install "$id"; then
show_bar "$pct" "✔︎ $name"; newline_below_bar
else
log_warning "failed: $name"
fi
done
# attempt a bulk upgrade; ignore errors
mas upgrade || true
}
###############################################################################
# Z-shell config (same as previous)
###############################################################################
setup_zsh() {
log_info "Configuring Z-shell…"
[[ -f ~/.zshrc ]] && mv ~/.zshrc ~/.zshrc.backup.$(date +%Y%m%d_%H%M%S)
prefix=$(brew --prefix)
cat > ~/.zshrc <<EOF
[[ -r "\${XDG_CACHE_HOME:-\$HOME/.cache}/p10k-instant-prompt-\${(%):-%n}.zsh" ]] &&
source "\${XDG_CACHE_HOME:-\$HOME/.cache}/p10k-instant-prompt-\${(%):-%n}.zsh"
source $prefix/share/zsh-autosuggestions/zsh-autosuggestions.zsh
source $prefix/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh
source $prefix/share/zsh-history-substring-search/zsh-history-substring-search.zsh
source $prefix/share/powerlevel10k/powerlevel10k.zsh-theme
autoload -U compinit && compinit
alias c='clear' rmm='rm -rf' lss='ls -lah' reload='source ~/.zshrc'
alias t='tmux' e='code' z='zed' mtop='macmon'
[[ -f ~/.p10k.zsh ]] && source ~/.p10k.zsh
EOF
[[ $SHELL != "$(which zsh)" ]] && chsh -s "$(which zsh)"
log_success "Z-shell configured"
}
###############################################################################
# Main
###############################################################################
main() {
welcome
newline_below_bar
brew_bootstrap
newline_below_bar
configure_system
newline_below_bar
log_info "Installing Homebrew apps…"; newline_below_bar
install_brew_items
newline_below_bar
log_info "Installing Mac App Store apps…"; newline_below_bar
install_mas_items
newline_below_bar
setup_zsh
echo -e "\n${GREEN}${BOLD}✨ All done! Consider rebooting.${NC}"
}
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment