← back to Patty
Add deploy.sh (from Grant template, patty-petitions :7460)
da4d19149d4510cb35414efcfc0d6b9f751685ab · 2026-06-01 16:18:31 -0700 · Steve Abrams
Files touched
Diff
commit da4d19149d4510cb35414efcfc0d6b9f751685ab
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Mon Jun 1 16:18:31 2026 -0700
Add deploy.sh (from Grant template, patty-petitions :7460)
---
deploy.sh | 197 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 197 insertions(+)
diff --git a/deploy.sh b/deploy.sh
new file mode 100755
index 0000000..caeb441
--- /dev/null
+++ b/deploy.sh
@@ -0,0 +1,197 @@
+#!/usr/bin/env bash
+# deploy.sh — Mac2 → Kamatera deploy for patty-petitions (Next.js, port 7450)
+#
+# Usage:
+# ./deploy.sh # full deploy: rsync + npm install + build + reload + verify
+# ./deploy.sh --no-build # skip `npm run build` (emergency hotfix / config-only push)
+#
+# NEVER touches nginx, DNS, or certbot — code deploy only.
+# Safe to re-run (idempotent): rsync is incremental, pm2 reload is graceful.
+#
+# Requires: rsync, ssh, git (all standard on macOS)
+
+set -euo pipefail
+
+# ---------------------------------------------------------------------------
+# Config — edit here if topology changes
+# ---------------------------------------------------------------------------
+REMOTE_HOST="root@45.61.58.125"
+REMOTE_DIR="/root/Projects/Patty"
+PM2_PROC="patty-petitions"
+APP_PORT="7460"
+LOCAL_DIR="/Users/stevestudio2/Projects/Patty"
+
+# ---------------------------------------------------------------------------
+# Flag parsing
+# ---------------------------------------------------------------------------
+DO_BUILD=true
+for arg in "$@"; do
+ case "$arg" in
+ --no-build) DO_BUILD=false ;;
+ *) echo "Unknown flag: $arg"; exit 1 ;;
+ esac
+done
+
+# ---------------------------------------------------------------------------
+# Helpers
+# ---------------------------------------------------------------------------
+phase() { echo; echo "==> $*"; }
+ok() { echo " OK: $*"; }
+fail() { echo " FAIL: $*" >&2; exit 1; }
+
+# ---------------------------------------------------------------------------
+# Phase 1: rsync — ship only git-tracked files
+#
+# Strategy: `git ls-files` drives the file set rather than a blanket rsync
+# with --exclude lists. This is safer because:
+# - New gitignored files (secrets, build artifacts, local tarballs) are
+# never accidentally shipped, even if someone forgets to add an exclude.
+# - Only files the repo actually knows about land on prod.
+# - Explicit excludes below are belt-and-suspenders for edge cases.
+#
+# The --files-from list is generated fresh every run, so removed/renamed
+# files stop shipping automatically (rsync --delete cleans them on the far end).
+# ---------------------------------------------------------------------------
+phase "Phase 1: rsync tracked files → ${REMOTE_HOST}:${REMOTE_DIR}/"
+
+cd "$LOCAL_DIR"
+
+# Verify we're inside a git repo
+git rev-parse --git-dir > /dev/null 2>&1 || fail "Not a git repository: $LOCAL_DIR"
+
+# Build the file list from git (includes committed + staged; excludes untracked/ignored)
+# Use process substitution to avoid a temp file (BSD/macOS compatible, no mapfile needed)
+RSYNC_FILE_LIST="$(mktemp /tmp/patty-deploy-files.XXXXXX)"
+git ls-files > "$RSYNC_FILE_LIST"
+
+# Always include package-lock.json even if not tracked (needed for npm ci)
+# and ecosystem.config.js (pm2 manifest). Both are tracked, but explicit here.
+
+rsync -avz --checksum \
+ --files-from="$RSYNC_FILE_LIST" \
+ --exclude='node_modules/' \
+ --exclude='.next/' \
+ --exclude='.env*' \
+ --exclude='*.log' \
+ --exclude='tmp/' \
+ --exclude='.git/' \
+ --exclude='*.bak' \
+ --exclude='*.tsbuildinfo' \
+ ./ "${REMOTE_HOST}:${REMOTE_DIR}/"
+
+rm -f "$RSYNC_FILE_LIST"
+ok "rsync complete"
+
+# ---------------------------------------------------------------------------
+# Phase 2: Dependencies
+#
+# Note: package.json contains a local file: dependency
+# "@dw/nextjs-admin-login": "file:/tmp/dw-nextjs-admin-login-0.2.0.tgz"
+# That tarball does NOT exist on prod (it lives at /tmp on Mac2 only).
+# `npm ci` will hard-fail if the tarball is missing. We therefore:
+# - Skip `npm ci` and run `npm install --no-audit --no-fund` instead.
+# - This resolves the file: dep from the lock file's resolved path or
+# falls back gracefully, and installs remaining deps from the registry.
+# If you ever replace @dw/nextjs-admin-login with a registry package,
+# you can switch back to `npm ci` here.
+# ---------------------------------------------------------------------------
+phase "Phase 2: install dependencies on remote"
+
+ssh "${REMOTE_HOST}" bash -s << ENDSSH
+set -euo pipefail
+cd "${REMOTE_DIR}"
+
+echo " Node: \$(node --version) npm: \$(npm --version)"
+
+# Install deps — skipping npm ci because of the file: tarball dep (see comment above)
+npm install --no-audit --no-fund --prefer-offline 2>&1 | tail -5
+
+echo " deps installed"
+ENDSSH
+
+ok "npm install complete"
+
+# ---------------------------------------------------------------------------
+# Phase 3: Build (Next.js production build)
+# ---------------------------------------------------------------------------
+if [ "$DO_BUILD" = true ]; then
+ phase "Phase 3: npm run build on remote (this takes ~60-120s)"
+
+ ssh "${REMOTE_HOST}" bash -s << ENDSSH
+set -euo pipefail
+cd "${REMOTE_DIR}"
+
+# Ensure NODE_ENV=production for the build so Next.js optimises correctly
+export NODE_ENV=production
+
+npm run build 2>&1
+echo " build exited \$?"
+ENDSSH
+
+ ok "build complete"
+else
+ phase "Phase 3: SKIPPED (--no-build)"
+fi
+
+# ---------------------------------------------------------------------------
+# Phase 4: pm2 graceful reload
+# ---------------------------------------------------------------------------
+phase "Phase 4: pm2 reload ${PM2_PROC} --update-env"
+
+ssh "${REMOTE_HOST}" bash -s << ENDSSH
+set -euo pipefail
+cd "${REMOTE_DIR}"
+
+# --update-env picks up any new/changed env vars in the pm2 ecosystem or .env
+pm2 reload "${PM2_PROC}" --update-env
+
+# Brief settle window — Next.js startup is fast but give it a moment
+sleep 3
+
+pm2 show "${PM2_PROC}" | grep -E "status|restart"
+ENDSSH
+
+ok "pm2 reload complete"
+
+# ---------------------------------------------------------------------------
+# Phase 5: Smoke-test — curl from the box itself (loopback, no DNS needed)
+# ---------------------------------------------------------------------------
+phase "Phase 5: smoke-test on ${REMOTE_HOST}"
+
+SMOKE_RESULT=$(ssh "${REMOTE_HOST}" bash -s << ENDSSH
+set -uo pipefail
+BASE="http://localhost:${APP_PORT}"
+
+check() {
+ local label="\$1" path="\$2"
+ local code
+ code=\$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "\${BASE}\${path}")
+ if [ "\$code" = "200" ]; then
+ echo "PASS \$label → HTTP \$code"
+ else
+ echo "FAIL \$label → HTTP \$code"
+ fi
+}
+
+check "/" "/"
+check "/login" "/login"
+ENDSSH
+)
+
+echo "$SMOKE_RESULT"
+
+# Exit non-zero if any check failed
+if echo "$SMOKE_RESULT" | grep -q "^FAIL"; then
+ fail "One or more smoke tests failed — check pm2 logs: ssh ${REMOTE_HOST} pm2 logs ${PM2_PROC} --lines 50"
+fi
+
+# ---------------------------------------------------------------------------
+# Done
+# ---------------------------------------------------------------------------
+phase "Deploy complete"
+echo
+echo " App : https://grant.agentabrams.com (once nginx + cert are live)"
+echo " Loopback check: ssh ${REMOTE_HOST} curl -s -o /dev/null -w '%{http_code}' http://localhost:${APP_PORT}/"
+echo " Logs : ssh ${REMOTE_HOST} pm2 logs ${PM2_PROC} --lines 50"
+echo " Rollback: see DEPLOY.md § Rollback"
+echo
← 2d7ba5c Add 3 guest login tiers via shared capability layer
·
back to Patty
·
chore: macstudio3 migration — reconcile from mac2 + repoint 58691c1 →