#!/usr/bin/env bash # Magic Civilization — Task Runner # Usage: ./run [args...] # # Naming convention: `` for global actions; `:` for subcommands. # Examples: lint / lint:rust, build / build:wasm, install:osx, smoke:linux. set -uo pipefail REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # Source all run modules for _script in "$REPO_ROOT/scripts/run/"*.sh; do source "$_script" done unset _script usage() { echo -e "${BLUE}Magic Civilization${NC} — Task Runner" echo "" echo "Usage: ./run [args...]" echo "" echo -e "${YELLOW}Development${NC}" echo " play Launch the game locally" echo " editor Open Godot editor" echo " guide Start guide dev server (port 5800) designs [port] Serve .project/designs/ HTML sketches (default port 7777)" echo " lint Lint all (GDScript + Rust + TypeScript)" echo " lint:gd GDScript only (gdlint + gdformat --check)" echo " lint:rust Rust only (fmt --check + clippy + machete)" echo " lint:ts TypeScript only (ESLint + tsc typecheck)" echo " format Format all (GDScript + Rust + TypeScript)" echo " format:gd GDScript only (gdformat)" echo " format:rust Rust only (cargo fmt)" echo " format:ts TypeScript only (ESLint --fix)" echo " typecheck pnpm -r typecheck (all TS packages)" echo " validate Validate game data JSON files against schemas" echo " test Run GUT + Rust (nextest if available) + vitest" echo " test:golden Cross-language golden-vector parity (Rust + WASM + GDExt)" echo " coverage Coverage reports (cargo llvm-cov + pnpm test:coverage)" echo " verify Full pipeline: schemas + i18n + build + tests + lint + docs + LOC cap" echo " screenshot [name] [scene] [delay] Capture screenshot" echo " autoplay [seed] Run single seeded auto_play game + report (opt-in)" echo " autoplay-batch [count] Run N seeded games + aggregate report (opt-in)" echo "" echo -e "${YELLOW}Build${NC}" echo " build Build WASM + GDExtension" echo " build:wasm Build WASM only (src/simulator → pkg/)" echo " build:gdext Build GDExtension only (src/simulator → src/game/addons/)" echo "" echo -e "${YELLOW}Export${NC}" echo " export [version] Export all platforms (parallel)" echo " export:macos [version] Export macOS only" echo " export:linux [version] Export Linux only" echo " export:windows [version] Export Windows only" echo " export:android [version] Export Android APK" echo " export:ios [version] Export iOS Xcode project" echo "" echo -e "${YELLOW}Install (deploy + launch on target)${NC}" echo " install:osx [version] [--dev] Export + install .app on \$OSX_HOST (default: plum)" echo " install:linux [version] [--dev] Export + install binary on \$LINUX_HOST" echo " install:iphone [version] [--dev] Export + xcodebuild + devicectl install via \$OSX_HOST" echo " install:android [version] [--dev] Export + adb install on connected Android device" echo "" echo -e "${YELLOW}Remote control${NC}" echo " start:osx Launch installed app on \$OSX_HOST" echo " start:linux Launch installed binary on \$LINUX_HOST" echo " start:ios Launch app on connected iPhone (via \$OSX_HOST)" echo " stop:osx Kill running app on \$OSX_HOST" echo " stop:linux Kill running binary on \$LINUX_HOST" echo " smoke:osx Full smoke test (macOS — export → ship → launch → screenshot)" echo " smoke:linux [boot-secs] Full smoke test (Linux — default 20s boot wait)" echo "" echo -e "${YELLOW}Tools${NC}" echo " tools:spritegen Sprite generation pipeline" echo " setup Install/verify all dev dependencies (auto-detects OS)" echo " setup:lan-dns Subscribe this host to black.lan dnsmasq (idempotent)" echo "" echo -e "${YELLOW}Deploy${NC}" echo " deploy:guide:next Build dev guide (all episodes) + rsync to mc.next.black.lan" echo " DEPLOY_BAKE_SCENARIOS=base_no_magic|all bakes sim-cache before rsync" echo " bake:simcache [ids|all] Pre-compute sim-cache frames into dist/__sim-cache/" } # ── Install args parser (shared by install:* targets) ──────────────── # Separates --dev from positional args; echoes them as # "DEV_MODE|pos1 pos2 ..." for callers to split. _parse_install_args() { local dev_flag="" local pos=() for arg in "$@"; do case "$arg" in --dev) dev_flag="--dev" ;; *) pos+=("$arg") ;; esac done echo "$dev_flag|${pos[*]}" } _dispatch_install() { local target="$1"; shift local parsed dev_flag pos_args parsed="$(_parse_install_args "$@")" dev_flag="${parsed%%|*}" pos_args="${parsed#*|}" # shellcheck disable=SC2086 case "$target" in osx) cmd_install_osx $dev_flag $pos_args ;; linux) cmd_install_linux $dev_flag $pos_args ;; iphone) cmd_install_ios iphone $dev_flag $pos_args ;; sim) cmd_install_ios sim $dev_flag $pos_args ;; android) cmd_install_android $dev_flag $pos_args ;; *) echo -e "${RED}Unknown install target: $target${NC}"; echo "Available: osx, linux, iphone, android"; return 1 ;; esac } COMMAND="${1:-}" shift 2>/dev/null || true # Special cases (subcommand requires alias, multi-arg dispatch, help). # Everything else resolves to `cmd_`. case "$COMMAND" in help|--help|-h|"") usage; exit 0 ;; # Export single-target takes a positional `target` arg. export:windows|export:macos|export:linux|export:android|export:ios) cmd_export_single "${COMMAND#export:}" "$@"; exit $? ;; # Install dispatches through a shared args parser. install:osx|install:macos) _dispatch_install osx "$@"; exit $? ;; install:linux) _dispatch_install linux "$@"; exit $? ;; install:iphone|install:ios) _dispatch_install iphone "$@"; exit $? ;; install:sim) _dispatch_install sim "$@"; exit $? ;; install:android) _dispatch_install android "$@"; exit $? ;; # Aliases so `install:macos` → `install:osx` (etc.) dispatch to the canonical fn. start:macos) cmd_start_osx "$@"; exit $? ;; stop:macos) cmd_stop_osx "$@"; exit $? ;; smoke:macos) cmd_smoke_osx "$@"; exit $? ;; start:iphone) cmd_start_ios "$@"; exit $? ;; # Legacy space-form aliases: `install osx --dev` → `install:osx --dev` install) TARGET=""; POS=() for arg in "$@"; do case "$arg" in osx|macos|linux|iphone|ios|sim|android) TARGET="$arg" ;; *) POS+=("$arg") ;; esac done [ -n "$TARGET" ] || { echo -e "${RED}install requires a target (use install:)${NC}"; exit 1; } _dispatch_install "${TARGET/macos/osx}" "${POS[@]+"${POS[@]}"}" exit $? ;; start|stop|smoke|tools) VERB="$COMMAND"; TARGET="${1:-}"; shift 2>/dev/null || true [ -n "$TARGET" ] || { echo -e "${RED}$VERB requires a target (use $VERB:)${NC}"; exit 1; } exec "$0" "$VERB:$TARGET" "$@" ;; esac # Data-driven dispatch: `:` → `cmd__`, `` → `cmd_`. # New subcommands don't require editing this file — just define `cmd_` in # any `scripts/run/*.sh` (or subcommand alias in `scripts/dev-setup/`). FUNC="cmd_${COMMAND//[:-]/_}" if declare -F "$FUNC" >/dev/null; then "$FUNC" "$@" exit $? fi echo -e "${RED}Unknown command: $COMMAND${NC}" echo "" usage exit 1