diff --git a/scripts/runewager_redeploy.sh b/scripts/runewager_redeploy.sh index b9877e1..41067b5 100755 --- a/scripts/runewager_redeploy.sh +++ b/scripts/runewager_redeploy.sh @@ -79,6 +79,19 @@ _check_root() { fi } +_check_deps() { + local missing=() + for cmd in lsof curl; do + command -v "$cmd" >/dev/null 2>&1 || missing+=("$cmd") + done + if (( ${#missing[@]} )); then + fail "Missing required tool(s): ${missing[*]}" + fail "Install with: apt-get install -y ${missing[*]}" + exit 1 + fi + ok "Dependencies present: lsof curl" +} + _check_app_dir() { if [[ ! -f "$APP_DIR/index.js" ]]; then # Try VPS default path @@ -166,12 +179,18 @@ _npm_rebuild() { fi pushd "$app" >/dev/null - # Run at low priority so this install doesn't spike the box + # Run at low priority so this install doesn't spike the box. + # Capture PIPESTATUS[0] before anything else can overwrite it. nice -n 19 ionice -c 3 npm ci --prefer-offline 2>&1 \ | tail -5 \ | sed 's/^/ /' - ok "npm ci complete" + local npm_rc=${PIPESTATUS[0]} popd >/dev/null + if (( npm_rc != 0 )); then + fail "npm ci failed (exit ${npm_rc}) — redeploy aborted" + return "$npm_rc" + fi + ok "npm ci complete" } # ─────────────────────────────── STEP 4: INSTALL GUARD ─────────────────────── @@ -232,9 +251,13 @@ _health_check() { http_code=$(curl -sS --max-time 3 -o /dev/null -w "%{http_code}" "$HEALTH_URL" 2>/dev/null) || true if [[ "$http_code" == "200" ]]; then ok "Bot healthy at ${HEALTH_URL} (${elapsed}s)" - # Print health payload - curl -sS --max-time 3 "$HEALTH_URL" 2>/dev/null | python3 -m json.tool 2>/dev/null \ - | head -20 | sed 's/^/ /' || true + # Print health payload — degrade gracefully if python3 absent + local _payload; _payload=$(curl -sS --max-time 3 "$HEALTH_URL" 2>/dev/null) + if command -v python3 >/dev/null 2>&1; then + echo "$_payload" | python3 -m json.tool 2>/dev/null | head -20 | sed 's/^/ /' || echo "$_payload" | head -20 | sed 's/^/ /' + else + echo "$_payload" | head -20 | sed 's/^/ /' + fi return 0 fi sleep 3 @@ -269,11 +292,19 @@ _start_forensics() { return 0 fi - "$GUARD_SCRIPT" --forensics=300 >> /var/log/rw_cpu_forensics_deploy.log 2>&1 & + # Allow override; default to /var/log which requires root (already checked) + local forensics_log="${RW_FORENSICS_LOG:-/var/log/rw_cpu_forensics_deploy.log}" + if ! touch "$forensics_log" 2>/dev/null; then + warn "Cannot write forensics log '$forensics_log' — skipping" + warn "Override: export RW_FORENSICS_LOG=/tmp/rw_forensics.log" + return 0 + fi + + "$GUARD_SCRIPT" --forensics=300 >> "$forensics_log" 2>&1 & local fpid=$! ok "Forensics trace running in background (PID ${fpid})" - ok "Log: /var/log/rw_cpu_forensics_deploy.log" - ok "Data: /root/rw_cpu_forensics/" + ok "Log: ${forensics_log}" + ok "Data: ${FORENSICS_DIR:-/root/rw_cpu_forensics}/" } # ─────────────────────────────── STEP 8: SUMMARY ───────────────────────────── @@ -305,7 +336,8 @@ _summary() { echo "" echo -e " ${_bold}Load avg:${_reset} $(cat /proc/loadavg)" - echo -e " ${_bold}Memory:${_reset} $(free -m | awk 'NR==2{printf "used=%sMB free=%sMB\n",$3,$4}')" + local _mem; _mem=$(free -m | awk 'NR==2{printf "used=%sMB free=%sMB", $3, $4}') + echo -e " ${_bold}Memory:${_reset} ${_mem}" echo "" echo -e " ${_bold}Last guard actions:${_reset}" @@ -339,15 +371,17 @@ main() { esac done + # Run preflight checks first so the banner always shows the resolved app dir + _check_root + _check_deps + _check_app_dir + hdr "Runewager Nuclear Redeploy — $(date)" echo -e " App dir : $(_app)" echo -e " Guard : $GUARD_SCRIPT" echo -e " Dry-run : $( (( DRY_RUN )) && echo YES || echo no )" echo "" - _check_root - _check_app_dir - _stop_bot_service _kill_port _npm_rebuild diff --git a/scripts/rw_cpu_guard.sh b/scripts/rw_cpu_guard.sh index c0cede7..907e185 100755 --- a/scripts/rw_cpu_guard.sh +++ b/scripts/rw_cpu_guard.sh @@ -602,7 +602,14 @@ _show_status() { _run_forensics() { local duration="${1:-$FORENSICS_DURATION}" local out="$FORENSICS_DIR" - mkdir -p "$out" + + # Fail fast if we can't write to the output directory + if ! mkdir -p "$out" 2>/dev/null || ! touch "$out/.write_test" 2>/dev/null; then + echo "ERROR: forensics output dir '$out' is not writable." >&2 + echo " Set FORENSICS_DIR to a writable path and retry." >&2 + return 1 + fi + rm -f "$out/.write_test" local trace="$out/cpu_trace.csv" local vmstat_log="$out/vmstat.log" @@ -645,8 +652,9 @@ _run_forensics() { ( while true; do ps -eo pid,ppid,pcpu,pmem,cmd --sort=-pcpu --no-headers 2>/dev/null \ | awk -v ts="$(date '+%F %T')" '$3+0 > 15 { - gsub(/,/, ";", $5); - printf "%s,%s,%s,%s,%s,%s\n", ts, $1, $2, $3, $4, $5 + cmd = ""; for (i=5; i<=NF; i++) cmd = cmd (i>5 ? " " : "") $i; + gsub(/,/, ";", cmd); + printf "%s,%s,%s,%s,%s,%s\n", ts, $1, $2, $3, $4, cmd }' sleep 1 done @@ -705,8 +713,9 @@ _run_forensics() { echo "" echo "── Peak process count (fork-storm indicator) ───" - sort -k2 -rn "$proc_log" 2>/dev/null | head -5 \ - | awk '{printf " %s procs=%s\n", $1, $2}' || echo " (no data)" + # proc_log format: "