upgrade.sh raw

   1  #!/bin/bash
   2  # ORLY Relay Upgrade Script
   3  #
   4  # Single-command upgrade: reads version from pkg/version/version, builds the
   5  # unified binary (with embedded web UI), deploys to the target host via rsync,
   6  # and restarts the systemd service.
   7  #
   8  # Usage:
   9  #   ./scripts/upgrade.sh                    # Build + deploy + restart relay.orly.dev
  10  #   ./scripts/upgrade.sh --dry-run          # Show what would happen, don't execute
  11  #   ./scripts/upgrade.sh --build-only       # Build locally, don't deploy
  12  #   ./scripts/upgrade.sh --host new.orly.dev # Deploy to a different host
  13  #   ./scripts/upgrade.sh --no-restart       # Deploy without restarting the service
  14  #   ./scripts/upgrade.sh --no-web           # Skip web UI rebuild (faster iteration)
  15  #   ./scripts/upgrade.sh --skip-deploy      # Build and show version, don't deploy
  16  #
  17  # The typical workflow is:
  18  #   1. Edit pkg/version/version (bump the version)
  19  #   2. Run: ./scripts/upgrade.sh
  20  #   3. The script builds, deploys, and restarts automatically.
  21  #
  22  # Architecture:
  23  #   - Builds the UNIFIED binary (./cmd/orly) which includes launcher subcommands
  24  #   - Target arch is amd64 (relay.orly.dev is x86_64, NOT arm64)
  25  #   - Web UI is rebuilt and embedded unless --no-web is passed
  26  #   - Previous binary is preserved on the server for manual rollback
  27  
  28  set -euo pipefail
  29  
  30  # --- Configuration ---
  31  SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
  32  PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
  33  VERSION_FILE="$PROJECT_DIR/pkg/version/version"
  34  
  35  DEPLOY_HOST="relay.orly.dev"
  36  DEPLOY_USER="root"
  37  DEPLOY_IP="69.164.249.71"
  38  REMOTE_BIN="/home/mleku/.local/bin/orly"
  39  SERVICE_NAME="orly"
  40  SSH_KEY="$HOME/.ssh/id_ed25519"
  41  SSH_OPTS="-i $SSH_KEY -o IdentitiesOnly=yes"
  42  
  43  # Build settings
  44  TARGET_GOOS="linux"
  45  TARGET_GOARCH="amd64"
  46  BUILD_OUTPUT="$PROJECT_DIR/orly"
  47  
  48  # Flags
  49  DRY_RUN=false
  50  BUILD_ONLY=false
  51  NO_RESTART=false
  52  NO_WEB=false
  53  
  54  # Colors
  55  RED='\033[0;31m'
  56  GREEN='\033[0;32m'
  57  YELLOW='\033[1;33m'
  58  BLUE='\033[0;34m'
  59  BOLD='\033[1m'
  60  NC='\033[0m'
  61  
  62  log()  { echo -e "${BLUE}[upgrade]${NC} $1"; }
  63  ok()   { echo -e "${GREEN}[upgrade]${NC} $1"; }
  64  warn() { echo -e "${YELLOW}[upgrade]${NC} $1"; }
  65  err()  { echo -e "${RED}[upgrade]${NC} $1" >&2; }
  66  bold() { echo -e "${BOLD}$1${NC}"; }
  67  
  68  # --- Parse arguments ---
  69  while [[ $# -gt 0 ]]; do
  70      case $1 in
  71          --dry-run)      DRY_RUN=true; shift ;;
  72          --build-only)   BUILD_ONLY=true; shift ;;
  73          --no-restart)   NO_RESTART=true; shift ;;
  74          --no-web)       NO_WEB=true; shift ;;
  75          --skip-deploy)  BUILD_ONLY=true; shift ;;
  76          --host)         DEPLOY_HOST="$2"; shift 2 ;;
  77          --help|-h)
  78              head -25 "$0" | tail -23
  79              exit 0
  80              ;;
  81          *)
  82              err "Unknown option: $1"
  83              exit 1
  84              ;;
  85      esac
  86  done
  87  
  88  # --- Preflight checks ---
  89  cd "$PROJECT_DIR"
  90  
  91  if [[ ! -f "$VERSION_FILE" ]]; then
  92      err "Version file not found: $VERSION_FILE"
  93      exit 1
  94  fi
  95  
  96  VERSION=$(cat "$VERSION_FILE" | tr -d '[:space:]')
  97  if [[ -z "$VERSION" ]]; then
  98      err "Version file is empty"
  99      exit 1
 100  fi
 101  
 102  # Check for required tools
 103  for cmd in go rsync ssh; do
 104      if ! command -v "$cmd" &>/dev/null; then
 105          err "Required command not found: $cmd"
 106          exit 1
 107      fi
 108  done
 109  
 110  if [[ "$NO_WEB" == false ]] && ! command -v bun &>/dev/null; then
 111      warn "bun not found — skipping web UI build (use --no-web to suppress this warning)"
 112      NO_WEB=true
 113  fi
 114  
 115  # Show plan
 116  echo ""
 117  bold "ORLY Upgrade Plan"
 118  echo "  Version:    $VERSION"
 119  echo "  Binary:     unified (./cmd/orly)"
 120  echo "  Target:     ${TARGET_GOOS}/${TARGET_GOARCH}"
 121  echo "  Host:       $DEPLOY_HOST"
 122  echo "  Web UI:     $(if $NO_WEB; then echo 'skip'; else echo 'rebuild'; fi)"
 123  echo "  Restart:    $(if $NO_RESTART; then echo 'no'; else echo 'yes'; fi)"
 124  if $DRY_RUN; then
 125      echo "  Mode:       DRY RUN (no changes)"
 126  fi
 127  echo ""
 128  
 129  if $DRY_RUN; then
 130      log "Dry run — exiting."
 131      exit 0
 132  fi
 133  
 134  # --- Step 1: Build web UI ---
 135  if [[ "$NO_WEB" == false ]]; then
 136      log "Building web UI..."
 137      WEB_DIR="$PROJECT_DIR/app/web"
 138      if [[ -d "$WEB_DIR" ]]; then
 139          (cd "$WEB_DIR" && bun install --silent && bun run build)
 140          ok "Web UI built"
 141      else
 142          warn "Web directory not found at $WEB_DIR — skipping"
 143      fi
 144  else
 145      log "Skipping web UI build (--no-web)"
 146  fi
 147  
 148  # --- Step 2: Build unified binary ---
 149  log "Building unified binary for ${TARGET_GOOS}/${TARGET_GOARCH}..."
 150  CGO_ENABLED=0 GOOS="$TARGET_GOOS" GOARCH="$TARGET_GOARCH" \
 151      go build -ldflags "-s -w" -o "$BUILD_OUTPUT" ./cmd/orly
 152  
 153  BINARY_SIZE=$(du -h "$BUILD_OUTPUT" | cut -f1)
 154  ok "Built: $BUILD_OUTPUT ($BINARY_SIZE)"
 155  
 156  # Verify the binary has the right architecture
 157  BINARY_ARCH=$(file "$BUILD_OUTPUT" | grep -o 'x86-64\|aarch64\|ARM' || echo 'unknown')
 158  if [[ "$TARGET_GOARCH" == "amd64" && "$BINARY_ARCH" != *"x86-64"* ]]; then
 159      warn "Binary architecture mismatch — expected x86-64, got: $BINARY_ARCH"
 160  fi
 161  
 162  if $BUILD_ONLY; then
 163      ok "Build complete. Binary: $BUILD_OUTPUT ($VERSION)"
 164      exit 0
 165  fi
 166  
 167  # --- Step 3: Deploy ---
 168  log "Stopping service on $DEPLOY_HOST..."
 169  ssh $SSH_OPTS "${DEPLOY_USER}@${DEPLOY_IP}" "systemctl stop $SERVICE_NAME" || warn "Service may not be running"
 170  
 171  log "Backing up current binary..."
 172  ssh $SSH_OPTS "${DEPLOY_USER}@${DEPLOY_IP}" \
 173      "cp -f $REMOTE_BIN ${REMOTE_BIN}.prev 2>/dev/null || true"
 174  
 175  log "Deploying $VERSION to $DEPLOY_HOST..."
 176  rsync -avz --compress -e "ssh $SSH_OPTS" \
 177      "$BUILD_OUTPUT" "${DEPLOY_USER}@${DEPLOY_IP}:${REMOTE_BIN}"
 178  
 179  # Fix ownership (we deploy as root but binary should be owned by mleku)
 180  ssh $SSH_OPTS "${DEPLOY_USER}@${DEPLOY_IP}" \
 181      "chown mleku:mleku $REMOTE_BIN && chmod +x $REMOTE_BIN"
 182  ok "Deployed to $DEPLOY_HOST:$REMOTE_BIN"
 183  
 184  # --- Step 4: Restart ---
 185  if [[ "$NO_RESTART" == false ]]; then
 186      log "Starting service..."
 187      ssh $SSH_OPTS "${DEPLOY_USER}@${DEPLOY_IP}" "systemctl start $SERVICE_NAME"
 188  
 189      # Brief pause then verify
 190      sleep 3
 191      log "Verifying..."
 192      if ssh $SSH_OPTS "${DEPLOY_USER}@${DEPLOY_IP}" "systemctl is-active $SERVICE_NAME" | grep -q "active"; then
 193          ok "Service is running"
 194      else
 195          err "Service failed to start — check: ssh ${DEPLOY_USER}@${DEPLOY_IP} 'journalctl -u $SERVICE_NAME -n 50'"
 196          err "Rollback: ssh ${DEPLOY_USER}@${DEPLOY_IP} 'cp ${REMOTE_BIN}.prev ${REMOTE_BIN} && systemctl start $SERVICE_NAME'"
 197          exit 1
 198      fi
 199  
 200      # Show version from running relay
 201      REMOTE_VERSION=$(ssh $SSH_OPTS "${DEPLOY_USER}@${DEPLOY_IP}" "$REMOTE_BIN version" 2>/dev/null || echo "unknown")
 202      ok "Remote version: $REMOTE_VERSION"
 203  else
 204      log "Skipping restart (--no-restart)"
 205      log "Start manually: ssh ${DEPLOY_USER}@${DEPLOY_IP} 'systemctl start $SERVICE_NAME'"
 206  fi
 207  
 208  echo ""
 209  ok "Upgrade complete: $VERSION deployed to $DEPLOY_HOST"
 210  echo ""
 211  echo "  Rollback:  ssh ${DEPLOY_USER}@${DEPLOY_IP} 'cp ${REMOTE_BIN}.prev ${REMOTE_BIN} && systemctl restart $SERVICE_NAME'"
 212  echo "  Logs:      ssh ${DEPLOY_USER}@${DEPLOY_IP} 'journalctl -u $SERVICE_NAME -f'"
 213  echo ""
 214