Files
zopu-code/deploy/zopu-runtime/scripts/update.sh
-Puter 113c755e23 fix: deployment corrections from independent review
Six fixes addressing operational correctness on a real Debian host:

1. Ownership after git operations: chown -R zopu:zopu on the checkout
   after clone/update/rollback before running bun install/build as the
   service user. .env kept at 0600 with explicit chmod after each
   operation.

2. sudo replaced with runuser: minimal Debian does not include sudo.
   runuser is part of util-linux (essential) and always available.
   All three scripts (bootstrap, update, rollback) now use runuser -u.

3. cron.d entry fixed: /etc/cron.d format requires a username field.
   Added ${SERVICE_USER} between the time fields and the command path.

4. Firewall: added explicit `ufw allow in on tailscale0` rule so the
   deny-incoming default does not block Tailscale private-overlay
   reachability. Removed inaccurate claim that direct private IP works
   by default; documented that an explicit per-interface rule is needed.

5. SupplementaryGroups=docker added to zopu-agent.service: the Orb
   sandbox runtime lives in the agent process, not just the daemon.

6. zopu-health.service: added Environment=ENV_FILE=__INSTALL_DIR__/.env
   so health-check.sh sources the correct .env at custom install paths.

Verified Flue build output: bun run build:agents produces
packages/agents/dist/server.mjs (confirmed by running the build).
Agent unit ExecStart path is correct.

Validated: bash -n on all 6 shell scripts; mocked health-check smoke
against live RivetKit engine on port 6420; systemd unit structural
checks. No JS/TS/agent files changed.
2026-07-24 20:49:54 +05:30

109 lines
3.1 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.
#
# Must be run as root (uses runuser to build as the service user).
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; }
run_as_service() {
runuser -u "$SERVICE_USER" -- "$@"
}
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}"
# Restore ownership of the checkout to the service user after git operations
log "Setting ownership of checkout to $SERVICE_USER..."
chown -R "$SERVICE_USER":"$SERVICE_USER" "$INSTALL_DIR"
# Confirm .env is intact and has correct permissions
if [[ -f "${INSTALL_DIR}/.env" ]]; then
log ".env preserved."
chmod 600 "${INSTALL_DIR}/.env"
else
err ".env is missing! Restore it from backup before starting services."
fi
# Install and build
log "Running bun install..."
run_as_service bun install
log "Building daemon binary..."
run_as_service bun run build:daemon
log "Building agent service..."
run_as_service 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