www.pcbjam.com was the last piece of the stack on Vercel. It is now a
Cloudflare Pages project (pcbjam-site) deployed by deploy-site.yml on
every push to main touching site/** — content must not wait for a
release tag.
The Astro adapter is gone entirely: the build is pure static and the one
dynamic route, /api/waitlist, is a Pages Function. Going adapter-free
(rather than swapping in @astrojs/cloudflare, which has dropped Pages
support and only targets Workers) removes three problems at once — no
Astro/adapter major-version coupling, Footer.astro's build-time execSync
keeps working because prerendering stays in Node, and image optimisation
stays plain build-time sharp with no Cloudflare Images binding.
Verified against a real Pages runtime (wrangler pages dev): 21/21 parity
probes pass, versus 19/21 on live Vercel. The scripted runbook is in
deploy/site/ — every mutating step is dry-run by default.
Four behaviour differences were found by measurement and are handled here:
- The blog post's COOP/COEP was already broken in production. vercel.json
scoped the headers to the bare URL, but the page's own canonical is the
trailing-slash form, which served 200 with no isolation headers — so
search arrivals lost SharedArrayBuffer and the embedded Gerber viewer
degraded. public/_headers covers both forms.
- Pages answers unknown URLs with the homepage at HTTP 200 when the
output has no 404.html — a soft-404 that invites indexing junk URLs as
the homepage. Hence src/pages/404.astro.
- Vercel's edge refused cross-site form POSTs ("Cross-site POST form
submissions are forbidden"); Pages does not, and a cross-site <form>
submit needs no CORS permission to be sent, so the allowlist cannot
stop it. The Function reproduces the guard; JSON posts stay exempt as
that is demo.pcbjam.com's allowlisted path.
- Cache-Control: immutable on /_astro/* came from the Vercel adapter's
generated route config, so it is now an explicit _headers rule.
Secrets move to `wrangler pages secret put --project-name pcbjam-site`
(RESEND_API_KEY, RESEND_SEGMENT_ID, WAITLIST_FROM_EMAIL);
WAITLIST_ALLOWED_ORIGINS stays unset so the allowlist stays in code.
Local dev reads .dev.vars, now gitignored — the root repo's **/.dev.vars
does not cover a nested git repo.
privacy.md and cookies.md named Vercel as a GDPR Art. 28 processor; those
mentions are removed and the existing Cloudflare entry widened to cover
website hosting.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LAmkjM7okPdScp9XLW1JVr
72 lines
2.6 KiB
Shell
Executable file
72 lines
2.6 KiB
Shell
Executable file
#!/usr/bin/env bash
|
|
# One command, incident-grade: put www.pcbjam.com and the apex back on Vercel.
|
|
#
|
|
# deploy/site/99-rollback.sh # dry run — read it first
|
|
# deploy/site/99-rollback.sh --apply --yes
|
|
#
|
|
# Order matters: re-attach at Vercel FIRST so the target exists before DNS points
|
|
# at it, then restore DNS, then disable the redirect rule.
|
|
#
|
|
# Leaves the Pages project, its deployments and its secrets alone — re-cutting
|
|
# over later is just `07-dns-cutover.sh --phase swap --apply`.
|
|
set -euo pipefail
|
|
. "$(dirname "$0")/lib/common.sh"
|
|
. "$(dirname "$0")/lib/cf-api.sh"
|
|
|
|
require_cmd curl dig jq
|
|
SKIP_VERCEL=0
|
|
parse_common_flags "$@"
|
|
set -- $CFM_ARGS
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
--skip-vercel) SKIP_VERCEL=1; shift ;;
|
|
"") shift ;;
|
|
*) die "unknown arg: $1" ;;
|
|
esac
|
|
done
|
|
dry_banner
|
|
|
|
HERE="$(dirname "$0")"
|
|
FLAGS=""; [ "$DRY_RUN" = 0 ] && FLAGS="--apply"
|
|
|
|
section "1/3 re-attach the domains at Vercel"
|
|
if [ "$SKIP_VERCEL" = 1 ]; then
|
|
echo " skipped (--skip-vercel)"
|
|
elif [ -z "${VERCEL_TOKEN:-}" ]; then
|
|
warn "VERCEL_TOKEN unset — skipping the re-attach step."
|
|
warn "Do it by hand NOW (Vercel -> project $VERCEL_PROJECT -> Domains -> add"
|
|
warn "$ZONE_NAME and www.$ZONE_NAME) before the DNS change below propagates."
|
|
else
|
|
# shellcheck disable=SC2086
|
|
"$HERE/09-detach-vercel.sh" --rollback $FLAGS || warn "re-attach reported an error; check the Vercel dashboard"
|
|
fi
|
|
|
|
section "2/3 restore DNS + disable the redirect rule"
|
|
# shellcheck disable=SC2086
|
|
"$HERE/07-dns-cutover.sh" --rollback $FLAGS --yes
|
|
|
|
if [ "$DRY_RUN" = 1 ]; then
|
|
echo; echo "done: (dry run) nothing changed. Re-run with --apply --yes to roll back."
|
|
exit 0
|
|
fi
|
|
|
|
section "3/3 waiting for Vercel to serve again (www ttl is 60)"
|
|
ok=1
|
|
for i in $(seq 1 60); do
|
|
h="$(curl -sS -o /dev/null -D - "$PROD_BASE/" 2>/dev/null | tr -d '\r' || true)"
|
|
st="$(printf '%s' "$h" | awk '/^HTTP/{c=$2} END{print c}')"
|
|
vid="$(printf '%s' "$h" | awk 'tolower($1)=="x-vercel-id:"{print $2}' | tail -1)"
|
|
echo " [${i}] status=$st x-vercel-id=${vid:-none} cname=$(dig +short CNAME "www.$ZONE_NAME" | head -1)"
|
|
if [ "$st" = "200" ] && [ -n "$vid" ]; then ok=0; break; fi
|
|
sleep 5
|
|
done
|
|
|
|
section "done"
|
|
if [ "$ok" = 0 ]; then
|
|
echo "www.$ZONE_NAME is served by Vercel again."
|
|
else
|
|
echo "www.$ZONE_NAME is NOT confirmably back on Vercel yet."
|
|
echo "Check: the domains are attached in Vercel, and dig www.$ZONE_NAME."
|
|
fi
|
|
echo "The Pages project is untouched; re-cutover is 07-dns-cutover.sh --phase swap --apply"
|
|
echo "done: rollback complete (re-baseline with 00-baseline.sh --force if you want a fresh reference)"
|