#!/usr/bin/env bash # deploy.sh — Deploy Aether Native Launcher to onsite Mac laptops # # USAGE: # ./deploy.sh [num ...] Deploy to one or more laptops (e.g. 03 04 05) # ./deploy.sh all Deploy to all laptops in devices.conf # ./deploy.sh --seed-only Update seed.json only — skip .app copy # ./deploy.sh --seed-only all # ./deploy.sh --build [num ...] Build first (npm run package:mac), then deploy # ./deploy.sh --build all # # REQUIRES: # event.env — copy from event.env.example and fill in AETHER_API_KEY # builds/ — pre-built, or use --build to build before deploying # # SSH keys must already be installed on each target (run ssh-copy-id once per laptop). set -uo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" DEVICES_FILE="$SCRIPT_DIR/devices.conf" EVENT_ENV="$SCRIPT_DIR/event.env" SSH_USER="speaker ready" BUILD_DIR="$SCRIPT_DIR/../builds" # ── Argument parsing ────────────────────────────────────────────────────────── SEED_ONLY=false BUILD_FIRST=false TARGETS=() usage() { grep '^#' "$0" | grep -v '^#!/' | sed 's/^# \{0,1\}//' exit 1 } if [[ $# -eq 0 ]]; then usage; fi while [[ $# -gt 0 ]]; do case "$1" in --seed-only) SEED_ONLY=true ;; --build) BUILD_FIRST=true ;; --help|-h) usage ;; all) TARGETS+=("all") ;; *) TARGETS+=("$1") ;; esac shift done if [[ ${#TARGETS[@]} -eq 0 ]]; then echo "ERROR: No targets specified." usage fi # ── Load event config ───────────────────────────────────────────────────────── if [[ ! -f "$EVENT_ENV" ]]; then echo "ERROR: $EVENT_ENV not found." echo " Copy event.env.example → event.env and fill in AETHER_API_KEY." exit 1 fi # shellcheck source=/dev/null source "$EVENT_ENV" : "${AETHER_API_KEY:?event.env must set AETHER_API_KEY}" : "${PRIMARY_API_BASE_URL:?event.env must set PRIMARY_API_BASE_URL}" : "${BACKUP_API_BASE_URL:?event.env must set BACKUP_API_BASE_URL}" # Render onsite URL as JSON null or quoted string if [[ -n "${ONSITE_API_BASE_URL:-}" ]]; then ONSITE_JSON="\"$ONSITE_API_BASE_URL\"" else ONSITE_JSON="null" fi # ── Device list helpers ─────────────────────────────────────────────────────── # Returns "ip device_id" for a laptop number, or empty if not found lookup_device() { local num="$1" grep -v '^[[:space:]]*#' "$DEVICES_FILE" \ | grep -E "^[[:space:]]*${num}[[:space:]]" \ | awk '{print $2, $3}' \ | head -1 } # Returns all laptop numbers from devices.conf (first column, non-comment lines) all_device_nums() { grep -v '^[[:space:]]*#' "$DEVICES_FILE" \ | grep -v '^[[:space:]]*$' \ | awk '{print $1}' } # ── Deploy one laptop ───────────────────────────────────────────────────────── deploy_laptop() { local num="$1" local info info=$(lookup_device "$num") if [[ -z "$info" ]]; then echo " ERROR: Laptop $num not found in devices.conf" return 1 fi local ip device_id ip=$(echo "$info" | awk '{print $1}') device_id=$(echo "$info" | awk '{print $2}') echo "" echo "══════════════════════════════════════════════" echo " Laptop $num · $ip · $device_id" echo "══════════════════════════════════════════════" # ── Copy .app ────────────────────────────────────────────────────────── if [[ "$SEED_ONLY" != "true" ]]; then echo " Detecting architecture..." local arch arch=$(ssh "$SSH_USER@$ip" "uname -m" 2>/dev/null) || { echo " ERROR: SSH failed for $ip — is the laptop on the network?" return 1 } local bundle case "$arch" in x86_64) bundle="$BUILD_DIR/aether_launcher-darwin-x64/aether_launcher.app" ;; arm64) bundle="$BUILD_DIR/aether_launcher-darwin-arm64/aether_launcher.app" ;; *) echo " ERROR: Unknown arch '$arch' on $ip" return 1 ;; esac if [[ ! -d "$bundle" ]]; then echo " ERROR: Build not found: $bundle" echo " Run: npm run package:mac" return 1 fi echo " Arch: $arch → syncing $(basename "$bundle")..." # rsync --delete syncs contents in-place without removing the top-level .app dir. # This preserves the inode so macOS Aliases and Desktop shortcuts keep working. rsync -a --delete -e ssh "$bundle/" "$SSH_USER@$ip:/Applications/aether_launcher.app/" || { echo " ERROR: rsync failed." return 1 } echo " .app synced." else echo " (--seed-only: skipping .app copy)" fi # ── Write seed.json ──────────────────────────────────────────────────── echo " Writing seed.json..." ssh "$SSH_USER@$ip" "cat > ~/seed.json" <