deploy.sh died at step 1 on the machine it was written on

"go: command not found". Go sits in a directory the operator's .zprofile
adds and a script does not inherit, so the very first step of the deploy
failed for a reason having nothing to do with the deploy. Found the only
way it could be - by somebody running it - and a deploy that needs the
operator to fix their environment before it works is a deploy that gets
skipped, which is the failure this script exists to end.

It now looks in the three places Go actually lands and says so plainly if
it finds none.

Step 7 also verified five routes and none of them were the nine that
shipped in the last two commits. It checks all of them now, and treats
401 as a PASS on purpose: an unauthenticated call to a route that exists
is refused, while a route the binary never registered is a 404. That
makes this step prove the ROUTING rather than the auth - which is
precisely what a deploy gets wrong, and what otherwise surfaces weeks
later as a console reporting "Backend integration required" against an
API that had already shipped. A missing route now fails the deploy loudly
instead of printing a number nobody reads.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KGcjxF1cNLcuwc3DAPcnfj
This commit is contained in:
2026-09-28 18:08:48 +05:30
parent 0e4cb1274e
commit 6fafecd6e4

View File

@@ -28,6 +28,17 @@ REMOTE_DIR=/root/behavision
PUBLIC=https://mcp.loyaly.ai
SSH=(ssh -i "$KEY" -o BatchMode=yes -o ConnectTimeout=10 "$HOST")
# Go is not always on an interactive shell's PATH - a Homebrew or tarball
# install lands in a directory that .zprofile adds but a script does not
# inherit, so this failed at step 1 with "go: command not found" on the very
# machine it was written on. Found the only way it could be: by somebody
# running it. A deploy that needs the operator to fix their environment first
# is a deploy that gets skipped.
for d in "$HOME/go/bin" /usr/local/go/bin /opt/homebrew/bin; do
[ -x "$d/go" ] && case ":$PATH:" in *":$d:"*) ;; *) PATH="$PATH:$d";; esac
done
command -v go >/dev/null || { echo "go not found - install it or add it to PATH" >&2; exit 1; }
VERSION=$(git describe --tags --always --dirty)
case "$VERSION" in *-dirty) echo "refusing to deploy uncommitted changes ($VERSION)" >&2; exit 1;; esac
@@ -65,7 +76,33 @@ step "6. Switch"
"${SSH[@]}" "cd $REMOTE_DIR && docker compose up -d --no-build --no-deps backend && sleep 4 && docker logs --tail 15 behavision-backend"
step "7. Verify over $PUBLIC"
for p in /healthz /api/admin/clients /api/team /api/visits /api/cameras; do
printf ' %-20s %s\n' "$p" "$(curl -s -o /dev/null -w '%{http_code}' -m 15 "$PUBLIC$p")"
# 401 is a PASS, and that distinction is the whole point of this step. An
# unauthenticated call to a route that EXISTS is refused; a route the binary
# never registered is a 404. So this proves the routing rather than the auth -
# which is precisely what a deploy gets wrong, and what otherwise surfaces as a
# console showing "Backend integration required" against an API that shipped.
#
# The uuid matches nothing on purpose: the admin drill-down must answer 401
# with no session, never 404.
NOBODY=00000000-0000-4000-8000-000000000000
fail=0
for p in /healthz \
/api/admin/clients \
"/api/admin/clients/$NOBODY" \
"/api/admin/clients/$NOBODY/sites" \
"/api/admin/clients/$NOBODY/sites/x/cameras" \
/api/admin/monitoring/summary \
/api/sales \
/api/sales/x \
/api/dashboard/summary \
/api/team /api/visits /api/cameras; do
code=$(curl -s -o /dev/null -w '%{http_code}' -m 15 "$PUBLIC$p")
case "$code" in
200|401) verdict="ok" ;;
404) verdict="MISSING - this binary does not serve that route"; fail=1 ;;
*) verdict="unexpected"; fail=1 ;;
esac
printf ' %-46s %s %s\n' "$p" "$code" "$verdict"
done
curl -s -m 15 "$PUBLIC/healthz" | head -c 300; echo
[ "$fail" = 0 ] || { echo; echo "VERIFY FAILED - routes above marked MISSING did not ship" >&2; exit 1; }