diff --git a/deploy/sync-static.sh b/deploy/sync-static.sh index c43f397..25e82bf 100755 --- a/deploy/sync-static.sh +++ b/deploy/sync-static.sh @@ -1,35 +1,51 @@ #!/usr/bin/env bash # Sync the built static site to the live root in an order that avoids deploy-race -# blank screens. rsync isn't atomic, so a naïve `rsync --delete` can briefly serve -# a NEW index.html that points at chunks not synced yet (→ failed load), or delete -# old chunks an in-flight client still needs. Instead: +# blank screens AND post-deploy slow loads. rsync isn't atomic, so a naïve +# `rsync --delete` can briefly serve a NEW index.html that points at chunks not +# synced yet (→ failed load), or delete old chunks an in-flight client still +# needs. And even once synced, a new shell can point at chunks that aren't warm +# at the CDN edge yet, so the unlucky first visitor fetches them cold from the +# (residential) origin — the post-deploy slow-load window. So the order is: # 1. new hashed chunks first, and DON'T prune old ones (grace window) -# 2. other static assets (version.json, env.js, icons…), pruning removed files -# 3. the shell HTML — only once its chunks already exist -# 4. the service worker last — a returning client adopts it only after the rest +# 2. WARM those chunks at the edge BEFORE the shell goes live, so a new shell +# never references cold chunks +# 3. other static assets (version.json, icons…), pruning removed files +# 4. the shell HTML — only once its chunks exist AND are warm +# 5. the service worker last — a returning client adopts it only after the rest +# 6. warm the shell + key routes after publish # Old immutable chunks are pruned after a grace window to bound disk growth. +# NOTE: warming runs from this host, so it primes the Cloudflare POP nearest the +# origin (great for local/nearby users + our own post-deploy testing); a distant +# POP still cold-fills once on its first hit — inherent to a residential origin. set -euo pipefail src="$1"; site="$2" +base="https://upbeatbytes.com" +# Warm a newline-separated list of paths (on stdin) through the public domain. +# Best-effort: a warm miss must never fail the deploy. +warm() { xargs -P 8 -I{} curl -fsS -o /dev/null --max-time 20 "$base{}" 2>/dev/null || true; } + +# 1. New hashed chunks first (old ones kept — 14-day grace for in-flight clients). rsync -a "$src/_app/immutable/" "$site/_app/immutable/" + +# 2. Warm the chunks BEFORE publishing the shell, so the new shell never points +# at chunks still cold at the edge (the post-deploy slow-load cause). +echo " warming new chunks (before publish)…" +find "$site/_app/immutable" -type f \( -name '*.js' -o -name '*.css' \) -printf '/_app/immutable/%P\n' | warm + +# 3. Other static assets (prune removed files), then 4. the shell, then 5. the SW. rsync -a --delete \ --exclude='_app/immutable/***' --exclude='index.html' --exclude='service-worker.js' \ "$src/" "$site/" rsync -a "$src/index.html" "$site/index.html" rsync -a "$src/service-worker.js" "$site/service-worker.js" -find "$site/_app/immutable" -type f -mtime +14 -delete 2>/dev/null || true -# Warm the Cloudflare edge cache: fetch every immutable asset through the public -# domain so the FIRST real visitor after a deploy gets cache HITs instead of slow -# cold fetches from the (residential) origin — the post-deploy blank/slow-load cause. -echo " warming edge cache…" -base="https://upbeatbytes.com" -{ - # every immutable chunk/asset (a superset of what index.html boots from) - find "$site/_app/immutable" -type f \( -name '*.js' -o -name '*.css' \) -printf '/_app/immutable/%P\n' - # shell + key routes + SW + version + static assets (primes CF↔origin even where - # no-cache; caches the cacheable ones) - printf '%s\n' / /play /account /admin /service-worker.js /_app/version.json \ - /manifest.webmanifest /words-5.json /words-6.json /logo.svg /favicon.svg \ - /icon-192.png /icon-512.png /fonts/inter-latin-wght-normal.woff2 -} | xargs -P 8 -I{} curl -fsS -o /dev/null --max-time 20 "$base{}" 2>/dev/null || true +# 6. Warm the shell + key routes + remaining static now that they're published +# (primes CF↔origin even where no-cache; caches the cacheable shell/routes). +echo " warming shell + routes (after publish)…" +printf '%s\n' / /play /account /admin /service-worker.js /_app/version.json \ + /manifest.webmanifest /words-5.json /words-6.json /logo.svg /favicon.svg \ + /icon-192.png /icon-512.png /fonts/inter-latin-wght-normal.woff2 | warm + +# Bound disk growth: prune immutable chunks older than the grace window. +find "$site/_app/immutable" -type f -mtime +14 -delete 2>/dev/null || true