magicciv/scripts/dev-setup/osx.sh

322 lines
13 KiB
Bash
Raw Normal View History

#!/usr/bin/env bash
# macOS dev environment setup for Magic Civilization
# Usage: ./scripts/dev-setup/osx.sh [--skip-godot] [--skip-rust] [--with-runner]
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
WITH_RUNNER=false
for arg in "$@"; do
case "$arg" in
--skip-godot) SKIP_GODOT=true ;;
--skip-rust) SKIP_RUST=true ;;
--with-runner) WITH_RUNNER=true ;;
--help|-h)
echo "Usage: $0 [--skip-godot] [--skip-rust] [--with-runner]"
echo " --skip-godot Skip Godot 4 installation"
echo " --skip-rust Skip Rust toolchain installation"
echo " --with-runner Also install + register a forgejo-runner"
echo " (requires FORGEJO_* env — see .env.example)"
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
if $WITH_RUNNER; then
echo ""
echo -e "${BLUE}─────────────────────────────────────────────────${NC}"
echo -e "${BLUE} Forgejo runner (macOS / arm64 / plum)${NC}"
echo -e "${BLUE}─────────────────────────────────────────────────${NC}"
source "$REPO_ROOT/scripts/run/common.sh"
source "$REPO_ROOT/scripts/dev-setup/lib/runner.sh"
export RUNNER_OS=darwin
export RUNNER_ARCH=arm64
export RUNNER_NAME="$(hostname -s)"
export RUNNER_LABELS="self-hosted,macos,arm64"
runner_install_binary
runner_register
# launchd persistence — user-agent, no root needed.
PLIST="$HOME/Library/LaunchAgents/com.forgejo.runner.plist"
if [[ ! -f "$PLIST" ]]; then
mkdir -p "$(dirname "$PLIST")"
cat > "$PLIST" <<PLIST
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key><string>com.forgejo.runner</string>
<key>ProgramArguments</key>
<array>
<string>$HOME/.local/bin/forgejo-runner</string>
<string>daemon</string>
<string>--config</string>
<string>$HOME/.local/share/forgejo-runner/config.yaml</string>
</array>
<key>WorkingDirectory</key><string>$HOME/.local/share/forgejo-runner</string>
<key>RunAtLoad</key><true/>
<key>KeepAlive</key><true/>
<key>StandardOutPath</key><string>$HOME/.local/share/forgejo-runner/runner.out.log</string>
<key>StandardErrorPath</key><string>$HOME/.local/share/forgejo-runner/runner.err.log</string>
</dict>
</plist>
PLIST
echo " runner: wrote $PLIST"
fi
launchctl unload "$PLIST" 2>/dev/null || true
launchctl load "$PLIST"
echo " runner: launchd agent loaded (RunAtLoad + KeepAlive)"
runner_verify_online || true
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"
$WITH_RUNNER && echo -e " ${DIM}launchctl list | grep forgejo${NC} — verify runner daemon"
echo ""