feat(deploy): repeatable Cloudflare deploy script + make targets

deploy/cloudflare/deploy.sh (build → optional parallel R2 media sync → Pages
deploy); 'make deploy' (frontend-only) / 'make deploy-media'. One-time setup
stays documented in README. Guards against unauth + mis-keyed R2 uploads.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
BenStullsBets
2026-06-30 08:56:30 -07:00
parent 7da944af0c
commit a68d0bc4df
4 changed files with 111 additions and 1 deletions
+60
View File
@@ -0,0 +1,60 @@
#!/usr/bin/env bash
# Repeatable deploy of the HEF simulator to benstull.art (Cloudflare Pages + R2).
#
# deploy/cloudflare/deploy.sh # frontend only: build + Pages deploy (fast)
# deploy/cloudflare/deploy.sh --media # also re-sync all media to R2 (~2 min, parallel)
# deploy/cloudflare/deploy.sh --media-only # only the R2 media sync, no Pages deploy
#
# Assumes the ONE-TIME setup is already done (see README.md): wrangler logged in,
# the R2 bucket + CORS + static.benstull.art custom domain, and the Pages project +
# benstull.art custom domain. This script only does the repeatable part.
#
# Override any of these via env: BUCKET, PROJECT, MEDIA_BASE, APP_PATH, BRANCH, JOBS.
set -euo pipefail
BUCKET="${BUCKET:-human-experience-simulator-media}"
PROJECT="${PROJECT:-human-experience-simulator}"
MEDIA_BASE="${MEDIA_BASE:-https://static.benstull.art/}"
APP_PATH="${APP_PATH:-/human-experience-simulator}"
BRANCH="${BRANCH:-main}"
JOBS="${JOBS:-10}"
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
OUT="$REPO_ROOT/dist"
export MEDIA_OUT="$REPO_ROOT/dist-media"
PY="${PY:-$REPO_ROOT/.venv/bin/python}"
export WRANGLER="${WRANGLER:-$(command -v wrangler || echo "$HOME/.npm-global/bin/wrangler")}"
do_media=0
do_pages=1
case "${1:-}" in
--media) do_media=1 ;;
--media-only) do_media=1; do_pages=0 ;;
"") ;;
*) echo "unknown flag: $1 (use --media or --media-only)"; exit 2 ;;
esac
[ -x "$WRANGLER" ] || { command -v "$WRANGLER" >/dev/null || { echo "wrangler not found ($WRANGLER) — npm i -g wrangler"; exit 1; }; }
"$WRANGLER" whoami >/dev/null 2>&1 || { echo "not authenticated — run: $WRANGLER login"; exit 1; }
echo "▶ build ($MEDIA_BASE)"
cd "$REPO_ROOT"
"$PY" -m tools.build_static --out "$OUT" --media-out "$MEDIA_OUT" \
--media-base "$MEDIA_BASE" --app-path "$APP_PATH" | tail -1
if [ "$do_media" = 1 ]; then
echo "▶ R2 media sync → $BUCKET (parallel x$JOBS, no bulk-sync exists)"
export BUCKET
fails=$(find "$MEDIA_OUT" -type f -print0 \
| xargs -0 -P "$JOBS" -I {} bash "$REPO_ROOT/deploy/cloudflare/r2-put-one.sh" {} \
| tee /dev/stderr | grep -c '^FAIL ' || true)
[ "$fails" = 0 ] || { echo "$fails media uploads failed"; exit 1; }
echo "✔ media synced"
fi
if [ "$do_pages" = 1 ]; then
echo "▶ Pages deploy → $PROJECT"
"$WRANGLER" pages deploy "$OUT" --project-name "$PROJECT" --branch "$BRANCH" --commit-dirty=true | tail -3
fi
echo "✔ done — https://benstull.art$APP_PATH/"