diff --git a/.github/workflows/deploy-site.yml b/.github/workflows/deploy-site.yml index d42e008..6fdf4e5 100644 --- a/.github/workflows/deploy-site.yml +++ b/.github/workflows/deploy-site.yml @@ -19,7 +19,8 @@ name: deploy-site (marketing site) # from site/functions/. Its secrets (RESEND_API_KEY, RESEND_SEGMENT_ID, # WAITLIST_FROM_EMAIL) are NOT deploy inputs: set once with # `wrangler pages secret put --project-name pcbjam-site`. -# See deploy/site/README.md for the full runbook. +# deploy/site/README.md documents the setup, the invariants that fail silently, +# and deploy/site/verify.sh (a fuller health check than step 5 below). # # Secrets (Settings → Secrets → Actions) — already present for demo/editor: # CLOUDFLARE_API_TOKEN Cloudflare Pages:Edit diff --git a/CLAUDE.md b/CLAUDE.md index 74b66e8..8c96da3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -39,4 +39,5 @@ from; because it pins the kicad + wxwidgets submodule revisions implicitly, it i our GPLv3 corresponding-source pointer (see /licenses). It resolves automatically at build time in site/src/components/Footer.astro (CF_PAGES_COMMIT_SHA / GITHUB_SHA in CI, `git rev-parse` locally) — no manual bump needed. -The Cloudflare setup + cutover runbook is in pcbjam/deploy/site/README.md. +Cloudflare setup, the invariants that fail silently, and the health check +(deploy/site/verify.sh) are documented in pcbjam/deploy/site/README.md. diff --git a/deploy/site/00-baseline.sh b/deploy/site/00-baseline.sh deleted file mode 100755 index 171fc9b..0000000 --- a/deploy/site/00-baseline.sh +++ /dev/null @@ -1,77 +0,0 @@ -#!/usr/bin/env bash -# Freeze the LIVE Vercel behaviour as the reference every later step is compared -# against. Read-only; needs no Cloudflare credentials. Run this while Vercel is -# still serving — you cannot re-create it afterwards. -# -# deploy/site/00-baseline.sh [--force] -# -# Two probes are EXPECTED to fail here: post_coi (the blog post's COOP/COEP) is -# genuinely broken in production today — the page's canonical is the -# trailing-slash URL and that URL serves 200 with no isolation headers. Recording -# it is the point: 08-verify-prod.sh asserts those same probes PASS afterwards, -# so the migration proves it fixed the bug rather than porting it. -set -euo pipefail -. "$(dirname "$0")/lib/common.sh" -. "$(dirname "$0")/lib/parity.sh" - -require_cmd curl dig awk sed jq - -OUT="$STATE_DIR/baseline/vercel" -if [ -d "$OUT" ] && [ "${1:-}" != "--force" ]; then - die "baseline already exists at $OUT — refusing to overwrite (use --force). - Re-baselining after cutover would silently replace the reference." -fi -mkdir -p "$OUT" - -section "confirming $PROD_BASE is still served by Vercel" -hdrs="$(_headers "$PROD_BASE/")" -if [ -z "$(_hdr "$hdrs" x-vercel-id)" ]; then - die "no x-vercel-id header on $PROD_BASE — this host is not on Vercel any more. - Baselining a Cloudflare response as 'the Vercel reference' would be useless." -fi -echo "ok: x-vercel-id present" - -section "DNS + SOA snapshot" -{ - echo "# captured $(date -u +%Y-%m-%dT%H:%M:%SZ)" - echo "apex_cname=$(dig +short CNAME "$ZONE_NAME" || true)" - echo "www_cname=$(dig +short CNAME "www.$ZONE_NAME" || true)" - echo "ns=$(dig +short NS "$ZONE_NAME" | sort | tr '\n' ' ')" - # The SOA minimum is the NEGATIVE cache TTL. It is the number that makes - # delete-then-create dangerous: a resolver that asks while the record is gone - # caches NODATA for this long, and you cannot flush it. - echo "soa=$(dig +short SOA "$ZONE_NAME" || true)" - echo "soa_minimum_ttl=$(dig +short SOA "$ZONE_NAME" | awk '{print $NF}')" -} | tee "$OUT/dns.txt" - -section "page titles (used to detect a soft-404 later)" -printf 'home_title=%s\n' "$(_title "$PROD_BASE/")" | tee "$OUT/titles.txt" - -section "header snapshots" -for p in / /pricing /blog /privacy /blog/porting-kicad-graphics-to-webgl-in-2026 /gerber-demo/boot.js; do - f="$(printf '%s' "$p" | sed 's|/|_|g')"; [ "$f" = "_" ] && f="_home" - t="$(_trace "$PROD_BASE$p")"; eff="$(printf '%s' "$t" | cut -f1)" - { - echo "# requested: $PROD_BASE$p" - echo "# effective: $eff (hops $(printf '%s' "$t" | cut -f3))" - # Drop volatile headers so a later diff shows real changes, not timestamps. - _headers "$eff" | grep -vE '^(date|age|etag|last-modified|content-length|server|cf-ray|cf-cache-status|nel|report-to|alt-svc|set-cookie|x-vercel-id|x-vercel-cache|x-matched-path|expires|via):' | sort - } > "$OUT/$f.headers" - echo " $p -> $OUT/$f.headers" -done - -rc=0 -assert_parity "$PROD_BASE" --scope prod || rc=$? -assert_apex_redirect "$APEX_BASE" || rc=$? - -{ - echo "# baseline captured $(date -u +%Y-%m-%dT%H:%M:%SZ) against $PROD_BASE (Vercel)" - echo "# parity exit code: $rc (non-zero is EXPECTED — see the header of this script)" -} > "$OUT/parity-exit.txt" - -section "done" -echo "Baseline written to $OUT" -echo -echo "Read the FAIL rows above and keep them: they are the 'before' half of the" -echo "COOP/COEP fix. 08-verify-prod.sh requires those same probes to pass." -echo "done: file://$OUT" diff --git a/deploy/site/01-preflight.sh b/deploy/site/01-preflight.sh deleted file mode 100755 index d4fc6e7..0000000 --- a/deploy/site/01-preflight.sh +++ /dev/null @@ -1,120 +0,0 @@ -#!/usr/bin/env bash -# Prove every credential and token scope the later steps need, and snapshot DNS -# for rollback. Read-only — makes no changes anywhere. -# -# export CLOUDFLARE_API_TOKEN=... CLOUDFLARE_ACCOUNT_ID=... -# deploy/site/01-preflight.sh -# -# Re-run this freely; it is the script to come back to after fixing a scope or -# logging wrangler in. -set -euo pipefail -. "$(dirname "$0")/lib/common.sh" -. "$(dirname "$0")/lib/cf-api.sh" - -require_cmd curl dig jq awk node npm npx shasum - -ok=0; bad=0 -chk() { # chk "