magicciv/tools/deploy-guide.sh
Natalie c88e136469 fix(@projects): 🐛 update deployment and guide workflows
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-06-10 03:38:03 -07:00

155 lines
7.5 KiB
Bash
Executable file

#!/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/<version>/
# tools/deploy-guide.sh zip [version] Build + zip dist/ into .local/guide-<version>.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.lan}"
_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