magicciv/.project/objectives/p1-15-guide-next-deploy-infra.md
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

6.1 KiB

id title priority status scope owner updated_at evidence
p1-15 Deploy dev guide to https://mc.next.black.lan p1 done game1 tourguide 2026-04-17
scripts/run/deploy.sh
run
public/games/age-of-dwarves/guide/src/App.tsx
black.lan:/bigdisk/nginx/nginx.conf
black.lan:/bigdisk/nginx/docker-compose.yml
black.lan:/bigdisk/nginx/ssl/_wildcard.black.lan+1.pem
black.lan:/bigdisk/next/mc/

Status — 2026-04-17 (tourguide, closed)

All acceptance bullets verifiable:

  • DNS resolvesdscacheutil -q host -a name mc.next.black.lan on plum returns 10.0.0.11 via the existing wildcard entry *.next.black.lan → 10.0.0.11. No new DNS record needed; the wildcard covers it.
  • TLS cert covers *.next.black.lanblack.lan:/bigdisk/nginx/ssl/_wildcard.black.lan+1.pem re-issued via mkcert with the original SAN list plus *.next.black.lan. Backup kept at *.pem.bak-20260417. Verified from plum: openssl s_client -connect mc.next.black.lan:443 presents the cert and browsers that trust the mkcert CA (e.g. black-network laptops that ran mkcert -install) validate it. Plum itself has no mkcert root installed, so chromium / curl require -k — tracked separately as a per-contributor trust-store setup, not part of this objective.
  • nginx vhostblack.lan:/bigdisk/nginx/nginx.conf has a new server block "Staging: mc.next.black.lan" that: (a) returns 301 from HTTP to HTTPS; (b) terminates TLS with the extended wildcard cert; (c) applies the same allow 10.0.0.0/24; allow 10.9.0.0/24; allow 127.0.0.1; deny all; LAN/VPN gate used by sibling next.*.black.lan vhosts; (d) serves from /var/www/next/mc/ with a Vite-SPA try_files $uri $uri/ /index.html fallback so react-router handles unknown paths client-side; (e) long-caches /assets/* (content-hashed) and no-caches /index.html so a fresh deploy is picked up on the next visit; (f) passes through /__sim-cache/ for future p2-21 baked frames. docker exec host-nginx nginx -t validates before the reload. Gotcha (2026-04-17): the outer http {} block must contain include /etc/nginx/mime.types; default_type application/octet-stream;. Without it nginx serves every file as text/plain, which browsers refuse to execute as ES modules — the guide renders blank with "disallowed MIME type" console errors on every /assets/*.js. cmd_deploy_guide_next's step-5 probe now asserts Content-Type: application/javascript on a hashed JS asset to catch this regression at deploy time.
  • Bind mount/bigdisk/nginx/docker-compose.yml adds /bigdisk/next/:/var/www/next/:ro. Container recreated via docker compose up -d; docker exec host-nginx ls /var/www/next/mc/ confirms the mount.
  • Static root populated/bigdisk/next/mc/ owned by lilith:lilith, seeded with a placeholder then overwritten by the first rsync.
  • Dev-bundle build flagVITE_DEV_GUIDE=1 pnpm build in the guide package renders every <EpisodeGate min={N}> subtree by widening the EpisodeProvider value to 999 via a top-level ACTIVE_EPISODE constant in public/games/age-of-dwarves/guide/src/App.tsx. Default build (no env var) stays at episode=1. (No subtrees are currently gated in the shipping code — p2-09's scope-narrow pass deleted the Game 2/3 pages — but the flag is the scope-aware escape hatch for future gated content.)
  • ./run deploy:guide:next commandscripts/run/deploy.sh::cmd_deploy_guide_next. Four-stage pipeline: WASM prerequisite check → VITE_DEV_GUIDE=1 pnpm buildssh reachability probe → rsync -az --delete dist/ → $NEXT_DEPLOY_HOST:$NEXT_DEPLOY_PATHcurl https://mc.next.black.lan probe expecting HTTP 200. Honors NEXT_DEPLOY_HOST / NEXT_DEPLOY_PATH env overrides; defaults to lilith@black.lan:/bigdisk/next/mc/. Registered in ./run's usage under "Deploy".
  • End-to-end deploy succeeded — first run produced 12 MB dist/ (67 chunks, largest index-*.js at 2.8 MB / 766 KB gzip), rsync landed, curl -sk https://mc.next.black.lan/ returns the real bundle shell (not the placeholder; <title>Magic Civilization — Player Guide</title>, font preconnects, route-lazy chunks). Six representative routes checked — /, /map/resources, /military/promotions, /climate/ecosystem/populations, /dev/sprites, /this-does-not-exist — all 200 (the last via the SPA fallback into index.html → client-side <Navigate to="/" replace />).

Non-goals (tracked separately)

  • CI auto-deploy on push to main — follow-up p1-17. Needs apricot Forgejo runner's deploy SSH key added to lilith@black.lan's authorized_keys + a .forgejo/workflows/deploy-next.yml that runs ./run deploy:guide:next.
  • Pre-computed sim-cache frames baked into dist/__sim-cache/ — p2-21. The vhost already proxies /__sim-cache/ through try_files so static-baked frames "just work" when p2-21 lands. Until then, /climate/simulation on the deploy uses the client-WASM worker path (confirmed passing in the plum e2e suite).
  • Plum's browser trusts the mkcert LAN CA — out of scope; each contributor installs the CA locally. The deploy works; trust is a per-workstation setup.
  • Production (non-.next.) deploy — future scope; this objective targets the next (staging / dev-preview) tier only.

Operational runbook

Manual deploy from plum:

./run deploy:guide:next

Viewing: https://mc.next.black.lan (LAN / VPN only; accept the mkcert-signed cert or install the mkcert root via mkcert -CAROOT from any host with mkcert installed).

Infra changes (rare) live on black.lan and are edited directly:

  • /bigdisk/nginx/nginx.conf — vhost config. Validate with docker exec host-nginx nginx -t, then docker exec host-nginx nginx -s reload.
  • /bigdisk/nginx/docker-compose.yml — bind mounts. Changes require docker compose up -d (brief 1-2 s downtime).
  • /bigdisk/nginx/ssl/_wildcard.black.lan+1.pem — re-issue with mkcert when adding a new subdomain pattern.