From fdc4583288d02d3c2a9648a01ac58d72086c85d3 Mon Sep 17 00:00:00 2001 From: Viktor Vaczi Date: Mon, 27 Jul 2026 15:08:56 +0200 Subject: [PATCH] chore(deploy/site): retire the migration scaffolding, keep the health check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Vercel -> Cloudflare Pages move is done and the Vercel project is deleted, so the one-shot scripts have no remaining purpose. Nothing in CI ever called them — deploy-site.yml runs npm ci / test / build / pages deploy inline — so this removes 10 files and orphans nothing. Deleted: 00-baseline (refused to run without x-vercel-id, so permanently unrunnable), 01-preflight (proved Vercel state and API-token scopes), 07-dns-cutover (the phased cutover; in the end the records were attached through the dashboard, and the rules/apex phases went unused once we chose APEX_MODE=serve), 09-detach-vercel (its target project is gone), plus 03-ensure-project, 04-set-secrets, 05-deploy, 06-verify-deploy, 02-verify-local and 99-rollback, all either spent or duplicating CI. Their lib/cf-api.sh went with them: the survivors use wrangler, so the whole remaining path needs only `wrangler login` and no zone scopes. What is kept is the part with ongoing value: lib/parity.sh, the assertion set that caught five real defects during the migration — the live COOP/COEP bug on the post's canonical URL, the soft-404 Pages would have introduced, the cross-site form-POST guard Vercel had been providing for free, the missing immutable header, and HSTS max-age=0. "Does the page return 200" catches none of those. 08-verify-prod.sh becomes verify.sh, since the numbered sequence it belonged to no longer exists. It drops the stamp machinery, the dry-run plumbing and the Vercel-fallback messaging (there is no fallback now: recovery is promoting a previous Pages deployment), and gains --skip-dns / --skip-domains so it can be pointed at a single deployment via PROD_BASE before promoting it. The README is rewritten around the four invariants that fail SILENTLY — never widen _headers to /*, keep both URL forms of the Gerber post, never delete 404.astro, keep the cross-site form-POST guard — each with the reason, since the reason is the only thing that stops someone simplifying them back out. Verified after: 21 probes, 20 pass, 1 warn (HSTS max-age is 6 months vs Vercel's 2 years — on, just shorter), 0 fail. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01LAmkjM7okPdScp9XLW1JVr --- .github/workflows/deploy-site.yml | 3 +- CLAUDE.md | 3 +- deploy/site/00-baseline.sh | 77 ------- deploy/site/01-preflight.sh | 120 ----------- deploy/site/02-verify-local.sh | 121 ----------- deploy/site/03-ensure-project.sh | 75 ------- deploy/site/04-set-secrets.sh | 104 --------- deploy/site/05-deploy.sh | 71 ------ deploy/site/06-verify-deploy.sh | 99 --------- deploy/site/07-dns-cutover.sh | 347 ------------------------------ deploy/site/08-verify-prod.sh | 99 --------- deploy/site/09-detach-vercel.sh | 96 --------- deploy/site/99-rollback.sh | 72 ------- deploy/site/README.md | 177 ++++++--------- deploy/site/lib/cf-api.sh | 95 -------- deploy/site/lib/common.sh | 131 ++--------- deploy/site/lib/parity.sh | 20 +- deploy/site/verify.sh | 118 ++++++++++ site/README.md | 3 +- site/wrangler.toml | 4 +- 20 files changed, 216 insertions(+), 1619 deletions(-) delete mode 100755 deploy/site/00-baseline.sh delete mode 100755 deploy/site/01-preflight.sh delete mode 100755 deploy/site/02-verify-local.sh delete mode 100755 deploy/site/03-ensure-project.sh delete mode 100755 deploy/site/04-set-secrets.sh delete mode 100755 deploy/site/05-deploy.sh delete mode 100755 deploy/site/06-verify-deploy.sh delete mode 100755 deploy/site/07-dns-cutover.sh delete mode 100755 deploy/site/08-verify-prod.sh delete mode 100755 deploy/site/09-detach-vercel.sh delete mode 100755 deploy/site/99-rollback.sh delete mode 100644 deploy/site/lib/cf-api.sh create mode 100755 deploy/site/verify.sh 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 "