Lane D deliverable: reproducible execution-plane deployment on a fresh Debian dedicated server (~12 cores, 40 GB RAM, single-node). Artifacts: - bootstrap.sh: Docker Engine, Bun, repo clone, build, systemd install, firewall (deny-by-default), optional Tailscale, disk monitor cron - .env.template: all 8 environment groups documented (Convex, Gitea, model gateway, AgentOS/RivetKit, Zopu agent, daemon, Docker sandbox, service auth) - systemd units: zopu-daemon, zopu-agent (both Restart=always), zopu-health timer (60s), zopu-docker-cleanup timer (daily) - scripts: health-check, update, rollback, docker-cleanup, disk-monitor - Caddyfile: optional reverse proxy (documentation-only by default) - README.md: full runbook with start/stop/log/update/rollback/cleanup procedures, topology documentation, and boundary notes Verified topology (from installed RivetKit source): - registry.start() boots an in-process RivetKit engine (envoy mode) with a native Rust sidecar at http://localhost:6420 (DEFAULT_ENDPOINT in chunk-YDUQHING.js line 4751). createClient() connects back to it. - No separate Rivet Engine process required for single-node operation. - Linux x64 sidecar packages (@rivet-dev/agentos-sidecar-linux-x64-gnu) are available as optional dependencies. Docker / Orb boundary: - Docker Engine installed and zopu user in docker group. - No Docker-backed sandbox code wired; Orb lane contract not yet landed. - Volume pruning omitted from docker-cleanup.sh to protect durable data. Validated: bash -n on all 6 shell scripts; mocked health-check smoke (nc-based TCP probes, mocked systemctl/docker); systemd unit structural checks. No JS/TS/agent files changed.
98 lines
2.7 KiB
Bash
Executable File
98 lines
2.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# update.sh — Update the Zopu runtime to a specific branch or commit.
|
|
#
|
|
# Usage:
|
|
# update.sh # update to latest dogfood/v0
|
|
# update.sh dogfood/v0 # update to latest of branch dogfood/v0
|
|
# update.sh <commit-sha> # checkout and build a specific commit
|
|
#
|
|
# The argument is treated as a branch name first; if no matching remote
|
|
# tracking branch exists it is treated as a commit SHA.
|
|
#
|
|
# .env is gitignored and is never touched by git operations. It survives
|
|
# updates and rollbacks unchanged.
|
|
|
|
set -euo pipefail
|
|
|
|
INSTALL_DIR="${ZOPU_INSTALL_DIR:-/opt/zopu}"
|
|
SERVICE_USER="${ZOPU_SERVICE_USER:-zopu}"
|
|
TARGET="${1:-dogfood/v0}"
|
|
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
RED='\033[0;31m'
|
|
NC='\033[0m'
|
|
|
|
log() { echo -e "${GREEN}[update]${NC} $*"; }
|
|
warn() { echo -e "${YELLOW}[update]${NC} $*"; }
|
|
err() { echo -e "${RED}[update]${NC} $*" >&2; }
|
|
|
|
cd "$INSTALL_DIR"
|
|
|
|
# Record current commit for rollback
|
|
CURRENT_SHA=$(git rev-parse HEAD)
|
|
log "Current HEAD: ${CURRENT_SHA:0:12}"
|
|
echo "$CURRENT_SHA" > "${INSTALL_DIR}/.last-deployed-sha"
|
|
|
|
# Fetch all remotes
|
|
log "Fetching from origin..."
|
|
git fetch origin
|
|
|
|
# Resolve target: branch first, then commit
|
|
if git show-ref --verify --quiet "refs/remotes/origin/${TARGET}"; then
|
|
log "Target is a branch: ${TARGET}"
|
|
git checkout -B "$TARGET" "origin/${TARGET}"
|
|
elif git rev-parse --verify "${TARGET}^{commit}" &>/dev/null; then
|
|
log "Target is a commit: ${TARGET:0:12}"
|
|
git checkout "$TARGET"
|
|
else
|
|
err "'${TARGET}' is not a known branch or valid commit."
|
|
err "Available branches: $(git branch -r | tr '\n' ' ')"
|
|
exit 1
|
|
fi
|
|
|
|
NEW_SHA=$(git rev-parse HEAD)
|
|
log "Now at: ${NEW_SHA:0:12}"
|
|
|
|
# Confirm .env is intact
|
|
if [[ -f "${INSTALL_DIR}/.env" ]]; then
|
|
log ".env preserved."
|
|
else
|
|
err ".env is missing! Restore it from backup before starting services."
|
|
fi
|
|
|
|
# Install and build
|
|
log "Running bun install..."
|
|
sudo -u "$SERVICE_USER" bun install
|
|
|
|
log "Building daemon binary..."
|
|
sudo -u "$SERVICE_USER" bun run build:daemon
|
|
|
|
log "Building agent service..."
|
|
sudo -u "$SERVICE_USER" bun run build:agents
|
|
|
|
# Graceful restart: daemon first (owns RivetKit engine), then agent
|
|
log "Restarting services..."
|
|
systemctl restart zopu-daemon
|
|
sleep 3
|
|
systemctl restart zopu-agent
|
|
sleep 5
|
|
|
|
# Health check
|
|
HEALTH_SCRIPT="${INSTALL_DIR}/deploy/zopu-runtime/scripts/health-check.sh"
|
|
if [[ -x "$HEALTH_SCRIPT" ]]; then
|
|
log "Running health check..."
|
|
if "$HEALTH_SCRIPT"; then
|
|
log "Update complete and healthy."
|
|
else
|
|
err "Health check failed after update!"
|
|
err " journalctl -u zopu-daemon -n 50"
|
|
err " journalctl -u zopu-agent -n 50"
|
|
err "To rollback: ${INSTALL_DIR}/deploy/zopu-runtime/scripts/rollback.sh"
|
|
exit 1
|
|
fi
|
|
else
|
|
log "Update complete. Verify manually."
|
|
fi
|