← back to Ga Allsites
Add gate2_inject.sh + gate2_rollback.sh for GA4 fleet injection
6630fbffe29698c1c24bb35a60ce076f3ea8d7a1 · 2026-08-22 10:54:44 -0700 · Steve Abrams
Idempotent injection of G-J2SL8P5KDL into 17 target files (30 vendor
shared template + 16 standalones). Skips any file already containing
the ID. Timestamps .bak before every write; rollback restores all .bak
files and reloads affected pm2 processes.
Dry-run: all 17 files already contain ID (SKIP on first run).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Files touched
A gate2_inject.shA gate2_rollback.sh
Diff
commit 6630fbffe29698c1c24bb35a60ce076f3ea8d7a1
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Sat Aug 22 10:54:44 2026 -0700
Add gate2_inject.sh + gate2_rollback.sh for GA4 fleet injection
Idempotent injection of G-J2SL8P5KDL into 17 target files (30 vendor
shared template + 16 standalones). Skips any file already containing
the ID. Timestamps .bak before every write; rollback restores all .bak
files and reloads affected pm2 processes.
Dry-run: all 17 files already contain ID (SKIP on first run).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---
gate2_inject.sh | 368 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
gate2_rollback.sh | 146 ++++++++++++++++++++++
2 files changed, 514 insertions(+)
diff --git a/gate2_inject.sh b/gate2_inject.sh
new file mode 100755
index 0000000..39b7c6e
--- /dev/null
+++ b/gate2_inject.sh
@@ -0,0 +1,368 @@
+#!/usr/bin/env bash
+# gate2_inject.sh — GATE-2 GA4 injection for the 34-site DW fleet microsite fleet
+# Measurement ID: G-J2SL8P5KDL (property "DW Fleet Microsites" / 551081041)
+#
+# IDEMPOTENT: any file already containing G-J2SL8P5KDL is skipped unchanged.
+# REVERSIBLE: timestamped .bak files written before every edit; gate2_rollback.sh restores them.
+# RUNS ON KAMATERA: ship this entire heredoc over SSH.
+#
+# Deploy command (Steve pastes):
+# ! ssh root@45.61.58.125 'bash -s' < ~/Projects/ga-allsites/gate2_inject.sh
+#
+# Rollback command:
+# ! ssh root@45.61.58.125 'bash -s' < ~/Projects/ga-allsites/gate2_rollback.sh
+# ---------------------------------------------------------------------------
+set -euo pipefail
+
+ID="G-J2SL8P5KDL"
+
+ssh root@45.61.58.125 "ID=$ID" 'bash -s' <<'REMOTE'
+set -uo pipefail
+ID="${ID:-G-J2SL8P5KDL}"
+TS=$(date +%Y%m%d-%H%M%S)
+
+# ────────────────────────────────────────────────────────────────────────────
+# Counters
+N_INJECTED=0; N_SKIPPED=0; N_FAILED=0; N_MANUAL=0
+declare -A RESULT # file -> status string
+declare -A BAK_MAP # file -> .bak path (written to /tmp/gate2-bak-manifest.txt at end)
+
+# ────────────────────────────────────────────────────────────────────────────
+# STANDALONE TARGETS (file path | vendor slug | pm2 process name)
+# Each gets: async gtag loader + gtag('js') + gtag('config', ID, {vendor:'<slug>'})
+# inserted as the very first child of <head>.
+STANDALONE_RAW=(
+ "/root/Projects/artmura-site/public/index.html|artmura|artmura-site"
+ "/root/Projects/carnegie-site/public/index.html|carnegie|carnegie-site"
+ "/root/Projects/thibaut-site/public/index.html|thibaut|thibaut-site"
+ "/root/Projects/elitis-site/public/index.html|elitis|elitis-site"
+ "/root/Projects/romo-site/public/index.html|romo|romo-site"
+ "/root/Projects/la-walls-site/public/index.html|la-walls|la-walls-site"
+ "/root/Projects/british-walls-site/public/index.html|british-walls|british-walls-site"
+ "/root/Projects/leed-walls-site/public/index.html|leed-walls|leed-walls-site"
+ "/root/Projects/surface-stick-site/public/index.html|surface-stick|surface-stick-site"
+ "/root/Projects/coleandson-site/public/index.html|cole-and-son|coleandson-site"
+ "/root/Projects/fallingstar-site/public/index.html|fallingstar|fallingstar-site"
+ "/root/Projects/philipjeffries-site/public/index.html|phillip-jeffries|philipjeffries-site"
+ "/root/Projects/primo-leathers-site/public/index.html|primo-leathers|primo-leathers-site"
+ "/root/Projects/quadrille-house-site/public/index.html|quadrille|quadrille-house-site"
+ "/var/www/fabricut.designerwallcoverings.com/public/index.html|fabricut|fabricut-landing"
+ "/root/Projects/fentucci-site/public/index.html|fentucci|fentucci-site"
+)
+
+# SHARED TEMPLATE (30-vendor wolfgordon binary)
+# Gets: async loader + gtag('js') in <head>,
+# gtag('config', ID, {vendor:cfg.vendor}) inserted INSIDE the async IIFE
+# on the line immediately after applyConfig(cfg);
+SHARED_FILE="/root/Projects/wolfgordon-site/public/index.html"
+# All 30 pm2 processes that run wolfgordon-site/server.js — reload all of them.
+WOLF_PM2_NAMES=(
+ anna-french-site brunschwig-fils-site coordonne-site designers-guild-site
+ designtex-site gp-j-baker-site groundworks-site harlequin-site knoll-site
+ koroseal-site kravet-site lee-jofa-site maharam-site maya-romanoff-site
+ mulberry-site nina-campbell-site nobilis-site osborne-little-site
+ phillip-jeffries-site pierre-frey-site ralph-lauren-site rebel-walls-site
+ sancar-site sandberg-site scalamandre-site threads-site william-morris-site
+ wolf-gordon-site wolfgordon-site zoffany-site
+)
+
+# FENTUCCI-WEBSITE (old binary with commented-out placeholder)
+# Gets: commented placeholder uncommented + ID set to G-J2SL8P5KDL.
+# The real loader + config are inserted the same way as standalones (head injection),
+# but we ALSO remove the now-redundant commented placeholder if both coexist.
+FENTUCCI_OLD_FILE="/root/Projects/Designer-Wallcoverings/DW-Websites/Fentucci/public/index.html"
+FENTUCCI_OLD_PM2="fentucci-website"
+
+# ────────────────────────────────────────────────────────────────────────────
+# Helper: build the head snippet for a static vendor slug
+head_snippet_static() {
+ local vendor="$1"
+ printf '<script async src="https://www.googletagmanager.com/gtag/js?id=%s"></script>\n<script>window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments);}gtag('\''js'\'',new Date());gtag('\''config'\'','\''%s'\'',{vendor:'\''%s'\''});</script>' \
+ "$ID" "$ID" "$vendor"
+}
+
+# Helper: build the head snippet for the shared template (no config here; fires in IIFE)
+head_snippet_shared() {
+ printf '<script async src="https://www.googletagmanager.com/gtag/js?id=%s"></script>\n<script>window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments);}gtag('\''js'\'',new Date());</script>' \
+ "$ID"
+}
+
+# ────────────────────────────────────────────────────────────────────────────
+# inject_standalone FILE VENDOR PM2_NAME
+inject_standalone() {
+ local file="$1" vendor="$2" pm2_name="$3"
+ local label="$vendor"
+
+ if [ ! -f "$file" ]; then
+ RESULT["$label"]="MISSING — file not found: $file"
+ (( N_MANUAL++ )) || true; return
+ fi
+
+ if grep -qF "$ID" "$file"; then
+ RESULT["$label"]="SKIP — already contains $ID"
+ (( N_SKIPPED++ )) || true; return
+ fi
+
+ if ! grep -qiE '</head>' "$file"; then
+ RESULT["$label"]="NEEDS-MANUAL — no </head> anchor found: $file"
+ (( N_MANUAL++ )) || true; return
+ fi
+
+ local bak="${file}.bak-ga-${TS}"
+ cp -a "$file" "$bak"
+ BAK_MAP["$file"]="$bak"
+
+ local snippet
+ snippet="$(head_snippet_static "$vendor")"
+
+ # Insert the snippet immediately before </head>
+ perl -0777 -i -pe "s{(</head>)}{${snippet}\n\$1}i" "$file"
+
+ if grep -qF "$ID" "$file"; then
+ RESULT["$label"]="INJECTED — $file [pm2: $pm2_name]"
+ (( N_INJECTED++ )) || true
+ # Reload pm2 process
+ if pm2 reload "$pm2_name" >/dev/null 2>&1; then
+ RESULT["$label"]+=" | pm2 reload: OK"
+ else
+ RESULT["$label"]+=" | pm2 reload: WARN (try restart manually)"
+ fi
+ else
+ # Revert
+ cp -a "$bak" "$file"
+ RESULT["$label"]="FAIL — perl injection failed, reverted from bak: $file"
+ (( N_FAILED++ )) || true
+ fi
+}
+
+# ────────────────────────────────────────────────────────────────────────────
+# inject_shared — wolfgordon-site/public/index.html
+inject_shared() {
+ local file="$SHARED_FILE"
+ local label="wolfgordon-shared-template"
+
+ if [ ! -f "$file" ]; then
+ RESULT["$label"]="MISSING — file not found: $file"
+ (( N_MANUAL++ )) || true; return
+ fi
+
+ if grep -qF "$ID" "$file"; then
+ RESULT["$label"]="SKIP — already contains $ID"
+ (( N_SKIPPED++ )) || true; return
+ fi
+
+ # Both anchors must be present
+ if ! grep -qiE '</head>' "$file"; then
+ RESULT["$label"]="NEEDS-MANUAL — no </head> anchor: $file"
+ (( N_MANUAL++ )) || true; return
+ fi
+ if ! grep -qF 'applyConfig(cfg);' "$file"; then
+ RESULT["$label"]="NEEDS-MANUAL — no 'applyConfig(cfg);' anchor in IIFE: $file"
+ (( N_MANUAL++ )) || true; return
+ fi
+
+ local bak="${file}.bak-ga-${TS}"
+ cp -a "$file" "$bak"
+ BAK_MAP["$file"]="$bak"
+
+ local head_snip
+ head_snip="$(head_snippet_shared)"
+
+ # 1. Insert loader + gtag('js') before </head>
+ perl -0777 -i -pe "s{(</head>)}{${head_snip}\n\$1}i" "$file"
+
+ # 2. Insert gtag('config', ID, {vendor:cfg.vendor}) immediately after applyConfig(cfg);
+ # (single-line match; the line has 6 leading spaces in the current file)
+ perl -0777 -i -pe "s{(applyConfig\(cfg\);)(\s*\n)}{\$1\$2 gtag('config','${ID}',{vendor:cfg.vendor});\n}" "$file"
+
+ if grep -qF "$ID" "$file"; then
+ RESULT["$label"]="INJECTED — $file [30 pm2 processes]"
+ (( N_INJECTED++ )) || true
+ # Reload all 30 processes on this binary
+ local reload_ok=0 reload_fail=0
+ for pname in "${WOLF_PM2_NAMES[@]}"; do
+ if pm2 reload "$pname" >/dev/null 2>&1; then
+ (( reload_ok++ )) || true
+ else
+ (( reload_fail++ )) || true
+ fi
+ done
+ RESULT["$label"]+=" | pm2 reload: ${reload_ok}/${#WOLF_PM2_NAMES[@]} OK"
+ [ "$reload_fail" -gt 0 ] && RESULT["$label"]+=" (${reload_fail} failed — check pm2 log)"
+ else
+ cp -a "$bak" "$file"
+ RESULT["$label"]="FAIL — injection failed, reverted: $file"
+ (( N_FAILED++ )) || true
+ fi
+}
+
+# ────────────────────────────────────────────────────────────────────────────
+# inject_fentucci_old — /root/Projects/Designer-Wallcoverings/DW-Websites/Fentucci/public/index.html
+# Strategy: the file has a commented-out placeholder:
+# <!-- <script async src="...G-XXXXXXXXXX"></script> -->
+# Uncomment it and set the ID. Then also ensure the config call is present.
+# If the file already has G-J2SL8P5KDL anywhere, skip.
+inject_fentucci_old() {
+ local file="$FENTUCCI_OLD_FILE"
+ local label="fentucci-website-OLD-binary"
+
+ if [ ! -f "$file" ]; then
+ RESULT["$label"]="MISSING — file not found: $file"
+ (( N_MANUAL++ )) || true; return
+ fi
+
+ if grep -qF "$ID" "$file"; then
+ RESULT["$label"]="SKIP — already contains $ID"
+ (( N_SKIPPED++ )) || true; return
+ fi
+
+ if ! grep -qiE '</head>' "$file"; then
+ RESULT["$label"]="NEEDS-MANUAL — no </head> anchor: $file"
+ (( N_MANUAL++ )) || true; return
+ fi
+
+ local bak="${file}.bak-ga-${TS}"
+ cp -a "$file" "$bak"
+ BAK_MAP["$file"]="$bak"
+
+ # Try to uncomment the placeholder line and set ID
+ # Pattern: <!-- <script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"></script> -->
+ local placeholder_found=0
+ if grep -q 'GA4 placeholder\|gtag/js.*G-' "$file"; then
+ # Uncomment the script tag and set the correct ID
+ perl -i -pe 's{<!--\s*(<script async src="https://www\.googletagmanager\.com/gtag/js\?id=)[^"]*(">[^<]*</script>)\s*-->}{\1'"$ID"'\2}' "$file"
+ placeholder_found=1
+ fi
+
+ # Whether or not we uncommented a placeholder, we need the full bootstrap in <head>.
+ # Insert before </head> if the ID isn't present yet (the uncommented placeholder is only the loader tag).
+ if ! grep -qF "$ID" "$file"; then
+ # placeholder uncomment didn't land the ID — inject from scratch
+ local snippet
+ snippet="$(head_snippet_static "fentucci")"
+ perl -0777 -i -pe "s{(</head>)}{${snippet}\n\$1}i" "$file"
+ else
+ # We got the loader via placeholder uncomment but still need the config call + init script.
+ # Insert a config+init block right after the uncommented loader line.
+ perl -i -pe 's{(<script async src="https://www\.googletagmanager\.com/gtag/js\?id='"$ID"'"></script>)}{$1\n<script>window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments);}gtag('\''js'\'',new Date());gtag('\''config'\'','\'''"$ID"''\'',{vendor:'\''fentucci'\''});</script>}' "$file" || true
+ fi
+
+ if grep -qF "$ID" "$file"; then
+ RESULT["$label"]="INJECTED — $file [pm2: $FENTUCCI_OLD_PM2]"
+ (( N_INJECTED++ )) || true
+ if pm2 reload "$FENTUCCI_OLD_PM2" >/dev/null 2>&1; then
+ RESULT["$label"]+=" | pm2 reload: OK"
+ else
+ RESULT["$label"]+=" | pm2 reload: WARN"
+ fi
+ else
+ cp -a "$bak" "$file"
+ RESULT["$label"]="FAIL — injection failed, reverted: $file"
+ (( N_FAILED++ )) || true
+ fi
+}
+
+# ────────────────────────────────────────────────────────────────────────────
+# EXECUTE
+
+echo ""
+echo "========================================================"
+echo " GATE-2 GA4 INJECT | ID: $ID"
+echo " Timestamp: $TS"
+echo "========================================================"
+echo ""
+
+echo "[1/3] Shared wolfgordon template (30 vendors)..."
+inject_shared
+
+echo "[2/3] Standalone sites (16 files)..."
+for entry in "${STANDALONE_RAW[@]}"; do
+ IFS='|' read -r fpath vendor pm2name <<< "$entry"
+ inject_standalone "$fpath" "$vendor" "$pm2name"
+done
+
+echo "[3/3] Fentucci-website old binary (placeholder uncomment)..."
+inject_fentucci_old
+
+# ────────────────────────────────────────────────────────────────────────────
+# Write bak manifest so rollback can find all .bak files for this run
+echo "" > /tmp/gate2-bak-manifest-${TS}.txt
+for orig in "${!BAK_MAP[@]}"; do
+ echo "${orig}|${BAK_MAP[$orig]}" >> /tmp/gate2-bak-manifest-${TS}.txt
+done
+echo "(bak manifest: /tmp/gate2-bak-manifest-${TS}.txt)"
+
+# ────────────────────────────────────────────────────────────────────────────
+# VERIFY — curl each site, grep for the ID in served HTML
+echo ""
+echo "========================================================"
+echo " VERIFY"
+echo "========================================================"
+
+# Map vendor slug -> live URL for curl verification
+declare -A VERIFY_URL
+VERIFY_URL["artmura"]="http://127.0.0.1:$(pm2 jlist 2>/dev/null | python3 -c "import json,sys;d=json.load(sys.stdin);[print(p['pm2_env']['env'].get('PORT',3000)) for p in d if p['name']=='artmura-site']" 2>/dev/null | head -1 || echo 3000)"
+# Simpler: pull port from pm2 env for each site
+get_port() {
+ local pm2name="$1"
+ pm2 jlist 2>/dev/null | python3 -c "
+import json,sys
+try:
+ d=json.load(sys.stdin)
+ for p in d:
+ if p.get('name')=='$pm2name':
+ env=p.get('pm2_env',{}).get('env',{})
+ port=env.get('PORT') or env.get('port') or 3000
+ print(port)
+ break
+except:
+ print(3000)
+" 2>/dev/null | head -1
+}
+
+# Build verify list: "label|url"
+VERIFY_LIST=()
+for entry in "${STANDALONE_RAW[@]}"; do
+ IFS='|' read -r fpath vendor pm2name <<< "$entry"
+ port=$(get_port "$pm2name")
+ VERIFY_LIST+=("$vendor|http://127.0.0.1:${port}/")
+done
+# wolfgordon shared: pick wolf-gordon-site process
+wolf_port=$(get_port "wolf-gordon-site")
+VERIFY_LIST+=("wolfgordon-shared|http://127.0.0.1:${wolf_port}/")
+# fentucci-website old binary
+fwo_port=$(get_port "$FENTUCCI_OLD_PM2")
+VERIFY_LIST+=("fentucci-website-OLD|http://127.0.0.1:${fwo_port}/")
+
+N_VERIFY_PASS=0; N_VERIFY_FAIL=0
+for entry in "${VERIFY_LIST[@]}"; do
+ IFS='|' read -r label url <<< "$entry"
+ html=$(curl -sf --max-time 6 --retry 2 "$url" 2>/dev/null || true)
+ if echo "$html" | grep -qF "$ID"; then
+ printf " VERIFY PASS %-30s %s\n" "$label" "$url"
+ (( N_VERIFY_PASS++ )) || true
+ else
+ printf " VERIFY FAIL %-30s %s\n" "$label" "$url"
+ (( N_VERIFY_FAIL++ )) || true
+ fi
+done
+
+# ────────────────────────────────────────────────────────────────────────────
+echo ""
+echo "========================================================"
+echo " SUMMARY"
+echo "========================================================"
+for label in $(echo "${!RESULT[@]}" | tr ' ' '\n' | sort); do
+ printf " %-40s %s\n" "$label" "${RESULT[$label]}"
+done
+echo ""
+echo " Injected : $N_INJECTED"
+echo " Skipped : $N_SKIPPED"
+echo " Failed : $N_FAILED"
+echo " Manual : $N_MANUAL"
+echo " Verify : ${N_VERIFY_PASS} PASS / ${N_VERIFY_FAIL} FAIL"
+echo ""
+echo "Rollback: ssh root@45.61.58.125 'bash -s' < ~/Projects/ga-allsites/gate2_rollback.sh"
+echo " (restores all .bak files matching *.bak-ga-${TS})"
+echo "========================================================"
+REMOTE
diff --git a/gate2_rollback.sh b/gate2_rollback.sh
new file mode 100755
index 0000000..1e8dec6
--- /dev/null
+++ b/gate2_rollback.sh
@@ -0,0 +1,146 @@
+#!/usr/bin/env bash
+# gate2_rollback.sh — Restore all files modified by gate2_inject.sh
+# Finds every .bak-ga-* file written by the inject run and restores it.
+# Then reloads affected pm2 processes.
+#
+# Run from Mac2:
+# ! ssh root@45.61.58.125 'bash -s' < ~/Projects/ga-allsites/gate2_rollback.sh
+# ---------------------------------------------------------------------------
+set -uo pipefail
+
+ssh root@45.61.58.125 'bash -s' <<'REMOTE'
+set -uo pipefail
+ID="G-J2SL8P5KDL"
+
+echo ""
+echo "========================================================"
+echo " GATE-2 ROLLBACK — restoring .bak-ga-* files"
+echo "========================================================"
+echo ""
+
+# Find every .bak file this inject run created (pattern: <original>.bak-ga-YYYYMMDD-HHMMSS)
+BAK_FILES=()
+for f in \
+ /root/Projects/wolfgordon-site/public/index.html.bak-ga-* \
+ /root/Projects/artmura-site/public/index.html.bak-ga-* \
+ /root/Projects/carnegie-site/public/index.html.bak-ga-* \
+ /root/Projects/thibaut-site/public/index.html.bak-ga-* \
+ /root/Projects/elitis-site/public/index.html.bak-ga-* \
+ /root/Projects/romo-site/public/index.html.bak-ga-* \
+ /root/Projects/la-walls-site/public/index.html.bak-ga-* \
+ /root/Projects/british-walls-site/public/index.html.bak-ga-* \
+ /root/Projects/leed-walls-site/public/index.html.bak-ga-* \
+ /root/Projects/surface-stick-site/public/index.html.bak-ga-* \
+ /root/Projects/coleandson-site/public/index.html.bak-ga-* \
+ /root/Projects/fallingstar-site/public/index.html.bak-ga-* \
+ /root/Projects/philipjeffries-site/public/index.html.bak-ga-* \
+ /root/Projects/primo-leathers-site/public/index.html.bak-ga-* \
+ /root/Projects/quadrille-house-site/public/index.html.bak-ga-* \
+ "/var/www/fabricut.designerwallcoverings.com/public/index.html.bak-ga-*" \
+ /root/Projects/fentucci-site/public/index.html.bak-ga-* \
+ "/root/Projects/Designer-Wallcoverings/DW-Websites/Fentucci/public/index.html.bak-ga-*"
+do
+ [ -f "$f" ] && BAK_FILES+=("$f")
+done
+
+if [ ${#BAK_FILES[@]} -eq 0 ]; then
+ echo " Nothing to roll back — no .bak-ga-* files found for any of the 17 target paths."
+ echo " (Either gate2_inject.sh was never run, or all files were already skipped as no-op.)"
+ exit 0
+fi
+
+N_RESTORED=0
+N_FAILED=0
+TOUCHED_PM2=()
+
+restore_file() {
+ local bak="$1"
+ # Original file is everything before .bak-ga-
+ local orig="${bak%.bak-ga-*}"
+
+ echo " Restoring: $orig"
+ echo " from: $bak"
+
+ if cp -a "$bak" "$orig"; then
+ echo " status: RESTORED"
+ (( N_RESTORED++ )) || true
+ # Track for pm2 reload
+ TOUCHED_PM2+=("$orig")
+ else
+ echo " status: FAIL — could not copy bak"
+ (( N_FAILED++ )) || true
+ fi
+}
+
+for bak in "${BAK_FILES[@]}"; do
+ restore_file "$bak"
+ echo ""
+done
+
+# ─── Reload pm2 processes for restored files ────────────────────────────────
+echo "Reloading pm2 processes..."
+
+# If wolfgordon shared template was restored, reload all 30 wolf processes
+wolf_restored=0
+for orig in "${TOUCHED_PM2[@]}"; do
+ [[ "$orig" == *wolfgordon-site/public/index.html ]] && wolf_restored=1
+done
+
+if [ "$wolf_restored" -eq 1 ]; then
+ echo " wolfgordon-site shared template — reloading all 30 processes..."
+ WOLF_PROCS=(
+ anna-french-site brunschwig-fils-site coordonne-site designers-guild-site
+ designtex-site gp-j-baker-site groundworks-site harlequin-site knoll-site
+ koroseal-site kravet-site lee-jofa-site maharam-site maya-romanoff-site
+ mulberry-site nina-campbell-site nobilis-site osborne-little-site
+ phillip-jeffries-site pierre-frey-site ralph-lauren-site rebel-walls-site
+ sancar-site sandberg-site scalamandre-site threads-site william-morris-site
+ wolf-gordon-site wolfgordon-site zoffany-site
+ )
+ ok=0; fail=0
+ for p in "${WOLF_PROCS[@]}"; do
+ pm2 reload "$p" >/dev/null 2>&1 && (( ok++ )) || (( fail++ )) || true
+ done
+ echo " reloaded ${ok}/30 OK (${fail} failed)"
+fi
+
+# Map standalone paths to pm2 names
+declare -A PATH_PM2
+PATH_PM2["/root/Projects/artmura-site/public/index.html"]="artmura-site"
+PATH_PM2["/root/Projects/carnegie-site/public/index.html"]="carnegie-site"
+PATH_PM2["/root/Projects/thibaut-site/public/index.html"]="thibaut-site"
+PATH_PM2["/root/Projects/elitis-site/public/index.html"]="elitis-site"
+PATH_PM2["/root/Projects/romo-site/public/index.html"]="romo-site"
+PATH_PM2["/root/Projects/la-walls-site/public/index.html"]="la-walls-site"
+PATH_PM2["/root/Projects/british-walls-site/public/index.html"]="british-walls-site"
+PATH_PM2["/root/Projects/leed-walls-site/public/index.html"]="leed-walls-site"
+PATH_PM2["/root/Projects/surface-stick-site/public/index.html"]="surface-stick-site"
+PATH_PM2["/root/Projects/coleandson-site/public/index.html"]="coleandson-site"
+PATH_PM2["/root/Projects/fallingstar-site/public/index.html"]="fallingstar-site"
+PATH_PM2["/root/Projects/philipjeffries-site/public/index.html"]="philipjeffries-site"
+PATH_PM2["/root/Projects/primo-leathers-site/public/index.html"]="primo-leathers-site"
+PATH_PM2["/root/Projects/quadrille-house-site/public/index.html"]="quadrille-house-site"
+PATH_PM2["/var/www/fabricut.designerwallcoverings.com/public/index.html"]="fabricut-landing"
+PATH_PM2["/root/Projects/fentucci-site/public/index.html"]="fentucci-site"
+PATH_PM2["/root/Projects/Designer-Wallcoverings/DW-Websites/Fentucci/public/index.html"]="fentucci-website"
+
+for orig in "${TOUCHED_PM2[@]}"; do
+ [[ "$orig" == *wolfgordon-site/public/index.html ]] && continue # already handled above
+ pname="${PATH_PM2[$orig]:-}"
+ if [ -n "$pname" ]; then
+ pm2 reload "$pname" >/dev/null 2>&1 && echo " reloaded $pname" || echo " WARN: reload $pname failed"
+ fi
+done
+
+# ────────────────────────────────────────────────────────────────────────────
+echo ""
+echo "========================================================"
+echo " ROLLBACK SUMMARY"
+echo "========================================================"
+echo " Restored : $N_RESTORED files"
+echo " Failed : $N_FAILED files"
+echo ""
+echo "NOTE: .bak files are preserved at their original .bak-ga-* paths."
+echo " Verify with: grep -l 'G-J2SL8P5KDL' <any restored file> (should return nothing)"
+echo "========================================================"
+REMOTE
← bccd4f3 auto-data-snapshot: 2026-08-21T17:47:31 (1 data files) — etl
·
back to Ga Allsites
·
chore: GA GATE-2 fleet injector (route-by-nginx, idempotent, f399dae →