#!/usr/bin/env bash # Deploy ClawMates: build the server, frontend, node daemon, AND the agent runtime # images on the build host (tank), then load + recreate on the gateway. The agent # images (agent-base / agent-browser / agent-terminal) are locally-built dev images # — not in any registry — so a `docker pull` can't fetch them. This script loads # them onto the gateway AND every fleet node, so agent/terminal containers never # 404 with "No such image" (the failure mode this script exists to prevent). # # Usage: scripts/deploy.sh # full deploy # IMAGES_ONLY=1 scripts/deploy.sh # just (re)build + load the agent images # # Override hosts via env: BUILD_HOST, GW, NODES, TAG. set -euo pipefail BUILD_HOST=${BUILD_HOST:-osobh@tank} # builds the images + the linux daemon GW=${GW:-gw-04} # the gateway (server + frontend + postgres) NODES=${NODES:-"morpheus osobh@tank"} # fleet nodes that provision agent containers TAG=${TAG:-latest} AGENT_IMAGES=(agent-base agent-browser agent-terminal) load() { ssh "$BUILD_HOST" "docker save $1" | ssh "$2" "docker load"; } # Load only if the target lacks the exact image (skips re-transferring unchanged # multi-hundred-MB agent images to every node on each code deploy). load_if_changed() { local lid rid lid=$(ssh "$BUILD_HOST" "docker image inspect -f '{{.Id}}' $1 2>/dev/null" || true) rid=$(ssh "$2" "docker image inspect -f '{{.Id}}' $1 2>/dev/null" || true) if [ -n "$lid" ] && [ "$lid" = "$rid" ]; then echo " (unchanged — skip)" return 0 fi load "$1" "$2" } echo "→ sync to $BUILD_HOST" rsync -az --delete --exclude target/ --exclude node_modules/ --exclude .git/ \ --exclude '.next/' --exclude 'frontend/public/dl/' --exclude '**/.DS_Store' \ ./ "$BUILD_HOST":~/clawmates/ if [ -z "${IMAGES_ONLY:-}" ]; then echo "→ build server + frontend + daemon on $BUILD_HOST" ssh "$BUILD_HOST" 'set -e; cd ~/clawmates export PATH=$HOME/.cargo/bin:$PATH CARGO_NET_GIT_FETCH_WITH_CLI=true SQLX_OFFLINE=true cargo build --release -p clawmates-node cp target/release/clawmates-node frontend/public/dl/clawmates-node-linux-amd64 docker build -f images/server.Dockerfile -t clawmates/server:'"$TAG"' . docker build -f images/frontend.Dockerfile -t clawmates/frontend:'"$TAG"' .' fi echo "→ build agent runtime images on $BUILD_HOST" ssh "$BUILD_HOST" 'set -e; cd ~/clawmates for i in '"${AGENT_IMAGES[*]}"'; do docker build -f images/$i/Dockerfile -t clawmates/$i:dev images/$i/ done' if [ -z "${IMAGES_ONLY:-}" ]; then echo "→ load + recreate server + frontend on $GW" ssh "$GW" "docker tag clawmates/server:$TAG clawmates/server:rollback 2>/dev/null || true docker tag clawmates/frontend:$TAG clawmates/frontend:rollback 2>/dev/null || true" load "clawmates/server:$TAG" "$GW" load "clawmates/frontend:$TAG" "$GW" fi echo "→ load agent runtime images onto $GW + every fleet node" for img in "${AGENT_IMAGES[@]}"; do for host in "$GW" $NODES; do printf ' %-26s → %s\n' "clawmates/$img:dev" "$host" load_if_changed "clawmates/$img:dev" "$host" done done if [ -z "${IMAGES_ONLY:-}" ]; then echo "→ recreate server + frontend" ssh "$GW" "cd /root/clawmates && docker-compose -p clawmates up -d --force-recreate server frontend" sleep 6 curl -s -o /dev/null -w "edge HTTP %{http_code}\n" -m 10 https://clawmates.work/ || true fi echo "✓ deploy complete"