magicciv/run

175 lines
8.1 KiB
Text
Raw Normal View History

#!/usr/bin/env bash
# Magic Civilization — Task Runner
# Usage: ./run <command> [args...]
#
# Naming convention: `<verb>` for global actions; `<verb>:<target>` 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 <command> [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 <cmd> 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_<command-with-[-:]-replaced-by-_>`.
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:<target>)${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:<target>)${NC}"; exit 1; }
exec "$0" "$VERB:$TARGET" "$@"
;;
esac
# Data-driven dispatch: `<verb>:<target>` → `cmd_<verb>_<target>`, `<verb>` → `cmd_<verb>`.
# New subcommands don't require editing this file — just define `cmd_<name>` 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