summaryrefslogtreecommitdiff
path: root/scripts/verify-deploy
diff options
context:
space:
mode:
authorPeter Stone <thepeterstone@gmail.com>2026-09-20 00:34:23 +0000
committerPeter Stone <thepeterstone@gmail.com>2026-09-20 00:34:23 +0000
commitc58f7519936dc547ee2aec5bfbb1e477356656d9 (patch)
treeaca675adcb1e77926258961edd656c4c0ec16d2c /scripts/verify-deploy
parentde3a2ba7280ab66cc4ec05a80925291b9648706d (diff)
Verify deploys, and add a self-recovering health check
Deploy used to end with `systemctl status`, which a human skims and a script ignores: it exits zero whether or not anything works, and says nothing about the sub-services behind the gateway. A deploy could therefore report success while /scout/ served 502s — which is exactly what happened, undetected, for three and a half weeks. scripts/deploy now: * syncs scripts/ to ${SITE_DIR}/app-scripts/ so the timer runs the deployed version from a stable path * restarts sub-services that are not active. They are separate units and a doot deploy does not touch them, so one sitting in `failed` after exhausting its restart limit stays down through any number of green deploys. * calls scripts/verify-deploy, which asserts and exits non-zero scripts/verify-deploy asserts all five binaries are executable, all four units are active, all four gateway routes answer without a 5xx, and the running build stamp matches the commit just built. scripts/health-check runs the same assertions on a 5-minute timer and recovers what it can: chmod +x a binary that lost its execute bit, reset-failed and start a unit that gave up. There is no mail/ntfy/apprise on this host, so alerting is a greppable HEALTH ALERT line in the journal, a status JSON at data/health-status.json for the dashboard to surface later, and a non-zero exit so the unit shows in `systemctl --failed`. OnBootSec=2min because a reboot is when this failure mode first appeared. Shared assertions live in scripts/lib/site-checks.sh so verify-deploy and health-check cannot drift apart. Two bugs were found and fixed in these checks by testing their failure paths, both of which would have made them report a broken site as healthy: * `$(curl ... || echo 000)` concatenates curl's own "000" transport-error output with the echoed one, giving "000000", which matched no case arm and was reported ok. * check_services sets FAILED_SERVICES, but health-check captured it with `$(...)`, so the array was populated in a subshell and discarded — silently disabling service recovery entirely. Both the calling convention and the subshell semantics are now pinned by tests. scripts/test-health-checks.sh: 9 cases, mostly negative, against a temp tree. Recovery additionally verified live on this host by stopping hawaii.service and by clearing scout's execute bit; both were detected, recovered and re-verified. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018AwnALZL6bD4VBtATDVv3B
Diffstat (limited to 'scripts/verify-deploy')
-rwxr-xr-xscripts/verify-deploy49
1 files changed, 49 insertions, 0 deletions
diff --git a/scripts/verify-deploy b/scripts/verify-deploy
new file mode 100755
index 0000000..712c26d
--- /dev/null
+++ b/scripts/verify-deploy
@@ -0,0 +1,49 @@
+#!/bin/bash
+#
+# Assert that the site is actually healthy. Run automatically at the end of
+# scripts/deploy, and usable standalone at any time.
+#
+# scripts/deploy used to finish with `systemctl status`, which a human reads and
+# a script ignores — it exits zero whether or not anything works, and it says
+# nothing at all about the sub-services behind the gateway. A deploy could
+# therefore "succeed" while /scout/ returned 502, which is precisely what
+# happened for three and a half weeks.
+#
+# Usage:
+# scripts/verify-deploy [expected-build-commit]
+#
+# Exit: 0 everything healthy, 1 something is not.
+
+set -uo pipefail
+
+cd "$(dirname "$0")/.."
+# shellcheck source=scripts/lib/site-checks.sh
+. "$(dirname "$0")/lib/site-checks.sh"
+
+expected_build=${1:-}
+rc=0
+
+echo "==> Verifying binaries are executable..."
+check_binaries || rc=1
+
+echo "==> Verifying services are active..."
+check_services || rc=1
+
+echo "==> Verifying gateway routes..."
+check_routes || rc=1
+
+if [ -n "$expected_build" ]; then
+ echo "==> Verifying deployed build..."
+ check_build_stamp "$expected_build" || rc=1
+fi
+
+echo
+if [ $rc -eq 0 ]; then
+ echo "==> Verification PASSED"
+else
+ echo "==> Verification FAILED — the site is not fully healthy" >&2
+ echo " Sub-services are started separately from the main app; a failed" >&2
+ echo " unit that has exhausted its restart limit needs:" >&2
+ echo " systemctl reset-failed <unit> && systemctl start <unit>" >&2
+fi
+exit $rc