Monorepo for Aesthetic.Computer aesthetic.computer
at main 334 lines 12 kB view raw
1#!/bin/bash 2# Fast oven deploy with verbose output 3# Usage: ./deploy.sh [--no-restart] 4 5set -e 6 7OVEN_HOST="137.184.237.166" 8SSH_KEY="${SSH_KEY:-$(dirname "$0")/../aesthetic-computer-vault/oven/ssh/oven-deploy-key}" 9REMOTE_DIR="/opt/oven" 10NATIVE_GIT_FETCH_URL="${NATIVE_GIT_FETCH_URL:-https://tangled.org/aesthetic.computer/core.git}" 11SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" 12AC_SOURCE="$SCRIPT_DIR/../system/public/aesthetic.computer" 13FEDAC_SOURCE="$SCRIPT_DIR/../fedac" 14VAULT_OS_KEY="$SCRIPT_DIR/../aesthetic-computer-vault/oven/os-build-admin-key.txt" 15 16echo "🚀 Deploying oven..." 17echo " Host: $OVEN_HOST" 18echo " Key: $SSH_KEY" 19 20# Get current git version for OVEN_VERSION env var 21GIT_VERSION=$(git rev-parse --short HEAD 2>/dev/null || echo "unknown") 22echo " Version: $GIT_VERSION" 23 24# Time the rsync 25START_TIME=$(date +%s%3N) 26 27echo "" 28echo "📦 Syncing oven files..." 29rsync -avz --progress --delete \ 30 --exclude='node_modules' \ 31 --exclude='.git' \ 32 --exclude='*.log' \ 33 --exclude='ac-source' \ 34 --exclude='native-git' \ 35 --exclude='secrets' \ 36 -e "ssh -i $SSH_KEY -o StrictHostKeyChecking=no" \ 37 "$SCRIPT_DIR/" \ 38 "root@$OVEN_HOST:$REMOTE_DIR/" 39 40END_SYNC=$(date +%s%3N) 41SYNC_TIME=$((END_SYNC - START_TIME)) 42echo "" 43echo "✅ Oven sync complete in ${SYNC_TIME}ms" 44 45# Sync aesthetic.computer source files needed for bundle generation 46echo "" 47echo "📦 Syncing ac-source files for bundler..." 48rsync -avz --progress --delete \ 49 --include='*/' \ 50 --include='*.mjs' \ 51 --include='*.js' \ 52 --include='*.json' \ 53 --include='*.lisp' \ 54 --exclude='*' \ 55 -e "ssh -i $SSH_KEY -o StrictHostKeyChecking=no" \ 56 "$AC_SOURCE/" \ 57 "root@$OVEN_HOST:$REMOTE_DIR/ac-source/" 58 59END_AC_SYNC=$(date +%s%3N) 60AC_SYNC_TIME=$((END_AC_SYNC - END_SYNC)) 61echo "" 62echo "✅ ac-source sync complete in ${AC_SYNC_TIME}ms" 63 64# Sync fedac scripts/overlays used by background OS base-image build jobs 65echo "" 66echo "📦 Syncing fedac OS build pipeline..." 67rsync -avz --progress --delete \ 68 --exclude='.git' \ 69 --exclude='*.img' \ 70 --exclude='*.iso' \ 71 --exclude='*.qcow2' \ 72 --exclude='*.log' \ 73 --exclude='native/build/' \ 74 -e "ssh -i $SSH_KEY -o StrictHostKeyChecking=no" \ 75 "$FEDAC_SOURCE/" \ 76 "root@$OVEN_HOST:$REMOTE_DIR/fedac/" 77 78END_FEDAC_SYNC=$(date +%s%3N) 79FEDAC_SYNC_TIME=$((END_FEDAC_SYNC - END_AC_SYNC)) 80echo "" 81echo "✅ fedac sync complete in ${FEDAC_SYNC_TIME}ms" 82 83# Install kernel build tools needed for native OTA builds (idempotent) 84echo "" 85echo "🔧 Installing native kernel build tools..." 86ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "root@$OVEN_HOST" \ 87 "apt-get install -y -q gcc make flex bison libelf-dev libssl-dev bc cpio lz4 musl-tools python3 pahole libdrm-dev libasound2-dev flite1-dev pkg-config dosfstools mtools util-linux 2>&1 | tail -5 || true" 88echo "✅ Kernel build tools ready" 89 90# Install Nix package manager for NixOS-based native builds (idempotent) 91echo "" 92echo "❄️ Installing Nix package manager..." 93ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "root@$OVEN_HOST" bash -s <<'NIX_EOF' 94 if command -v nix >/dev/null 2>&1; then 95 echo "Nix already installed: $(nix --version)" 96 else 97 curl -sSf -L https://install.determinate.systems/nix | sh -s -- install --no-confirm 2>&1 | tail -10 98 fi 99 NIX_BIN="" 100 for candidate in \ 101 /nix/var/nix/profiles/default/bin/nix \ 102 /root/.nix-profile/bin/nix \ 103 /home/oven/.nix-profile/bin/nix; do 104 if [ -x "$candidate" ]; then 105 NIX_BIN="$candidate" 106 break 107 fi 108 done 109 if [ -n "$NIX_BIN" ]; then 110 ln -sf "$NIX_BIN" /usr/local/bin/nix 111 NIX_GC_BIN="$(dirname "$NIX_BIN")/nix-collect-garbage" 112 if [ -x "$NIX_GC_BIN" ]; then 113 ln -sf "$NIX_GC_BIN" /usr/local/bin/nix-collect-garbage 114 fi 115 echo "Nix binary: $NIX_BIN" 116 else 117 echo "WARNING: nix binary not found after install" 118 fi 119 if id -u oven >/dev/null 2>&1; then 120 mkdir -p /home/oven/.cache/nix 121 chown -R oven:oven /home/oven/.cache 122 fi 123 # Enable flakes 124 mkdir -p /etc/nix 125 grep -q 'experimental-features' /etc/nix/nix.conf 2>/dev/null || \ 126 echo 'experimental-features = nix-command flakes' >> /etc/nix/nix.conf 127 # Garbage collection timer (weekly, keep 7 days) 128 if ! systemctl is-enabled nix-gc.timer >/dev/null 2>&1; then 129 cat > /etc/systemd/system/nix-gc.service <<'SVC' 130[Unit] 131Description=Nix store garbage collection 132[Service] 133Type=oneshot 134ExecStart=/nix/var/nix/profiles/default/bin/nix-collect-garbage --delete-older-than 7d 135SVC 136 cat > /etc/systemd/system/nix-gc.timer <<'TMR' 137[Unit] 138Description=Weekly Nix garbage collection 139[Timer] 140OnCalendar=weekly 141Persistent=true 142[Install] 143WantedBy=timers.target 144TMR 145 systemctl daemon-reload 146 systemctl enable --now nix-gc.timer 147 fi 148NIX_EOF 149echo "✅ Nix ready" 150 151# Install TeX Live for papers PDF builds (idempotent) 152echo "" 153echo "📄 Installing TeX Live for papers builds..." 154ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "root@$OVEN_HOST" \ 155 "apt-get install -y -q texlive-xetex texlive-fonts-extra texlive-latex-extra texlive-bibtex-extra texlive-lang-chinese texlive-lang-cjk fonts-droid-fallback 2>&1 | tail -5 || true" 156echo "✅ TeX Live ready" 157 158# Optional vault-managed admin key for /os-base-build endpoints 159echo "" 160if [ -f "$VAULT_OS_KEY" ]; then 161 echo "🔐 Syncing OS build admin key from vault..." 162 ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "root@$OVEN_HOST" " 163mkdir -p $REMOTE_DIR/secrets 164chmod 700 $REMOTE_DIR/secrets 165if id -u oven >/dev/null 2>&1; then 166 chown oven:oven $REMOTE_DIR/secrets 167fi 168" 169 rsync -avz --progress \ 170 -e "ssh -i $SSH_KEY -o StrictHostKeyChecking=no" \ 171 "$VAULT_OS_KEY" \ 172 "root@$OVEN_HOST:$REMOTE_DIR/secrets/os-build-admin-key.txt" 173 ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "root@$OVEN_HOST" " 174chmod 600 $REMOTE_DIR/secrets/os-build-admin-key.txt 175if id -u oven >/dev/null 2>&1; then 176 chown oven:oven $REMOTE_DIR/secrets/os-build-admin-key.txt 177fi 178if grep -q '^OS_BUILD_ADMIN_KEY_FILE=' $REMOTE_DIR/.env 2>/dev/null; then 179 sed -i 's|^OS_BUILD_ADMIN_KEY_FILE=.*|OS_BUILD_ADMIN_KEY_FILE=$REMOTE_DIR/secrets/os-build-admin-key.txt|' $REMOTE_DIR/.env 180else 181 echo 'OS_BUILD_ADMIN_KEY_FILE=$REMOTE_DIR/secrets/os-build-admin-key.txt' >> $REMOTE_DIR/.env 182fi 183" 184 echo "✅ OS build admin key synced" 185else 186 echo "⚠️ No vault key at $VAULT_OS_KEY (skipping OS_BUILD_ADMIN_KEY_FILE provisioning)" 187fi 188 189END_SECRET_SYNC=$(date +%s%3N) 190SECRET_SYNC_TIME=$((END_SECRET_SYNC - END_FEDAC_SYNC)) 191echo "✅ Secret sync stage complete in ${SECRET_SYNC_TIME}ms" 192 193# Sync BDF font files + glyph caches for bundle font embedding 194echo "" 195echo "📦 Syncing font assets (BDF + glyph caches)..." 196rsync -avz --progress \ 197 --include='*/' \ 198 --include='*.bdf' \ 199 --include='*.bdf.gz' \ 200 --include='*.json' \ 201 --exclude='*' \ 202 -e "ssh -i $SSH_KEY -o StrictHostKeyChecking=no" \ 203 "$SCRIPT_DIR/../system/public/assets/type/" \ 204 "root@$OVEN_HOST:$REMOTE_DIR/assets-type/" 205 206END_FONT_SYNC=$(date +%s%3N) 207FONT_SYNC_TIME=$((END_FONT_SYNC - END_SECRET_SYNC)) 208echo "" 209echo "✅ Font glyph sync complete in ${FONT_SYNC_TIME}ms" 210 211# Ensure OS cache path is writable by the oven service user. 212echo "" 213echo "🧹 Ensuring OS cache directory + permissions..." 214ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "root@$OVEN_HOST" " 215mkdir -p $REMOTE_DIR/cache 216if id -u oven >/dev/null 2>&1; then 217 chown -R oven:oven $REMOTE_DIR 218fi 219if grep -q '^OS_CACHE_DIR=' $REMOTE_DIR/.env 2>/dev/null; then 220 sed -i 's|^OS_CACHE_DIR=.*|OS_CACHE_DIR=$REMOTE_DIR/cache|' $REMOTE_DIR/.env 221else 222 echo 'OS_CACHE_DIR=$REMOTE_DIR/cache' >> $REMOTE_DIR/.env 223fi 224" 225echo "✅ OS cache path ready: $REMOTE_DIR/cache" 226 227# Set up native git repo for auto-polling OTA builds 228echo "" 229echo "📦 Setting up native git repo for OTA auto-builds..." 230echo " Fetch remote: $NATIVE_GIT_FETCH_URL" 231ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "root@$OVEN_HOST" " 232set -euo pipefail 233NATIVE_GIT_DIR=/opt/oven/native-git 234NATIVE_GIT_FETCH_URL='$NATIVE_GIT_FETCH_URL' 235if [ ! -d \$NATIVE_GIT_DIR/.git ]; then 236 echo ' Cloning repo (first time)...' 237 git clone --branch main --single-branch \$NATIVE_GIT_FETCH_URL \$NATIVE_GIT_DIR 238else 239 echo ' Git repo exists, fetching latest...' 240 cd \$NATIVE_GIT_DIR 241 git remote set-url origin \$NATIVE_GIT_FETCH_URL 242 git fetch origin main --quiet || true 243 git branch --set-upstream-to=origin/main main >/dev/null 2>&1 || true 244 if git rev-parse --verify origin/main >/dev/null 2>&1 && git merge-base --is-ancestor HEAD origin/main; then 245 git merge origin/main --ff-only --quiet || true 246 else 247 echo ' Repo has local commits or divergence; leaving checkout as-is (native build preflight will hard-sync to origin/main).' 248 fi 249fi 250if id -u oven >/dev/null 2>&1; then 251 chown -R oven:oven \$NATIVE_GIT_DIR 252 su - oven -s /bin/bash -c 'git config --global --add safe.directory /opt/oven/native-git' 2>/dev/null || true 253fi 254echo ' Done.' 255" 256echo "✅ Native git repo ready" 257 258# Configure git push credentials for papers PDF auto-push 259echo "" 260echo "🔑 Configuring git push credentials for papers auto-build..." 261ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "root@$OVEN_HOST" " 262NATIVE_GIT_DIR=/opt/oven/native-git 263# Read OVEN_GH_TOKEN from .env if it exists 264OVEN_GH_TOKEN=\$(grep '^OVEN_GH_TOKEN=' $REMOTE_DIR/.env 2>/dev/null | cut -d= -f2-) 265if [ -n \"\$OVEN_GH_TOKEN\" ]; then 266 cd \$NATIVE_GIT_DIR 267 # Keep fetch on Tangled, but push papers auto-build commits to the GitHub mirror. 268 git remote set-url --push origin https://x-access-token:\${OVEN_GH_TOKEN}@github.com/whistlegraph/aesthetic-computer.git 269 echo ' Push URL configured with token auth' 270else 271 echo ' WARNING: OVEN_GH_TOKEN not found in $REMOTE_DIR/.env — papers auto-push will fail' 272 echo ' Add OVEN_GH_TOKEN=ghp_... to aesthetic-computer-vault/oven/.env and re-deploy' 273fi 274" 275echo "✅ Git push credentials configured" 276 277# Restart unless --no-restart flag 278if [ "$1" != "--no-restart" ]; then 279 echo "" 280 echo "🔄 Restarting oven service..." 281 282 # Update OVEN_VERSION in .env and rewrite the managed systemd override, then restart 283 ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "root@$OVEN_HOST" " 284set -euo pipefail 285cd $REMOTE_DIR 286# Update version in .env 287if grep -q '^OVEN_VERSION=' .env 2>/dev/null; then 288 sed -i 's/^OVEN_VERSION=.*/OVEN_VERSION=$GIT_VERSION/' .env 289else 290 echo 'OVEN_VERSION=$GIT_VERSION' >> .env 291fi 292install -m 0644 $REMOTE_DIR/infra/oven.service /etc/systemd/system/oven.service 293# Rewrite systemd override from scratch so stale directives do not survive deploys. 294mkdir -p /etc/systemd/system/oven.service.d 295cat > /etc/systemd/system/oven.service.d/override.conf <<EOF 296[Service] 297Environment=OVEN_VERSION=$GIT_VERSION 298Environment=PATH=/usr/local/bin:/nix/var/nix/profiles/default/bin:/home/oven/.nix-profile/bin:/root/.nix-profile/bin:/usr/local/sbin:/usr/sbin:/usr/bin:/sbin:/bin 299LimitNOFILE=65536 300EOF 301systemctl daemon-reload 302systemctl restart oven 303sleep 2 304systemctl is-active --quiet oven.service 305systemctl status oven.service --no-pager | sed -n '1,5p' 306" 307 308 END_RESTART=$(date +%s%3N) 309 RESTART_TIME=$((END_RESTART - END_FONT_SYNC)) 310 311 echo "" 312 echo "✅ Restart complete in ${RESTART_TIME}ms" 313 314 # Prewarm the bundle cache after restart 315 echo "" 316 echo "🔥 Prewarming bundle cache..." 317 PREWARM_RESULT=$(ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "root@$OVEN_HOST" \ 318 "curl -s -X POST http://localhost:3002/bundle-prewarm --max-time 120" 2>/dev/null || echo '{"error":"prewarm timeout"}') 319 echo " $PREWARM_RESULT" 320 321 END_PREWARM=$(date +%s%3N) 322 PREWARM_TIME=$((END_PREWARM - END_RESTART)) 323 TOTAL_TIME=$((END_PREWARM - START_TIME)) 324 325 echo "" 326 echo "✅ Prewarm complete in ${PREWARM_TIME}ms" 327 echo "🏁 Total deploy time: ${TOTAL_TIME}ms" 328else 329 echo "" 330 echo "⏭️ Skipped restart (--no-restart)" 331fi 332 333echo "" 334echo "🔥 Done! https://oven.aesthetic.computer"