← back to Homesonspec

finish-tk10.sh

191 lines

#!/usr/bin/env bash
# ============================================================================
# finish-tk10.sh — one-command HomesOnSpec.com production go-live finisher
# ============================================================================
# TK-10 turnkey driver. Steve does exactly ONE manual thing first (the GoDaddy
# nameserver swap — GoDaddy's write-API is 404-gated for this domain, so it
# CANNOT be automated), then runs this ONE script to finish the go-live.
#
#   DO THIS ONE THING FIRST (GoDaddy dashboard, homesonspec.com → Nameservers):
#     change from  ns69.domaincontrol.com + ns70.domaincontrol.com
#             to   ezra.ns.cloudflare.com  + kimora.ns.cloudflare.com
#   THEN run:   bash ~/Projects/homesonspec/finish-tk10.sh
#
# WHAT THIS SCRIPT DOES (in order):
#   (a) GATE — verifies `dig NS homesonspec.com` actually shows the two
#       Cloudflare nameservers. If not (swap not done / not propagated yet),
#       it PRINTS the exact GoDaddy step and EXITS 2. Nothing else runs.
#   (b) CLOUDFLARE — runs cutover-dns.sh (API-driven + reversible) to ensure
#       the CF zone / proxied A / email records / SSL=full are all in place.
#       (Idempotent — a no-op if everything is already set.)
#   (c) DEPLOY + SMOKE — runs the project's deploy script (Kamatera) and then
#       HTTP-smoke-tests the apex + admin.
#   (d) REVOKE — PRINTS (does NOT auto-run) the exact old-GoDaddy-key
#       revocation step for Steve to do by hand.
#
# ── REQUIREMENTS ────────────────────────────────────────────────────────────
#   * The DEPLOY step (c) SSHes to Kamatera (root@45.61.58.125). Run this from
#     a Kamatera-SSH-CAPABLE session (a plain interactive shell on Mac2 — NOT a
#     sandboxed agent/workflow session, whose SSH is blocked). If SSH is not
#     available, pass --skip-deploy to run only the Cloudflare + gate steps and
#     deploy by hand.
#   * Reads Cloudflare + GoDaddy creds from ~/Projects/secrets-manager/.env
#     (the same source cutover-dns.sh uses). No secrets live in this file.
#
# ── PATH NOTE (pre-existing) ────────────────────────────────────────────────
#   The gitignored deploy scripts (deploy-kamatera.sh / golive-homesonspec.sh)
#   still reference the OLD source path `~/Projects/spechomes` and remote
#   `/root/Projects/spechomes` internally (project was renamed to homesonspec).
#   That is intentional-for-now: the Kamatera remote dir is still
#   /root/Projects/spechomes and the local rsync source `~/Projects/spechomes`
#   must exist for deploy-kamatera.sh. This driver runs the scripts AS-IS from
#   ~/Projects/homesonspec (where they physically live). If ~/Projects/spechomes
#   no longer exists, choose --relaunch (golive-homesonspec.sh, which also uses
#   the spechomes source path) or fix the paths in the deploy script first.
#
# ── FLAGS ───────────────────────────────────────────────────────────────────
#   --relaunch      use golive-homesonspec.sh (demo-purge relaunch on an
#                   already-seeded prod DB) instead of deploy-kamatera.sh
#                   (first-time full stand-up). Default: first-time stand-up.
#   --skip-deploy   run gate + Cloudflare + revoke-notice only; skip step (c).
#   --skip-cf       skip step (b) (assume CF already fully cut over).
#   -h | --help     show this header.
# ============================================================================
set -euo pipefail

DOMAIN=homesonspec.com
ORIGIN_IP=45.61.58.125
WEB_PORT=9975
ADMIN_PORT=9976
CF_NS1=ezra.ns.cloudflare.com
CF_NS2=kimora.ns.cloudflare.com
PROJ_DIR="$HOME/Projects/homesonspec"
CUTOVER="$PROJ_DIR/cutover-dns.sh"
DEPLOY="$PROJ_DIR/deploy-kamatera.sh"
RELAUNCH_SCRIPT="$PROJ_DIR/golive-homesonspec.sh"

RELAUNCH=0
SKIP_DEPLOY=0
SKIP_CF=0
for arg in "$@"; do
  case "$arg" in
    --relaunch)    RELAUNCH=1 ;;
    --skip-deploy) SKIP_DEPLOY=1 ;;
    --skip-cf)     SKIP_CF=1 ;;
    -h|--help)     sed -n '2,60p' "$0"; exit 0 ;;
    *) echo "unknown flag: $arg (see --help)"; exit 64 ;;
  esac
done

hr() { printf '────────────────────────────────────────────────────────────\n'; }
say() { printf '\n\033[1m%s\033[0m\n' "$*"; }

# ── (a) GATE on nameserver propagation ──────────────────────────────────────
say "(a) GATE — checking homesonspec.com nameservers"
hr
CUR_NS=$(dig +short NS "$DOMAIN" | sed 's/\.$//' | sort | tr '\n' ' ' | sed 's/ *$//')
echo "  dig NS $DOMAIN → ${CUR_NS:-<empty>}"
if echo "$CUR_NS" | grep -qi "$CF_NS1" && echo "$CUR_NS" | grep -qi "$CF_NS2"; then
  echo "  ✓ Cloudflare nameservers are live — the GoDaddy swap is done. Continuing."
