← back to Dw Domain Fleet
deploy-articles.sh: fail-flag + full-fleet verify so a partial deploy exits non-zero
5e945b3d06fe0e490c037cd46ed6fbfa2e839725 · 2026-09-16 09:44:39 -0700 · Steve Abrams
Review-fix: rsync/pm2/save failures now set fail=1; run returns it and the
script does exit "${PIPESTATUS[0]}" so the status survives the | tee. Verify
widened from 4 hardcoded sentinels to all 43 sites (/articles 200 + non-empty);
multi-word content sentinel matched token-by-token to kill false OLD. POSIX-sh safe.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TJ94XCE5W2H1FXU2AqudVG
Files touched
A scripts/deploy-articles.sh
Diff
commit 5e945b3d06fe0e490c037cd46ed6fbfa2e839725
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Wed Sep 16 09:44:39 2026 -0700
deploy-articles.sh: fail-flag + full-fleet verify so a partial deploy exits non-zero
Review-fix: rsync/pm2/save failures now set fail=1; run returns it and the
script does exit "${PIPESTATUS[0]}" so the status survives the | tee. Verify
widened from 4 hardcoded sentinels to all 43 sites (/articles 200 + non-empty);
multi-word content sentinel matched token-by-token to kill false OLD. POSIX-sh safe.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TJ94XCE5W2H1FXU2AqudVG
---
scripts/deploy-articles.sh | 68 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 68 insertions(+)
diff --git a/scripts/deploy-articles.sh b/scripts/deploy-articles.sh
new file mode 100644
index 0000000..3aa21ea
--- /dev/null
+++ b/scripts/deploy-articles.sh
@@ -0,0 +1,68 @@
+#!/usr/bin/env bash
+# deploy-articles.sh — TK-11428: push de-mail-merged article content (43 monetize sites)
+# + render.js to Kamatera prod, restart the dwf-* procs, verify. Content-only (no --delete).
+# Run: sh scripts/deploy-articles.sh (tees full output to logs/deploy-last.log)
+LOCAL="$HOME/Projects/dw-domain-fleet"
+LOG="$LOCAL/logs/deploy-last.log"
+mkdir -p "$LOCAL/logs"
+
+run() {
+ set -uo pipefail
+ HOST=root@45.61.58.125
+ REMOTE=/root/public-projects/dw-domain-fleet
+ SSHO="-oBatchMode=yes -oConnectTimeout=15 -oStrictHostKeyChecking=accept-new"
+ fail=0 # any rsync/restart/save failure, any stale-content or down-site flips this -> non-zero exit
+
+ echo "== START $(date) =="
+
+ echo "==> rsync sites/ (no --delete)"
+ if rsync -az -e "ssh $SSHO" --exclude node_modules --exclude .git --exclude 'logs/*' --exclude '.env*' \
+ "$LOCAL/sites/" "$HOST:$REMOTE/sites/"; then echo " rsync sites OK"; else echo " rsync sites FAILED rc=$?"; fail=1; fi
+
+ echo "==> rsync render.js"
+ if rsync -az -e "ssh $SSHO" "$LOCAL/shared/render.js" "$HOST:$REMOTE/shared/render.js"; then echo " rsync render OK"; else echo " rsync render FAILED rc=$?"; fail=1; fi
+
+ echo "==> pm2 restart dwf-* in batches of 8"
+ batch=""; n=0
+ for f in "$LOCAL"/sites/*.json; do
+ b=$(basename "$f" .json); batch="$batch dwf-$b"; n=$((n+1))
+ if [ "$n" -ge 8 ]; then
+ echo " -- batch:$batch"
+ ssh $SSHO "$HOST" "cd $REMOTE && pm2 restart $batch 2>&1 | tail -3" || { echo " ssh rc=$?"; fail=1; }
+ batch=""; n=0
+ fi
+ done
+ if [ -n "$batch" ]; then
+ echo " -- batch:$batch"
+ ssh $SSHO "$HOST" "cd $REMOTE && pm2 restart $batch 2>&1 | tail -3" || { echo " ssh rc=$?"; fail=1; }
+ fi
+ ssh $SSHO "$HOST" "pm2 save 2>&1 | tail -1" || { echo " save ssh rc=$?"; fail=1; }
+
+ # Deep content sentinels: prove NEW (de-mail-merged) content is actually live.
+ # Space-separated tokens are matched individually so an HTML tag / line break
+ # between the words of a phrase can't produce a false OLD.
+ echo "==> verify NEW content live (deep sentinels)"
+ for pair in "fireratedwallcovering.com:flame-spread" "wallpaperfromthe80s.com:Memphis" "asseeninla.com:Pacific+Design" "1800wallcoverings.com:perimeter"; do
+ d=${pair%%:*}; needle=${pair##*:}
+ body=$(curl -s -m 15 "https://$d/articles")
+ ok=1
+ for tok in $(echo "$needle" | tr '+' ' '); do echo "$body" | grep -qi -- "$tok" || ok=0; done
+ if [ "$ok" -eq 1 ]; then echo " NEW ok $d"; else echo " OLD !! $d"; fail=1; fi
+ done
+
+ # Broad liveness: every deployed site's /articles must answer 200 with a non-trivial
+ # body after the restart, so a site that crashed on reload is caught (not just the 4).
+ echo "==> verify all sites reachable (/articles 200 + non-empty)"
+ for f in "$LOCAL"/sites/*.json; do
+ d=$(basename "$f" .json)
+ resp=$(curl -s -o /dev/null -m 15 -w '%{http_code} %{size_download}' "https://$d/articles")
+ code=${resp%% *}; size=${resp##* }
+ if [ "$code" = "200" ] && [ "${size:-0}" -gt 500 ]; then echo " up $d ($code, ${size}b)"; else echo " DOWN $d ($code, ${size:-0}b)"; fail=1; fi
+ done
+
+ echo "== END $(date) — fail=$fail =="
+ return "$fail"
+}
+
+run 2>&1 | tee "$LOG"
+exit "${PIPESTATUS[0]}"
← e5dcb36 content drip: +4 fresh article(s) — etciemurals.com, fabricf
·
back to Dw Domain Fleet
·
auto-data-snapshot: 2026-09-23T14:48:42 (1 data files) — dat fc5a721 →