fix(deploy): serve at monsterrobot.games/games/blobbo/ (relative base)
The arcade moved to monsterrobot.games, where every game lives under /games/<name>/. BLOBBO's build was pinned to base '/blobbo/', so at the new mount its absolute /blobbo/assets/... requests 404'd and the module loader got HTML (the error John hit). Fix: - vite base -> './' (relative). One build now works at ANY mount point: index.html + editor.html sit at the deploy root, assets resolve as ./assets/... against the (trailing-slash) page URL, and assetUrl() reads import.meta.env.BASE_URL='./' so the manifest resolves the same way. Verified by serving dist/ under a /games/blobbo/ subpath locally: game + editor boot, manifest fetch resolves to /games/blobbo/assets/manifest.json. - deploy.sh publishes to the RIGHT place for each host. monsterrobot.games's docroot is bind-mounted READ-ONLY into forum-nginx from host /home/humanjing/monsterrobot.games/games/blobbo, so it's rsync'd on the host (docker cp is refused on the ro mount). partly.party's container-internal /blobbo stays a best-effort docker-cp copy. Both overlay (keep old chunks; prune >7d) and both are verified live (index 200 + entry chunk is JS). Live-verified: monsterrobot.games/games/blobbo/ boots (world+blob, course select, no console errors); partly.party/blobbo/ still boots too. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
f8fce7ff2b
commit
7918f9f6f1
91
deploy.sh
91
deploy.sh
@ -1,25 +1,38 @@
|
||||
#!/usr/bin/env bash
|
||||
# =====================================================================
|
||||
# BLOBBO — Deploy to partly.party/blobbo/
|
||||
# BLOBBO — Deploy to monsterrobot.games/games/blobbo/ (+ legacy partly.party)
|
||||
# =====================================================================
|
||||
# Usage: bash deploy.sh (from the BLOBBO repo root)
|
||||
#
|
||||
# 1. npm run build (vite, base=/blobbo/)
|
||||
# 2. rsync dist/ to VPS staging
|
||||
# 3. OVERLAY into forum-nginx web root (keep old chunks; prune >7d orphans)
|
||||
# 4. verify live index AND that its entry chunk serves as JavaScript
|
||||
# 1. npm run build (vite, RELATIVE base './' — one build works at any mount)
|
||||
# 2. rsync dist/ to VPS staging (for the docker-cp leg)
|
||||
# 3. publish:
|
||||
# · monsterrobot.games — rsync into the HOST bind-mount source (canonical,
|
||||
# persistent). The mount is READ-ONLY inside the container, so we write
|
||||
# the host dir, not `docker cp`.
|
||||
# · partly.party — best-effort `docker cp` into the container-internal
|
||||
# legacy dir (ephemeral; not the real home).
|
||||
# Both OVERLAY (no delete) so a cached index.html keeps finding its chunks;
|
||||
# only >7d-orphaned chunks are pruned.
|
||||
# 4. verify each live index AND that its entry chunk serves as JavaScript.
|
||||
#
|
||||
# Follows the GAMES/ deploy doctrine (see cratewars/deploy.sh template).
|
||||
# Requirements: ssh humanjing@100.71.119.27 (tailscale), container forum-nginx.
|
||||
# =====================================================================
|
||||
set -euo pipefail
|
||||
|
||||
VPS="humanjing@100.71.119.27"
|
||||
CONTAINER="forum-nginx"
|
||||
REMOTE_WEB_ROOT="/usr/share/nginx/html/blobbo"
|
||||
STAGING="/tmp/blobbo_deploy"
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
|
||||
# canonical: the arcade. HOST path (bind-mounted read-only into forum-nginx at
|
||||
# /usr/share/nginx/html/games-lander) — so we write the host dir directly.
|
||||
HOST_ROOT="/home/humanjing/monsterrobot.games/games/blobbo"
|
||||
SITE_CANON="https://monsterrobot.games/games/blobbo/"
|
||||
# legacy: container-internal (not a bind mount) — kept live as a courtesy.
|
||||
LEGACY_ROOT="/usr/share/nginx/html/blobbo"
|
||||
SITE_LEGACY="https://partly.party/blobbo/"
|
||||
|
||||
echo "[1/4] Building ..."
|
||||
cd "$SCRIPT_DIR"
|
||||
npm run build
|
||||
@ -27,8 +40,6 @@ npm run build
|
||||
# Guard: scripts/stage-farm-assets.sh copies ~43MB of raw farm GLBs into
|
||||
# public/ for local Workshop testing, and vite copies public/ straight into
|
||||
# dist/. Shipping those means every player downloads 43MB of background props.
|
||||
# Custom models belong in the player's browser (IndexedDB) or in a decimated
|
||||
# committed pack — never as an accident of local testing.
|
||||
DIST_KB="$(du -sk dist | cut -f1)"
|
||||
if [ "$DIST_KB" -gt 10240 ]; then
|
||||
echo " ✗ dist/ is ${DIST_KB}KB (>10MB). Staged farm assets leaked into the build?"
|
||||
@ -41,32 +52,40 @@ echo "[2/4] Syncing dist/ to VPS staging ..."
|
||||
ssh "$VPS" "rm -rf '$STAGING' && mkdir -p '$STAGING'"
|
||||
rsync -az --exclude='.DS_Store' "$SCRIPT_DIR/dist/" "$VPS:$STAGING/"
|
||||
|
||||
echo "[3/4] Overlaying container content (no wipe) ..."
|
||||
# OVERLAY, don't 'rm -rf': assets are content-hash-named, so a browser (or CDN)
|
||||
# holding a PREVIOUS index.html must still find the chunks it references, or the
|
||||
# page dies with "expected a JS module, got text/html" (the missing chunk falls
|
||||
# back to index.html). Copying over the top keeps old chunks alive as a grace
|
||||
# window. Vite rewrites every current chunk each build, so their mtime is fresh;
|
||||
# prune only files orphaned for >7 days so /assets can't grow without bound.
|
||||
# ponytail: 7-day window is plenty for a browser cache; widen if a CDN caches index longer.
|
||||
ssh "$VPS" "docker exec $CONTAINER sh -c 'mkdir -p ${REMOTE_WEB_ROOT:?}' \
|
||||
&& docker cp $STAGING/. $CONTAINER:$REMOTE_WEB_ROOT/ \
|
||||
&& docker exec $CONTAINER sh -c 'find ${REMOTE_WEB_ROOT}/assets -type f -mtime +7 -delete 2>/dev/null || true'"
|
||||
echo "[3/4] Publishing ..."
|
||||
# --- monsterrobot.games (canonical, host dir, rsync OVERLAY) ---
|
||||
# No --delete: a browser/CDN holding a previous index.html must still find the
|
||||
# content-hashed chunks it references, or the module loader chokes on the HTML
|
||||
# fallback. Vite rewrites current chunks each build (fresh mtime); prune only
|
||||
# files orphaned >7d so /assets can't grow without bound.
|
||||
echo " → monsterrobot.games (host $HOST_ROOT)"
|
||||
ssh "$VPS" "mkdir -p '$HOST_ROOT/assets'"
|
||||
rsync -az --exclude='.DS_Store' "$SCRIPT_DIR/dist/" "$VPS:$HOST_ROOT/"
|
||||
ssh "$VPS" "find '$HOST_ROOT/assets' -type f -mtime +7 -delete 2>/dev/null || true"
|
||||
|
||||
echo "[4/4] Verifying live index + entry chunk ..."
|
||||
# --- partly.party (legacy, container-internal, best-effort) ---
|
||||
echo " → partly.party (container $LEGACY_ROOT, best-effort)"
|
||||
ssh "$VPS" "docker exec $CONTAINER sh -c 'mkdir -p $LEGACY_ROOT' \
|
||||
&& docker cp $STAGING/. $CONTAINER:$LEGACY_ROOT/ \
|
||||
&& docker exec $CONTAINER sh -c 'find $LEGACY_ROOT/assets -type f -mtime +7 -delete 2>/dev/null || true'" \
|
||||
|| echo " (legacy partly.party copy failed — non-fatal; monsterrobot.games is canonical)"
|
||||
|
||||
echo "[4/4] Verifying each live index + entry chunk ..."
|
||||
BUST="$(date +%s)"
|
||||
CODE_LIVE="$(curl -s -o /dev/null -w '%{http_code}' "https://partly.party/blobbo/?v=$BUST")"
|
||||
echo " https://partly.party/blobbo/ -> HTTP $CODE_LIVE"
|
||||
[ "$CODE_LIVE" = "200" ] || { echo " ✗ live index check failed"; exit 1; }
|
||||
# The index-only check can't catch a broken chunk graph: a missing chunk is
|
||||
# served as the HTML fallback (200 text/html), which is exactly the bug that
|
||||
# bit us. Fetch the entry chunk the LIVE index references and confirm it comes
|
||||
# back as JavaScript — the check that would actually have caught it.
|
||||
MAIN="$(curl -s "https://partly.party/blobbo/?v=$BUST" | grep -oE '/blobbo/assets/main-[^"]+\.js' | head -1)"
|
||||
[ -n "$MAIN" ] || { echo " ✗ no main chunk found in live index"; exit 1; }
|
||||
CT="$(curl -s -o /dev/null -w '%{content_type}' "https://partly.party${MAIN}?v=$BUST")"
|
||||
case "$CT" in
|
||||
application/javascript*|text/javascript*) echo " entry chunk ${MAIN##*/} -> ${CT} ✓" ;;
|
||||
*) echo " ✗ entry chunk ${MAIN##*/} served as '${CT}', not JS — broken deploy"; exit 1 ;;
|
||||
esac
|
||||
echo " ✓ deployed."
|
||||
verify_site() {
|
||||
local url="$1" code main ct
|
||||
code="$(curl -s -o /dev/null -w '%{http_code}' "${url}?v=$BUST")"
|
||||
# relative-base index references chunks as ./assets/... — resolve against url
|
||||
main="$(curl -s "${url}?v=$BUST" | grep -oE '(\./|/)?assets/main-[^"]+\.js' | head -1)"
|
||||
if [ "$code" = "200" ] && [ -n "$main" ]; then
|
||||
ct="$(curl -s -o /dev/null -w '%{content_type}' "${url}${main#./}?v=$BUST")"
|
||||
case "$ct" in
|
||||
application/javascript*|text/javascript*) echo " ✓ ${url} (index 200, entry chunk ${ct})"; return 0 ;;
|
||||
*) echo " ✗ ${url} entry chunk served as '${ct}', not JS"; return 1 ;;
|
||||
esac
|
||||
fi
|
||||
echo " ✗ ${url} -> HTTP ${code}, main chunk '${main:-none}'"; return 1
|
||||
}
|
||||
verify_site "$SITE_CANON" || { echo " ✗ CANONICAL (monsterrobot.games) deploy failed"; exit 1; }
|
||||
verify_site "$SITE_LEGACY" || echo " (legacy partly.party check failed — non-fatal)"
|
||||
echo " ✓ deployed — monsterrobot.games/games/blobbo/ is live."
|
||||
|
||||
@ -2,7 +2,12 @@ import { defineConfig } from 'vite'
|
||||
import { resolve } from 'path'
|
||||
|
||||
export default defineConfig({
|
||||
base: '/blobbo/', // served from partly.party/blobbo/ (forum-nginx web root)
|
||||
// Relative base so ONE build works at any mount point: partly.party/blobbo/
|
||||
// AND monsterrobot.games/games/blobbo/ (the arcade's /games/<name>/ layout).
|
||||
// index.html + editor.html sit at the deploy root, so their assets resolve as
|
||||
// ./assets/... against the (always trailing-slash) page URL. assetUrl() reads
|
||||
// import.meta.env.BASE_URL='./' and resolves the manifest the same way.
|
||||
base: './',
|
||||
build: {
|
||||
target: 'es2022',
|
||||
rollupOptions: {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user