#!/bin/bash # Fast oven deploy with verbose output # Usage: ./deploy.sh [--no-restart] set -e OVEN_HOST="137.184.237.166" SSH_KEY="${SSH_KEY:-$(dirname "$0")/../aesthetic-computer-vault/oven/ssh/oven-deploy-key}" REMOTE_DIR="/opt/oven" NATIVE_GIT_FETCH_URL="${NATIVE_GIT_FETCH_URL:-https://tangled.org/aesthetic.computer/core.git}" SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" AC_SOURCE="$SCRIPT_DIR/../system/public/aesthetic.computer" FEDAC_SOURCE="$SCRIPT_DIR/../fedac" VAULT_OS_KEY="$SCRIPT_DIR/../aesthetic-computer-vault/oven/os-build-admin-key.txt" echo "๐Ÿš€ Deploying oven..." echo " Host: $OVEN_HOST" echo " Key: $SSH_KEY" # Get current git version for OVEN_VERSION env var GIT_VERSION=$(git rev-parse --short HEAD 2>/dev/null || echo "unknown") echo " Version: $GIT_VERSION" # Time the rsync START_TIME=$(date +%s%3N) echo "" echo "๐Ÿ“ฆ Syncing oven files..." rsync -avz --progress --delete \ --exclude='node_modules' \ --exclude='.git' \ --exclude='*.log' \ --exclude='ac-source' \ --exclude='native-git' \ --exclude='secrets' \ -e "ssh -i $SSH_KEY -o StrictHostKeyChecking=no" \ "$SCRIPT_DIR/" \ "root@$OVEN_HOST:$REMOTE_DIR/" END_SYNC=$(date +%s%3N) SYNC_TIME=$((END_SYNC - START_TIME)) echo "" echo "โœ… Oven sync complete in ${SYNC_TIME}ms" # Sync aesthetic.computer source files needed for bundle generation echo "" echo "๐Ÿ“ฆ Syncing ac-source files for bundler..." rsync -avz --progress --delete \ --include='*/' \ --include='*.mjs' \ --include='*.js' \ --include='*.json' \ --include='*.lisp' \ --exclude='*' \ -e "ssh -i $SSH_KEY -o StrictHostKeyChecking=no" \ "$AC_SOURCE/" \ "root@$OVEN_HOST:$REMOTE_DIR/ac-source/" END_AC_SYNC=$(date +%s%3N) AC_SYNC_TIME=$((END_AC_SYNC - END_SYNC)) echo "" echo "โœ… ac-source sync complete in ${AC_SYNC_TIME}ms" # Sync fedac scripts/overlays used by background OS base-image build jobs echo "" echo "๐Ÿ“ฆ Syncing fedac OS build pipeline..." rsync -avz --progress --delete \ --exclude='.git' \ --exclude='*.img' \ --exclude='*.iso' \ --exclude='*.qcow2' \ --exclude='*.log' \ --exclude='native/build/' \ -e "ssh -i $SSH_KEY -o StrictHostKeyChecking=no" \ "$FEDAC_SOURCE/" \ "root@$OVEN_HOST:$REMOTE_DIR/fedac/" END_FEDAC_SYNC=$(date +%s%3N) FEDAC_SYNC_TIME=$((END_FEDAC_SYNC - END_AC_SYNC)) echo "" echo "โœ… fedac sync complete in ${FEDAC_SYNC_TIME}ms" # Install kernel build tools needed for native OTA builds (idempotent) echo "" echo "๐Ÿ”ง Installing native kernel build tools..." ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "root@$OVEN_HOST" \ "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" echo "โœ… Kernel build tools ready" # Install Nix package manager for NixOS-based native builds (idempotent) echo "" echo "โ„๏ธ Installing Nix package manager..." ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "root@$OVEN_HOST" bash -s <<'NIX_EOF' if command -v nix >/dev/null 2>&1; then echo "Nix already installed: $(nix --version)" else curl -sSf -L https://install.determinate.systems/nix | sh -s -- install --no-confirm 2>&1 | tail -10 fi NIX_BIN="" for candidate in \ /nix/var/nix/profiles/default/bin/nix \ /root/.nix-profile/bin/nix \ /home/oven/.nix-profile/bin/nix; do if [ -x "$candidate" ]; then NIX_BIN="$candidate" break fi done if [ -n "$NIX_BIN" ]; then ln -sf "$NIX_BIN" /usr/local/bin/nix NIX_GC_BIN="$(dirname "$NIX_BIN")/nix-collect-garbage" if [ -x "$NIX_GC_BIN" ]; then ln -sf "$NIX_GC_BIN" /usr/local/bin/nix-collect-garbage fi echo "Nix binary: $NIX_BIN" else echo "WARNING: nix binary not found after install" fi if id -u oven >/dev/null 2>&1; then mkdir -p /home/oven/.cache/nix chown -R oven:oven /home/oven/.cache fi # Enable flakes mkdir -p /etc/nix grep -q 'experimental-features' /etc/nix/nix.conf 2>/dev/null || \ echo 'experimental-features = nix-command flakes' >> /etc/nix/nix.conf # Garbage collection timer (weekly, keep 7 days) if ! systemctl is-enabled nix-gc.timer >/dev/null 2>&1; then cat > /etc/systemd/system/nix-gc.service <<'SVC' [Unit] Description=Nix store garbage collection [Service] Type=oneshot ExecStart=/nix/var/nix/profiles/default/bin/nix-collect-garbage --delete-older-than 7d SVC cat > /etc/systemd/system/nix-gc.timer <<'TMR' [Unit] Description=Weekly Nix garbage collection [Timer] OnCalendar=weekly Persistent=true [Install] WantedBy=timers.target TMR systemctl daemon-reload systemctl enable --now nix-gc.timer fi NIX_EOF echo "โœ… Nix ready" # Install TeX Live for papers PDF builds (idempotent) echo "" echo "๐Ÿ“„ Installing TeX Live for papers builds..." ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "root@$OVEN_HOST" \ "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" echo "โœ… TeX Live ready" # Optional vault-managed admin key for /os-base-build endpoints echo "" if [ -f "$VAULT_OS_KEY" ]; then echo "๐Ÿ” Syncing OS build admin key from vault..." ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "root@$OVEN_HOST" " mkdir -p $REMOTE_DIR/secrets chmod 700 $REMOTE_DIR/secrets if id -u oven >/dev/null 2>&1; then chown oven:oven $REMOTE_DIR/secrets fi " rsync -avz --progress \ -e "ssh -i $SSH_KEY -o StrictHostKeyChecking=no" \ "$VAULT_OS_KEY" \ "root@$OVEN_HOST:$REMOTE_DIR/secrets/os-build-admin-key.txt" ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "root@$OVEN_HOST" " chmod 600 $REMOTE_DIR/secrets/os-build-admin-key.txt if id -u oven >/dev/null 2>&1; then chown oven:oven $REMOTE_DIR/secrets/os-build-admin-key.txt fi if grep -q '^OS_BUILD_ADMIN_KEY_FILE=' $REMOTE_DIR/.env 2>/dev/null; then sed -i 's|^OS_BUILD_ADMIN_KEY_FILE=.*|OS_BUILD_ADMIN_KEY_FILE=$REMOTE_DIR/secrets/os-build-admin-key.txt|' $REMOTE_DIR/.env else echo 'OS_BUILD_ADMIN_KEY_FILE=$REMOTE_DIR/secrets/os-build-admin-key.txt' >> $REMOTE_DIR/.env fi " echo "โœ… OS build admin key synced" else echo "โš ๏ธ No vault key at $VAULT_OS_KEY (skipping OS_BUILD_ADMIN_KEY_FILE provisioning)" fi END_SECRET_SYNC=$(date +%s%3N) SECRET_SYNC_TIME=$((END_SECRET_SYNC - END_FEDAC_SYNC)) echo "โœ… Secret sync stage complete in ${SECRET_SYNC_TIME}ms" # Sync BDF font files + glyph caches for bundle font embedding echo "" echo "๐Ÿ“ฆ Syncing font assets (BDF + glyph caches)..." rsync -avz --progress \ --include='*/' \ --include='*.bdf' \ --include='*.bdf.gz' \ --include='*.json' \ --exclude='*' \ -e "ssh -i $SSH_KEY -o StrictHostKeyChecking=no" \ "$SCRIPT_DIR/../system/public/assets/type/" \ "root@$OVEN_HOST:$REMOTE_DIR/assets-type/" END_FONT_SYNC=$(date +%s%3N) FONT_SYNC_TIME=$((END_FONT_SYNC - END_SECRET_SYNC)) echo "" echo "โœ… Font glyph sync complete in ${FONT_SYNC_TIME}ms" # Ensure OS cache path is writable by the oven service user. echo "" echo "๐Ÿงน Ensuring OS cache directory + permissions..." ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "root@$OVEN_HOST" " mkdir -p $REMOTE_DIR/cache if id -u oven >/dev/null 2>&1; then chown -R oven:oven $REMOTE_DIR fi if grep -q '^OS_CACHE_DIR=' $REMOTE_DIR/.env 2>/dev/null; then sed -i 's|^OS_CACHE_DIR=.*|OS_CACHE_DIR=$REMOTE_DIR/cache|' $REMOTE_DIR/.env else echo 'OS_CACHE_DIR=$REMOTE_DIR/cache' >> $REMOTE_DIR/.env fi " echo "โœ… OS cache path ready: $REMOTE_DIR/cache" # Set up native git repo for auto-polling OTA builds echo "" echo "๐Ÿ“ฆ Setting up native git repo for OTA auto-builds..." echo " Fetch remote: $NATIVE_GIT_FETCH_URL" ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "root@$OVEN_HOST" " set -euo pipefail NATIVE_GIT_DIR=/opt/oven/native-git NATIVE_GIT_FETCH_URL='$NATIVE_GIT_FETCH_URL' if [ ! -d \$NATIVE_GIT_DIR/.git ]; then echo ' Cloning repo (first time)...' git clone --branch main --single-branch \$NATIVE_GIT_FETCH_URL \$NATIVE_GIT_DIR else echo ' Git repo exists, fetching latest...' cd \$NATIVE_GIT_DIR git remote set-url origin \$NATIVE_GIT_FETCH_URL git fetch origin main --quiet || true git branch --set-upstream-to=origin/main main >/dev/null 2>&1 || true if git rev-parse --verify origin/main >/dev/null 2>&1 && git merge-base --is-ancestor HEAD origin/main; then git merge origin/main --ff-only --quiet || true else echo ' Repo has local commits or divergence; leaving checkout as-is (native build preflight will hard-sync to origin/main).' fi fi if id -u oven >/dev/null 2>&1; then chown -R oven:oven \$NATIVE_GIT_DIR su - oven -s /bin/bash -c 'git config --global --add safe.directory /opt/oven/native-git' 2>/dev/null || true fi echo ' Done.' " echo "โœ… Native git repo ready" # Configure git push credentials for papers PDF auto-push echo "" echo "๐Ÿ”‘ Configuring git push credentials for papers auto-build..." ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "root@$OVEN_HOST" " NATIVE_GIT_DIR=/opt/oven/native-git # Read OVEN_GH_TOKEN from .env if it exists OVEN_GH_TOKEN=\$(grep '^OVEN_GH_TOKEN=' $REMOTE_DIR/.env 2>/dev/null | cut -d= -f2-) if [ -n \"\$OVEN_GH_TOKEN\" ]; then cd \$NATIVE_GIT_DIR # Keep fetch on Tangled, but push papers auto-build commits to the GitHub mirror. git remote set-url --push origin https://x-access-token:\${OVEN_GH_TOKEN}@github.com/whistlegraph/aesthetic-computer.git echo ' Push URL configured with token auth' else echo ' WARNING: OVEN_GH_TOKEN not found in $REMOTE_DIR/.env โ€” papers auto-push will fail' echo ' Add OVEN_GH_TOKEN=ghp_... to aesthetic-computer-vault/oven/.env and re-deploy' fi " echo "โœ… Git push credentials configured" # Restart unless --no-restart flag if [ "$1" != "--no-restart" ]; then echo "" echo "๐Ÿ”„ Restarting oven service..." # Update OVEN_VERSION in .env and rewrite the managed systemd override, then restart ssh -i "$SSH_KEY" -o StrictHostKeyChecking=no "root@$OVEN_HOST" " set -euo pipefail cd $REMOTE_DIR # Update version in .env if grep -q '^OVEN_VERSION=' .env 2>/dev/null; then sed -i 's/^OVEN_VERSION=.*/OVEN_VERSION=$GIT_VERSION/' .env else echo 'OVEN_VERSION=$GIT_VERSION' >> .env fi install -m 0644 $REMOTE_DIR/infra/oven.service /etc/systemd/system/oven.service # Rewrite systemd override from scratch so stale directives do not survive deploys. mkdir -p /etc/systemd/system/oven.service.d cat > /etc/systemd/system/oven.service.d/override.conf </dev/null || echo '{"error":"prewarm timeout"}') echo " $PREWARM_RESULT" END_PREWARM=$(date +%s%3N) PREWARM_TIME=$((END_PREWARM - END_RESTART)) TOTAL_TIME=$((END_PREWARM - START_TIME)) echo "" echo "โœ… Prewarm complete in ${PREWARM_TIME}ms" echo "๐Ÿ Total deploy time: ${TOTAL_TIME}ms" else echo "" echo "โญ๏ธ Skipped restart (--no-restart)" fi echo "" echo "๐Ÿ”ฅ Done! https://oven.aesthetic.computer"