#!/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 # 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 web app..." run_as_service bun run --cwd apps/web build log "Validating daemon production build..." 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 and web log "Restarting services..." systemctl restart zopu-daemon sleep 3 systemctl restart zopu-agent systemctl restart zopu-web 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 " journalctl -u zopu-web -n 50" err "To rollback: ${INSTALL_DIR}/deploy/zopu-runtime/scripts/rollback.sh" exit 1 fi else log "Update complete. Verify manually." fi