[object Object]

← back to Nationalrealestate

scripts: safe usre contractor code deploy (backup+smoke+rollback; scoped cred via runtime env) (TK-10488)

095c0c2e6b40bbc3ddd7c8fa86ce3b6e40b21cac · 2026-08-12 11:55:00 -0700 · Steve Abrams

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Files touched

Diff

commit 095c0c2e6b40bbc3ddd7c8fa86ce3b6e40b21cac
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Wed Aug 12 11:55:00 2026 -0700

    scripts: safe usre contractor code deploy (backup+smoke+rollback; scoped cred via runtime env) (TK-10488)
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
 scripts/deploy-usre-code-to-kamatera.sh | 87 +++++++++++++++++++++++++++++++++
 1 file changed, 87 insertions(+)

diff --git a/scripts/deploy-usre-code-to-kamatera.sh b/scripts/deploy-usre-code-to-kamatera.sh
new file mode 100644
index 0000000..d65362a
--- /dev/null
+++ b/scripts/deploy-usre-code-to-kamatera.sh
@@ -0,0 +1,87 @@
+#!/usr/bin/env bash
+# TK-10488 Phase 2a — deploy the usre contractor CODE to Kamatera + enable the scoped
+# contractor credential, SAFELY. RUN ON MAC2. Backs up every file it replaces, smoke-tests
+# BOTH the new /api/contractors route (must 200) AND an existing usre route (must stay 200),
+# and AUTO-ROLLBACKS if anything regresses. Deploys ONLY the 4 contractor files — never the
+# whole repo — so it can't revert unrelated prod code.
+set -euo pipefail
+KAM=root@45.61.58.125
+USRE_DIR=/root/public-projects/nationalrealestate
+FILES=(src/server/contractors.ts src/server/index.ts public/contractors.html public/nav-drawer.js)
+# scoped credential the builds will use (reaches ONLY /contractors* + /api/contractors).
+# NEVER hardcode the secret — pass it at runtime:  CONTRACTORS_PASS=... bash <this script>
+CU="${CONTRACTORS_USER:-contractors}"
+CP="${CONTRACTORS_PASS:?set CONTRACTORS_PASS=... in the environment when running (do not hardcode a secret in the repo)}"
+STAMP=$(cd ~/Projects/nationalrealestate && git rev-parse --short HEAD)
+
+echo "### pre-flight (Mac2) ###"
+cd ~/Projects/nationalrealestate
+for f in "${FILES[@]}"; do [ -f "$f" ] || { echo "MISSING $f"; exit 1; }; done
+
+echo "### 1. ship files to a staging dir on Kamatera ###"
+ssh "$KAM" "mkdir -p /tmp/usre-deploy-$STAMP/src/server /tmp/usre-deploy-$STAMP/public"
+scp -q src/server/contractors.ts src/server/index.ts "$KAM:/tmp/usre-deploy-$STAMP/src/server/"
+scp -q public/contractors.html public/nav-drawer.js  "$KAM:/tmp/usre-deploy-$STAMP/public/"
+
+echo "### 2-5. backup, deploy, enable scoped cred, restart, smoke, rollback-on-fail (Kamatera) ###"
+ssh "$KAM" "bash -s" <<REMOTE
+set -uo pipefail
+cd "$USRE_DIR" || { echo "usre dir missing"; exit 1; }
+PORT=\$(grep -E '^PORT=' .env 2>/dev/null | cut -d= -f2 | tr -d '"'"'"' ); PORT=\${PORT:-9913}
+BK="/root/backups/usre-contractors-$STAMP"; mkdir -p "\$BK/src/server" "\$BK/public"
+echo "backup dir: \$BK  (port \$PORT)"
+
+# --- backup current versions of the files we'll replace (contractors.ts is new -> may not exist) ---
+for f in src/server/contractors.ts src/server/index.ts public/contractors.html public/nav-drawer.js; do
+  [ -f "\$f" ] && cp -p "\$f" "\$BK/\$f" && echo "backed up \$f"
+done
+
+# --- deploy the 4 files ---
+cp /tmp/usre-deploy-$STAMP/src/server/contractors.ts src/server/contractors.ts
+cp /tmp/usre-deploy-$STAMP/src/server/index.ts        src/server/index.ts
+cp /tmp/usre-deploy-$STAMP/public/contractors.html    public/contractors.html
+cp /tmp/usre-deploy-$STAMP/public/nav-drawer.js       public/nav-drawer.js
+
+# --- enable the scoped contractor credential (idempotent) ---
+grep -q '^CONTRACTORS_USER=' .env || echo 'CONTRACTORS_USER=$CU' >> .env
+grep -q '^CONTRACTORS_PASS=' .env || echo 'CONTRACTORS_PASS=$CP' >> .env
+
+echo "--- restart usre ---"
+pm2 restart usrealestate --update-env >/dev/null 2>&1; sleep 3
+
+# --- smoke: NEW route must 200 with scoped cred; EXISTING route must stay 200 with admin ---
+NEW=\$(curl -s -o /dev/null -w '%{http_code}' -u "$CU:$CP" "http://localhost:\$PORT/api/contractors?limit=1")
+OLD=\$(curl -s -o /dev/null -w '%{http_code}' "http://localhost:\$PORT/healthz")   # pre-auth liveness — proves server up + routing intact, no creds
+SCOPE=\$(curl -s -o /dev/null -w '%{http_code}' -u "$CU:$CP" "http://localhost:\$PORT/api/ingest-health")  # scoped cred must NOT reach admin -> expect 401
+echo "smoke: /api/contractors(scoped)=\$NEW  /healthz(existing)=\$OLD  /api/ingest-health(scoped,should-401)=\$SCOPE"
+
+if [ "\$NEW" = "200" ] && [ "\$OLD" = "200" ]; then
+  echo "DEPLOY OK — contractor API live + scoped, existing routes intact."
+  [ "\$SCOPE" = "401" ] && echo "scope correctly enforced (contractor cred can't reach admin)." || echo "WARN: scope check returned \$SCOPE (expected 401) — review."
+  rm -rf /tmp/usre-deploy-$STAMP
+else
+  echo "SMOKE FAILED (new=\$NEW old=\$OLD) — ROLLING BACK."
+  for f in src/server/index.ts public/contractors.html public/nav-drawer.js; do [ -f "\$BK/\$f" ] && cp -p "\$BK/\$f" "\$f"; done
+  [ -f "\$BK/src/server/contractors.ts" ] || rm -f src/server/contractors.ts  # was a new file
+  pm2 restart usrealestate --update-env >/dev/null 2>&1; sleep 2
+  echo "ROLLED BACK to $STAMP baseline. Backup kept at \$BK. Investigate before retry."
+  exit 1
+fi
+REMOTE
+
+cat <<NEXT
+
+########################################################################
+# PHASE 2b — point the builds at the scoped usre API (after 2a is OK)
+# On Kamatera, in each build's .env, add (usre URL + the scoped cred):
+#   CONTRACTORS_API_BASE=https://usrealestate.agentabrams.com
+#   CONTRACTORS_API_USER=contractors
+#   CONTRACTORS_API_PASS=<the CONTRACTORS_PASS you set in phase 2a>   # keep out of git; use the secrets skill
+# CRCP:        cd ~/Projects/commercialrealestate && /deploy   (has .deploy.conf)
+# HomesOnSpec: deploy apps/web the normal way + pm2 restart homesonspec-web --update-env
+# Each build's fetch layer must send Basic auth to the usre API (the shared client supports it).
+# Smoke each: load a page that renders contractors -> should show data, no login prompt for end users.
+#
+# RENTV: claude-rentv mounts rentv-v1/contrib/contractors/ per README (DM M-00598 sent).
+########################################################################
+NEXT

← ac61383 usre: scoped Basic-auth credential for the shareable Contrac  ·  back to Nationalrealestate  ·  usre: pm2 ecosystem runs 'node --import tsx' directly (orpha be069fc →