Files
self/app/scripts/setup-macos.sh
Justin Hernandez 753461f4cb Create mobile dependency check script and update readme (#1547)
* update readme

* add check / install script and update readme

* agent feedback

* gracefully handle yarn check

* change order and steps
2026-01-05 09:27:14 -08:00

185 lines
7.3 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# Self.xyz macOS Development Environment Setup
# Usage: ./scripts/setup-macos.sh [--check-only] [--yes]
set -e
# Config
REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
APP_DIR="$REPO_ROOT/app"
RUBY_VERSION=$(cat "$APP_DIR/.ruby-version" 2>/dev/null | tr -d '[:space:]')
NODE_MAJOR=22
# Args (can be overridden interactively)
CHECK_ONLY=false; AUTO_YES=false
for arg in "$@"; do
case $arg in
--check-only) CHECK_ONLY=true ;;
--yes|-y) AUTO_YES=true ;;
--interactive|-i) ;; # default behavior
esac
done
# Colors
R='\033[31m' G='\033[32m' Y='\033[33m' B='\033[34m' C='\033[36m' NC='\033[0m' BOLD='\033[1m'
ok() { echo -e "${G}${NC} $1"; }
err() { echo -e "${R}${NC} $1"; }
warn() { echo -e "${Y}${NC} $1"; }
info() { echo -e "${C}${NC} $1"; }
confirm() {
$AUTO_YES && return 0
read -p "$1 [Y/n] " -n 1 -r; echo
[[ ! $REPLY =~ ^[Nn]$ ]]
}
# Check functions - return "ok:version" or "missing" or "wrong:version"
chk_brew() { command -v brew &>/dev/null && echo "ok:$(brew --version | head -1 | cut -d' ' -f2)" || echo "missing"; }
chk_nvm() { [[ -s "$HOME/.nvm/nvm.sh" ]] && echo "ok" || echo "missing"; }
chk_node() { command -v node &>/dev/null && { v=$(node -v 2>/dev/null | tr -d 'v'); [[ -n "$v" && "${v%%.*}" -ge $NODE_MAJOR ]] && echo "ok:$v" || echo "wrong:$v"; } || echo "missing"; }
chk_watch() { command -v watchman &>/dev/null && echo "ok:$(watchman --version 2>/dev/null)" || echo "missing"; }
chk_rbenv() { command -v rbenv &>/dev/null && echo "ok" || echo "missing"; }
chk_ruby() { command -v ruby &>/dev/null && { v=$(ruby -v 2>/dev/null | cut -d' ' -f2); [[ "$v" == "$RUBY_VERSION"* ]] && echo "ok:$v" || echo "wrong:$v"; } || echo "missing"; }
chk_pods() { command -v pod &>/dev/null && echo "ok:$(pod --version 2>/dev/null)" || echo "missing"; }
chk_bundler() { command -v bundle &>/dev/null && echo "ok" || echo "missing"; }
chk_java() { command -v java &>/dev/null && { v=$(java -version 2>&1 | head -1 | cut -d'"' -f2); [[ "$v" == 17* ]] && echo "ok:$v" || echo "wrong:$v"; } || echo "missing"; }
chk_xcode() { xcode-select -p &>/dev/null && [[ "$(xcode-select -p)" == *Xcode.app* ]] && echo "ok" || echo "missing"; }
chk_studio() { [[ -d "/Applications/Android Studio.app" ]] && echo "ok" || echo "missing"; }
chk_sdk() { [[ -d "${ANDROID_HOME:-$HOME/Library/Android/sdk}" ]] && echo "ok" || echo "missing"; }
chk_ndk() { [[ -d "${ANDROID_HOME:-$HOME/Library/Android/sdk}/ndk/27.0.12077973" ]] && echo "ok" || echo "missing"; }
chk_shell() { local rc=~/.zshrc; [[ "$SHELL" == *bash* ]] && rc=~/.bashrc; grep -q "ANDROID_HOME" "$rc" 2>/dev/null && echo "ok" || echo "missing"; }
# Install functions
inst_brew() { /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"; }
inst_nvm() { curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash; export NVM_DIR="$HOME/.nvm"; [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"; }
inst_node() { export NVM_DIR="$HOME/.nvm"; [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"; nvm install $NODE_MAJOR; }
inst_watch() { brew install watchman; }
inst_rbenv() { brew install rbenv; eval "$(rbenv init -)"; }
inst_ruby() { eval "$(rbenv init -)" 2>/dev/null; rbenv install "$RUBY_VERSION"; rbenv rehash; }
inst_pods() { gem install cocoapods; }
inst_bundler() { gem install bundler; }
inst_java() { brew install openjdk@17; sudo ln -sfn "$(brew --prefix openjdk@17)/libexec/openjdk.jdk" /Library/Java/JavaVirtualMachines/openjdk-17.jdk 2>/dev/null || true; }
inst_shell() {
local rc=~/.zshrc
[[ "$SHELL" == *bash* ]] && rc=~/.bashrc
# Check if already configured
if grep -q "# Self.xyz Dev Environment" "$rc" 2>/dev/null; then
ok "Shell already configured"
return 0
fi
info "Adding environment to $rc..."
cat >> "$rc" << 'EOF'
# Self.xyz Dev Environment
export JAVA_HOME=$(/usr/libexec/java_home -v 17 2>/dev/null || echo "")
export ANDROID_HOME=~/Library/Android/sdk
export ANDROID_SDK_ROOT=$ANDROID_HOME
export PATH=$PATH:$ANDROID_HOME/emulator:$ANDROID_HOME/platform-tools
command -v rbenv &>/dev/null && eval "$(rbenv init -)"
export NVM_DIR="$HOME/.nvm"; [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
EOF
ok "Shell configured. Run: source $rc"
}
# Main
echo -e "\n${C}${BOLD}═══ Self.xyz macOS Setup ═══${NC}\n"
# Interactive mode selection (if no flags provided)
if [[ "$CHECK_ONLY" == false && "$AUTO_YES" == false ]]; then
echo "How would you like to run the setup?"
echo ""
echo " 1) Check only - just show what's installed/missing"
echo " 2) Interactive setup - check and confirm before installing (recommended)"
echo " 3) Auto-install - install everything without prompts"
echo ""
read -p "Enter choice [1]: " -n 1 -r choice
echo ""
case $choice in
2) ;; # interactive setup
3) AUTO_YES=true ;;
*) CHECK_ONLY=true ;; # default: check only
esac
echo ""
fi
# Define deps: name|check_fn|install_fn|manual_msg
DEPS=(
"Homebrew|chk_brew|inst_brew|"
"nvm|chk_nvm|inst_nvm|"
"Node.js $NODE_MAJOR|chk_node|inst_node|"
"Watchman|chk_watch|inst_watch|"
"rbenv|chk_rbenv|inst_rbenv|"
"Ruby $RUBY_VERSION|chk_ruby|inst_ruby|"
"CocoaPods|chk_pods|inst_pods|"
"Bundler|chk_bundler|inst_bundler|"
"Java 17|chk_java|inst_java|"
"Xcode|chk_xcode||Install from App Store: https://apps.apple.com/app/xcode/id497799835"
"Android Studio|chk_studio||Download: https://developer.android.com/studio"
"Android SDK|chk_sdk||Open Android Studio → SDK Manager"
"Android NDK|chk_ndk||SDK Manager → SDK Tools → NDK 27.0.12077973"
"Shell Config|chk_shell|inst_shell|"
)
MISSING=()
MANUAL=()
info "Checking dependencies...\n"
for dep in "${DEPS[@]}"; do
IFS='|' read -r name chk inst manual <<< "$dep"
status=$($chk)
if [[ "$status" == ok* ]]; then
ver="${status#ok:}"; [[ -n "$ver" && "$ver" != "ok" ]] && ok "$name ($ver)" || ok "$name"
elif [[ -n "$manual" ]]; then
warn "$name - manual install required"
MANUAL+=("$name|$manual")
else
err "$name - not installed"
[[ -n "$inst" ]] && MISSING+=("$name|$inst")
fi
done
$CHECK_ONLY && {
[[ ${#MANUAL[@]} -gt 0 ]] && { echo -e "\n${Y}Manual installs:${NC}"; for m in "${MANUAL[@]}"; do IFS='|' read -r n msg <<< "$m"; echo " $n: $msg"; done; }
exit 0
}
# Install missing
if [[ ${#MISSING[@]} -gt 0 ]]; then
echo -e "\n${B}Missing:${NC} $(printf '%s\n' "${MISSING[@]}" | cut -d'|' -f1 | tr '\n' ', ' | sed 's/, $//')"
if confirm "Install all?"; then
for m in "${MISSING[@]}"; do
IFS='|' read -r name fn <<< "$m"
info "Installing $name..."
$fn && ok "$name installed" || err "Failed: $name"
done
fi
fi
# Manual instructions
[[ ${#MANUAL[@]} -gt 0 ]] && { echo -e "\n${Y}Manual installs needed:${NC}"; for m in "${MANUAL[@]}"; do IFS='|' read -r n msg <<< "$m"; echo " $n: $msg"; done; }
# Yarn install
echo ""
if confirm "Run 'yarn install' in repo root?"; then
info "Running yarn install..."
set +e # Temporarily disable exit-on-error
cd "$REPO_ROOT" && yarn install
yarn_exit=$?
set -e # Re-enable exit-on-error
if [[ $yarn_exit -eq 0 ]]; then
ok "Done!"
else
err "Yarn install failed (exit code: $yarn_exit)"
warn "This may be due to network issues or registry timeouts"
info "Try running manually: cd $REPO_ROOT && yarn install"
fi
fi
echo -e "\n${G}${BOLD}Setup complete!${NC} Open a new terminal, then: cd $APP_DIR && yarn ios\n"