#!/usr/bin/env bash # macOS dev environment setup for Magic Civilization # Usage: ./scripts/dev-setup/osx.sh [--skip-godot] [--skip-rust] set -euo pipefail RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' DIM='\033[2m' NC='\033[0m' SKIP_GODOT=false SKIP_RUST=false for arg in "$@"; do case "$arg" in --skip-godot) SKIP_GODOT=true ;; --skip-rust) SKIP_RUST=true ;; --help|-h) echo "Usage: $0 [--skip-godot] [--skip-rust]" echo " --skip-godot Skip Godot 4 installation" echo " --skip-rust Skip Rust toolchain installation" exit 0 ;; esac done REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" ok() { echo -e " ${GREEN}OK${NC} $1"; } skip() { echo -e " ${DIM}SKIP${NC} $1"; } info() { echo -e " ${BLUE}...${NC} $1"; } warn() { echo -e " ${YELLOW}WARN${NC} $1"; } fail() { echo -e " ${RED}FAIL${NC} $1"; } # Track what was installed so we can summarize at the end INSTALLED=() ALREADY=() SKIPPED=() FAILED=() check_or_install() { local name="$1" local check_cmd="$2" local install_cmd="$3" local skip_flag="${4:-false}" if [ "$skip_flag" = "true" ]; then skip "$name (--skip flag)" SKIPPED+=("$name") return 0 fi if eval "$check_cmd" &>/dev/null; then ok "$name (already installed)" ALREADY+=("$name") return 0 fi info "Installing $name..." if eval "$install_cmd"; then ok "$name" INSTALLED+=("$name") else fail "$name" FAILED+=("$name") return 1 fi } echo "" echo -e "${BLUE}Magic Civilization — macOS Dev Setup${NC}" echo -e "${DIM}Godot 4.3 + Rust + wasm-pack + gdtoolkit + pnpm${NC}" echo "" # ── Homebrew ────────────────────────────────────────────────────────── echo -e "${BLUE}[1/7] Homebrew${NC}" if ! command -v brew &>/dev/null; then info "Installing Homebrew..." /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" # Add to path for Apple Silicon if [ -f /opt/homebrew/bin/brew ]; then eval "$(/opt/homebrew/bin/brew shellenv)" fi INSTALLED+=("homebrew") else ok "Homebrew ($(brew --version | head -1))" ALREADY+=("homebrew") fi # ── Godot 4 ────────────────────────────────────────────────────────── echo -e "${BLUE}[2/7] Godot 4${NC}" check_or_install "Godot 4" \ "ls /Applications/Godot.app &>/dev/null || brew list --cask godot &>/dev/null" \ "brew install --cask godot" \ "$SKIP_GODOT" # ── Rust toolchain ─────────────────────────────────────────────────── echo -e "${BLUE}[3/7] Rust toolchain${NC}" if [ "$SKIP_RUST" = "true" ]; then skip "Rust (--skip-rust flag)" SKIPPED+=("rust") else if command -v rustc &>/dev/null; then ok "Rust $(rustc --version | awk '{print $2}')" ALREADY+=("rust") else info "Installing Rust via rustup..." curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable source "$HOME/.cargo/env" ok "Rust $(rustc --version | awk '{print $2}')" INSTALLED+=("rust") fi # wasm32 target (needed for guide WASM build) echo -e "${BLUE}[3b] wasm32-unknown-unknown target${NC}" if rustup target list --installed | grep -q wasm32-unknown-unknown; then ok "wasm32 target" ALREADY+=("wasm32-target") else info "Adding wasm32-unknown-unknown target..." rustup target add wasm32-unknown-unknown ok "wasm32 target" INSTALLED+=("wasm32-target") fi # macOS native target for GDExtension ARCH="$(uname -m)" if [ "$ARCH" = "arm64" ]; then DARWIN_TARGET="aarch64-apple-darwin" else DARWIN_TARGET="x86_64-apple-darwin" fi echo -e "${BLUE}[3c] $DARWIN_TARGET target${NC}" if rustup target list --installed | grep -q "$DARWIN_TARGET"; then ok "$DARWIN_TARGET target" ALREADY+=("$DARWIN_TARGET") else info "Adding $DARWIN_TARGET target..." rustup target add "$DARWIN_TARGET" ok "$DARWIN_TARGET target" INSTALLED+=("$DARWIN_TARGET") fi fi # ── wasm-pack ──────────────────────────────────────────────────────── echo -e "${BLUE}[4/7] wasm-pack${NC}" if [ "$SKIP_RUST" = "true" ]; then skip "wasm-pack (--skip-rust flag)" SKIPPED+=("wasm-pack") else check_or_install "wasm-pack" \ "command -v wasm-pack" \ "cargo install wasm-pack" fi # ── Python + gdtoolkit ─────────────────────────────────────────────── echo -e "${BLUE}[5/7] gdtoolkit (gdlint + gdformat)${NC}" check_or_install "gdtoolkit" \ "command -v gdlint" \ "pip3 install --break-system-packages gdtoolkit || pip3 install gdtoolkit" # ── Node.js + pnpm ────────────────────────────────────────────────── echo -e "${BLUE}[6/7] Node.js${NC}" check_or_install "Node.js" \ "command -v node" \ "brew install node" echo -e "${BLUE}[6b] pnpm${NC}" check_or_install "pnpm" \ "command -v pnpm" \ "npm install -g pnpm" # ── cargo extras: binstall + machete + deny + nextest + llvm-cov ───── # These back the ./run verify and ./run coverage pipelines. binstall # first so the rest can skip from-source cargo builds. if [ "$SKIP_RUST" != "true" ]; then echo -e "${BLUE}[6c] cargo-binstall${NC}" check_or_install "cargo-binstall" \ "command -v cargo-binstall" \ "curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash" _cargo_install_tool() { # $1 = tool binary name (e.g. cargo-machete) # $2 = crate name to install (usually same) local bin="$1"; local crate="${2:-$1}" if command -v cargo-binstall &>/dev/null; then check_or_install "$bin" "command -v $bin" "cargo binstall --no-confirm $crate" else check_or_install "$bin" "command -v $bin" "cargo install $crate" fi } echo -e "${BLUE}[6d] cargo-machete (dead-deps)${NC}" _cargo_install_tool cargo-machete echo -e "${BLUE}[6e] cargo-deny (security+licenses)${NC}" _cargo_install_tool cargo-deny echo -e "${BLUE}[6f] cargo-nextest (fast test runner)${NC}" _cargo_install_tool cargo-nextest echo -e "${BLUE}[6g] cargo-llvm-cov (coverage)${NC}" _cargo_install_tool cargo-llvm-cov fi # ── pnpm install ───────────────────────────────────────────────────── echo -e "${BLUE}[7/7] Project dependencies${NC}" if [ -f "$REPO_ROOT/pnpm-lock.yaml" ]; then info "Running pnpm install..." if (cd "$REPO_ROOT" && pnpm install 2>&1); then ok "pnpm dependencies" else warn "pnpm install had errors (private registry packages may need VPN/auth)" fi else skip "pnpm install (no pnpm-lock.yaml)" fi # ── Verify ─────────────────────────────────────────────────────────── echo "" echo -e "${BLUE}─────────────────────────────────────────────────${NC}" echo -e "${BLUE} Verification${NC}" echo -e "${BLUE}─────────────────────────────────────────────────${NC}" verify_cmd() { local label="$1" local cmd="$2" if eval "$cmd" &>/dev/null; then local version version=$(eval "$3" 2>/dev/null || echo "installed") echo -e " ${GREEN}OK${NC} $label ${DIM}($version)${NC}" else echo -e " ${RED}--${NC} $label" fi } verify_cmd "brew" "command -v brew" "brew --version | head -1" verify_cmd "godot" "ls /Applications/Godot.app || command -v godot" \ "echo 'Godot 4.3'" verify_cmd "rustc" "command -v rustc" "rustc --version" verify_cmd "cargo" "command -v cargo" "cargo --version" verify_cmd "wasm-pack" "command -v wasm-pack" "wasm-pack --version" verify_cmd "gdlint" "command -v gdlint" "gdlint --version 2>&1 || echo 'installed'" verify_cmd "gdformat" "command -v gdformat" "gdformat --version 2>&1 || echo 'installed'" verify_cmd "node" "command -v node" "node --version" verify_cmd "pnpm" "command -v pnpm" "pnpm --version" verify_cmd "cargo-machete" "command -v cargo-machete" "cargo-machete --version 2>&1 || echo installed" verify_cmd "cargo-deny" "command -v cargo-deny" "cargo-deny --version 2>&1 || echo installed" verify_cmd "cargo-nextest" "command -v cargo-nextest" "cargo-nextest --version 2>&1 || echo installed" verify_cmd "cargo-llvm-cov" "command -v cargo-llvm-cov" "cargo-llvm-cov --version 2>&1 || echo installed" # ── Summary ────────────────────────────────────────────────────────── echo "" echo -e "${BLUE}─────────────────────────────────────────────────${NC}" echo -e "${BLUE} Summary${NC}" echo -e "${BLUE}─────────────────────────────────────────────────${NC}" [ ${#INSTALLED[@]} -gt 0 ] && echo -e " ${GREEN}Installed:${NC} ${INSTALLED[*]}" [ ${#ALREADY[@]} -gt 0 ] && echo -e " ${DIM}Already had:${NC} ${ALREADY[*]}" [ ${#SKIPPED[@]} -gt 0 ] && echo -e " ${YELLOW}Skipped:${NC} ${SKIPPED[*]}" [ ${#FAILED[@]} -gt 0 ] && echo -e " ${RED}Failed:${NC} ${FAILED[*]}" if [ ${#FAILED[@]} -gt 0 ]; then echo "" echo -e " ${RED}Some tools failed to install. Fix the errors above and re-run.${NC}" exit 1 fi echo "" echo -e " ${GREEN}Ready to go.${NC} Try:" echo -e " ${DIM}./run verify${NC} — full lint + test pipeline" echo -e " ${DIM}./run play${NC} — launch the game" echo -e " ${DIM}./run guide${NC} — start guide dev server" echo ""