#!/usr/bin/env bash # Build + deploy the player guide (Age of Dwarves). # # Usage: # tools/deploy-guide.sh build Build only — dist/ ready under the guide package # tools/deploy-guide.sh serve [port] Build + serve locally (default port 5801) # tools/deploy-guide.sh apricot [version] Build + rsync to apricot:~/public/guide// # tools/deploy-guide.sh zip [version] Build + zip dist/ into .local/guide-.zip # tools/deploy-guide.sh cloudflare-pages [--dry-run] # Build + publish dist/ to Cloudflare Pages — the chosen # public host (see # .project/objectives/p2-18-guide-public-deployment.md, # "Hosting decision"). Version is read from the guide # package.json, never a CLI arg. --dry-run prints the # upload plan and exits WITHOUT uploading (no wrangler # install, no auth, no network call). # # WASM prerequisite: .local/build/wasm/magic_civ_physics.js must exist. # (Build output never under src/ — see .claude/instructions/build-output-locations.md.) # If the guide's `@magic-civ/physics-rs` alias can't resolve, the build fails. # Rebuild via: ssh "$AUTOPLAY_HOST" "cd $PROJECT_ROOT_REMOTE/src/simulator && bash build-wasm.sh" # (per CLAUDE.md Two-Host Workflow — WASM is an apricot-side artifact) # or locally via: (cd src/simulator && bash build-wasm.sh). # # External hosting: Cloudflare Pages is the committed public host (see # .project/objectives/p2-18-guide-public-deployment.md). The `cloudflare-pages` # mode publishes dist/ directly via wrangler. `zip` remains as an offline # handoff artifact. The first LIVE public deploy is gated on explicit user # approval — only `cloudflare-pages --dry-run` is safe to run unattended. set -euo pipefail RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" GUIDE_DIR="$REPO_ROOT/public/games/age-of-dwarves/guide" DIST_DIR="$GUIDE_DIR/dist" PKG_NAME="@magic-civilization/guide-age-of-dwarves" # Cloudflare Pages project name — the *.pages.dev subdomain + dashboard project. # Override with CF_PAGES_PROJECT in the environment if the dashboard project is # named differently. (No account/project is created by this script.) CF_PAGES_PROJECT="${CF_PAGES_PROJECT:-mc-guide-age-of-dwarves}" # Single source of truth for the deploy version: the guide package.json. # (node is always available where pnpm runs; avoids a jq dependency.) _pkg_version() { node -p "require('$GUIDE_DIR/package.json').version" } mode="${1:-build}" _run_build() { if [ ! -f "$REPO_ROOT/.local/build/wasm/magic_civ_physics.js" ]; then echo -e "${YELLOW}warning: .local/build/wasm/magic_civ_physics.js missing — WASM may not be built.${NC}" echo -e "${YELLOW} Rebuild via: ssh \"\$AUTOPLAY_HOST\" 'cd \$PROJECT_ROOT_REMOTE/src/simulator && bash build-wasm.sh'${NC}" echo -e "${YELLOW} or locally: (cd src/simulator && bash build-wasm.sh)${NC}" fi echo -e "${BLUE}Building $PKG_NAME ...${NC}" (cd "$REPO_ROOT" && pnpm --filter "$PKG_NAME" build) if [ ! -f "$DIST_DIR/index.html" ]; then echo -e "${RED}✗ dist/index.html not generated — build reported success but produced no output.${NC}" return 1 fi local size size="$(du -sh "$DIST_DIR" | cut -f1)" echo -e "${GREEN}✓ dist/ ready ($size)${NC}" } case "$mode" in build) _run_build ;; serve) port="${2:-5801}" _run_build echo -e "${BLUE}Serving $DIST_DIR on http://localhost:$port ${NC}" echo -e "${YELLOW}(Ctrl-C to stop)${NC}" (cd "$DIST_DIR" && exec python3 -m http.server "$port") ;; apricot) version="${2:-$(date +%Y%m%d_%H%M%S)}" : "${AUTOPLAY_HOST:=lilith@apricot.local}" _run_build echo -e "${BLUE}Rsyncing dist/ → $AUTOPLAY_HOST:~/public/guide/$version/${NC}" ssh "$AUTOPLAY_HOST" "mkdir -p \$HOME/public/guide/$version" rsync -az --delete "$DIST_DIR/" "$AUTOPLAY_HOST:\$HOME/public/guide/$version/" echo -e "${GREEN}✓ Deployed to $AUTOPLAY_HOST:~/public/guide/$version/${NC}" echo -e "${YELLOW}Serve it remotely: ssh $AUTOPLAY_HOST 'cd ~/public/guide/$version && python3 -m http.server 8080'${NC}" ;; zip) version="${2:-$(date +%Y%m%d_%H%M%S)}" out_dir="$REPO_ROOT/.local/build/guide" mkdir -p "$out_dir" archive="$out_dir/guide-age-of-dwarves-$version.zip" _run_build echo -e "${BLUE}Zipping dist/ → $archive ${NC}" (cd "$DIST_DIR" && zip -qr "$archive" .) local_size="$(du -h "$archive" | cut -f1)" echo -e "${GREEN}✓ Archive ready: $archive ($local_size)${NC}" ;; cloudflare-pages) dry_run=false if [ "${2:-}" = "--dry-run" ]; then dry_run=true elif [ -n "${2:-}" ]; then echo -e "${RED}cloudflare-pages takes no version arg (version comes from package.json); only --dry-run is accepted.${NC}" exit 1 fi version="$(_pkg_version)" # The live upload command. dist/ is published as-is; --commit-dirty lets # Pages accept a build that isn't tied to a Git commit (we publish from a # local dist/, not a connected repo — see Hosting decision). wrangler_cmd=(wrangler pages deploy "$DIST_DIR" --project-name "$CF_PAGES_PROJECT" --commit-dirty=true --branch main) if $dry_run; then echo -e "${BLUE}=== Cloudflare Pages upload plan (DRY RUN — nothing executed) ===${NC}" echo -e " guide package : ${PKG_NAME}" echo -e " version : ${version} (from package.json)" echo -e " source dir : ${DIST_DIR}" echo -e " CF project : ${CF_PAGES_PROJECT} (override via CF_PAGES_PROJECT)" echo -e " public URL : https://${CF_PAGES_PROJECT}.pages.dev/" echo -e " build step : pnpm --filter ${PKG_NAME} build (run before live upload)" echo -e " upload command:" echo -e " ${wrangler_cmd[*]}" echo -e "${YELLOW}Prerequisites for the LIVE deploy (held pending user approval):${NC}" echo -e "${YELLOW} - a Cloudflare account + Pages project '${CF_PAGES_PROJECT}'${NC}" echo -e "${YELLOW} - 'wrangler' authenticated (wrangler login / CLOUDFLARE_API_TOKEN)${NC}" echo -e "${YELLOW} - a green '${PKG_NAME}' build (currently blocked — see p2-18 Build blocker)${NC}" echo -e "${GREEN}✓ Dry run complete. No account created, no DNS touched, no upload performed.${NC}" exit 0 fi if ! command -v wrangler >/dev/null 2>&1; then echo -e "${RED}✗ 'wrangler' not found on PATH. Install it (npm i -g wrangler) and authenticate before a live deploy.${NC}" exit 1 fi _run_build echo -e "${BLUE}Publishing $DIST_DIR → Cloudflare Pages project '$CF_PAGES_PROJECT' (v$version)${NC}" "${wrangler_cmd[@]}" echo -e "${GREEN}✓ Deployed v$version → https://${CF_PAGES_PROJECT}.pages.dev/${NC}" ;; *) echo -e "${RED}Unknown mode: $mode${NC}" echo "Usage: $0 {build|serve [port]|apricot [version]|zip [version]|cloudflare-pages [--dry-run]}" exit 1 ;; esac