#!/usr/bin/env bash # Deploy Molly Cool to monsterrobot.games/mollycool # # The lander lives on the botchat/games VPS and forum-nginx serves it from a # bind mount of /home/humanjing/monsterrobot.games. That mount is READ-ONLY # inside the container, so `docker cp` fails silently — always write the HOST # path. (Same trap as beyondmorp V2.) # # monsterrobot.games -> /home/humanjing/monsterrobot.games (nginx root) # monsterrobot.games/mollycool -> games/mollycool via a root symlink # # nginx needs no config change: the server block's `location /` try_files # resolves the symlink, which is the same convention not-tonight uses. set -euo pipefail HOST="humanjing@100.71.119.27" LANDER="/home/humanjing/monsterrobot.games" DEST="$LANDER/games/mollycool" URL="https://monsterrobot.games/mollycool/" SRC="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" echo "==> shipping $SRC -> $HOST:$DEST" # Static game only. serve.py is the dev server, test/ is the headless suite, # and neither belongs on a public web root. rsync -az --delete \ --include='index.html' \ --include='src/' --include='src/**' \ --exclude='*' \ "$SRC/" "$HOST:$DEST/" echo "==> ensuring the /mollycool route" ssh "$HOST" "ln -sfn games/mollycool '$LANDER/mollycool' && ls -ld '$LANDER/mollycool'" # ---- Cloudflare purge -------------------------------------------------- # CF fronts monsterrobot.games and serves the modules with max-age=14400, so # without this a redeploy leaves you running four-hour-old code — the exact # bug the dev server's /v/ prefix exists to prevent, just moved # upstream. Token is read from disk at run time and never printed. if [ -z "${CF_API_TOKEN:-}" ] && [ -f "$SRC/deploy/.env" ]; then # shellcheck disable=SC1091 set +u; . "$SRC/deploy/.env"; set -u CF_API_TOKEN="${CF_API_TOKEN:-${CLOUDFLARE_API_TOKEN:-}}" fi if [ -n "${CF_API_TOKEN:-}" ]; then echo "==> purging cloudflare" API=https://api.cloudflare.com/client/v4 ZONE=$(curl -s -H "Authorization: Bearer $CF_API_TOKEN" \ "$API/zones?name=monsterrobot.games" | sed -n 's/.*"id":"\([a-f0-9]\{32\}\)".*/\1/p' | head -1) if [ -n "$ZONE" ]; then files=$(cd "$SRC" && find index.html src -type f \ | sed "s|^|\"https://monsterrobot.games/mollycool/|; s|$|\"|" | paste -sd, -) ok=$(curl -s -X POST "$API/zones/$ZONE/purge_cache" \ -H "Authorization: Bearer $CF_API_TOKEN" -H 'Content-Type: application/json' \ --data "{\"files\":[$files]}" | grep -o '"success":true' || true) [ -n "$ok" ] && echo " purged" || echo " !! purge failed — hard-reload to be sure" else echo " !! could not resolve zone id — hard-reload to be sure" fi else echo "==> NO CF_API_TOKEN — skipping purge." echo " Cloudflare holds the old JS for up to 4h. Hard-reload (cmd+shift+R)," echo " or put CF_API_TOKEN in deploy/.env so this is automatic." fi echo "==> verifying live" STAMP="$(date +%s)" code=$(curl -s -o /dev/null -w '%{http_code}' "$URL?cb=$STAMP") title=$(curl -s "$URL?cb=$STAMP" | grep -o '[^<]*' || true) # The real test is the module: a broken subpath serves the LANDER's html here # with content-type text/html, which the browser silently refuses to execute. mime=$(curl -s -o /dev/null -w '%{content_type}' "${URL}src/main.js?cb=$STAMP") mcode=$(curl -s -o /dev/null -w '%{http_code}' "${URL}src/main.js?cb=$STAMP") echo " page $code $title" echo " module $mcode $mime" if [ "$code" != "200" ] || [ "$mcode" != "200" ]; then echo "!! NOT DEPLOYED (non-200)"; exit 1 fi case "$title" in *"Molly Cool"*) ;; *) echo "!! wrong page served — check the symlink"; exit 1;; esac case "$mime" in *javascript*) ;; *) echo "!! module served as '$mime', not javascript"; exit 1;; esac echo "==> OK $URL"