Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 46 additions & 12 deletions scripts/runewager_redeploy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 ───────────────────────
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 ─────────────────────────────
Expand Down Expand Up @@ -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}"
Expand Down Expand Up @@ -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
Expand Down
28 changes: 22 additions & 6 deletions scripts/rw_cpu_guard.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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: "<date> <time> <count>" — count is field 3
sort -k3 -rn "$proc_log" 2>/dev/null | head -5 \
| awk '{printf " %s %s procs=%s\n", $1, $2, $3}' || echo " (no data)"

echo ""
echo "── CPU steal time (last 5 vmstat samples) ──────"
Expand Down Expand Up @@ -744,7 +753,14 @@ main() {
--once) mode="once" ;;
--dry-run) DRY_RUN="1" ;;
--forensics) mode="forensics" ;;
--forensics=*) mode="forensics"; FORENSICS_DURATION="${arg#*=}" ;;
--forensics=*)
mode="forensics"
FORENSICS_DURATION="${arg#*=}"
if ! [[ "$FORENSICS_DURATION" =~ ^[0-9]+$ ]] || (( FORENSICS_DURATION == 0 )); then
echo "Invalid --forensics duration: '$FORENSICS_DURATION' (must be a positive integer number of seconds)" >&2
exit 1
fi
;;
--help|-h)
grep '^#' "$SCRIPT_PATH" 2>/dev/null | head -45 | sed 's/^# *//'
exit 0
Expand Down