else
  cat <<GATE

  ✗ NOT YET on Cloudflare — the go-live is blocked on ONE manual step.

    ┌──────────────────────────────────────────────────────────────────┐
    │  DO THIS ONE THING (GoDaddy dashboard):                           │
    │                                                                   │
    │  homesonspec.com  →  Nameservers  →  Change  →  Enter my own      │
    │      set:   $CF_NS1
    │             $CF_NS2
    │      (was:  ns69.domaincontrol.com + ns70.domaincontrol.com)      │
    │                                                                   │
    │  Then re-run:  bash ~/Projects/homesonspec/finish-tk10.sh         │
    └──────────────────────────────────────────────────────────────────┘

  (GoDaddy's NS-swap write API returns 404 for this domain, so this step
   cannot be automated. Propagation after you save takes ~5–30 min — if the
   dashboard already shows the CF nameservers, just wait a few minutes and
   re-run this script.)
GATE
  exit 2
fi

# ── (b) CLOUDFLARE half (API-driven, reversible, idempotent) ────────────────
if [ "$SKIP_CF" -eq 1 ]; then
  say "(b) CLOUDFLARE — skipped (--skip-cf)"
else
  say "(b) CLOUDFLARE — ensuring zone / proxied A / email / SSL (cutover-dns.sh)"
  hr
  if [ ! -f "$CUTOVER" ]; then
    echo "  ✗ $CUTOVER not found — cannot run the Cloudflare half. ABORT."; exit 1
  fi
  # cutover-dns.sh is idempotent: upserts records, sets SSL=full/always_use_https,
  # re-writes ROLLBACK-DNS.txt, and (harmlessly) no-ops on the 404-gated GoDaddy PUT.
  bash "$CUTOVER" || {
    echo "  ⚠ cutover-dns.sh returned non-zero (the 404-gated GoDaddy NS PUT is expected"
    echo "    to fail; the Cloudflare record/SSL upserts above it are what matter). Review"
    echo "    the output above — if only the GoDaddy PUT failed, this is fine."; }
fi

# ── (c) DEPLOY + smoke test ─────────────────────────────────────────────────
if [ "$SKIP_DEPLOY" -eq 1 ]; then
  say "(c) DEPLOY — skipped (--skip-deploy)"
else
  if [ "$RELAUNCH" -eq 1 ]; then
    DEPLOY_SCRIPT="$RELAUNCH_SCRIPT"; DEPLOY_KIND="re-launch (demo-purge on seeded prod DB)"
  else
    DEPLOY_SCRIPT="$DEPLOY";          DEPLOY_KIND="first-time full stand-up"
  fi
  say "(c) DEPLOY — $DEPLOY_KIND → Kamatera ($ORIGIN_IP)"
  hr
  if [ ! -f "$DEPLOY_SCRIPT" ]; then
    echo "  ✗ $DEPLOY_SCRIPT not found. ABORT."; exit 1
  fi
  echo "  running: bash $DEPLOY_SCRIPT"
  echo "  (SSHes to root@$ORIGIN_IP — requires a Kamatera-SSH-capable session)"
  bash "$DEPLOY_SCRIPT"

  say "    smoke test"
  hr
  # Apex through Cloudflare edge (proxied). Allow a few tries for edge warm-up.
  APEX=000
  for i in 1 2 3 4 5; do
    APEX=$(curl -s -o /dev/null -w '%{http_code}' --max-time 15 "https://$DOMAIN/" || echo 000)
    [ "$APEX" = "200" ] && break
    echo "  apex try $i → $APEX (retrying in 5s)"; sleep 5
  done
  echo -n "  apex   https://$DOMAIN/ → $APEX"; [ "$APEX" = "200" ] && echo "  ✓" || echo "  ✗ (edge/cert may still be warming — recheck in a few min)"

  # Admin should fail closed (401) unauthenticated. Admin is origin-only (not in
  # public DNS), so probe the origin directly via the Host header.
  ADMINCODE=$(curl -s -o /dev/null -w '%{http_code}' --max-time 15 "http://$ORIGIN_IP:$ADMIN_PORT/" || echo 000)
  echo -n "  admin  origin :$ADMIN_PORT (401 expected, fails closed) → $ADMINCODE"
  [ "$ADMINCODE" = "401" ] && echo "  ✓" || echo "  — (verify: unauthenticated admin must be 401)"
fi

# ── (d) REVOKE the old GoDaddy key (PRINT ONLY — Steve runs this by hand) ────
say "(d) FINAL MANUAL STEP — revoke the old GoDaddy sso-key"
hr
cat <<'REVOKE'
  The leaked-then-scrubbed GoDaddy sso-key is git-clean but STILL LIVE at
  GoDaddy until you revoke it. Every live consumer already prefers the new
  Bearer PAT (verified under TK-10), so revoking is safe.

  DO THIS (by hand — NOT automated):
    1. Go to  https://developer.godaddy.com/keys
    2. REVOKE the old key  (fleet-wide key, sha-prefix 96974d0c).
    3. If you also created a fresh key, run:  /secrets
       to fan it across all .env / MCP / skill envs.
    4. Delete the pre-scrub backup that still holds old secrets:
         rm -rf ~/Projects/spechomes.pre-scrub-backup

  ROLLBACK (if the launch needs undoing):
    GoDaddy dashboard → homesonspec.com → Nameservers → set back to
    ns69.domaincontrol.com + ns70.domaincontrol.com (manual — the API PUT is
    404-gated). If a demo-purge relaunch ran, restore the pg_dump the
    golive script printed.
REVOKE

say "✅ finish-tk10.sh complete — homesonspec.com is live once the smoke checks are green."