#!/usr/bin/env bash # Incremental deploy — syncs only new/changed code + raw/--subagent data. # Skips re-syncing wiki/, processing/png/, processing/ocr/ (already on VPS). # # Steps: # 1. Sync infra updates (compose, embed-service Dockerfile, migration 02) # 2. Sync web/ Next.js source (V2, search, graph, timeline, stats, command palette) # 3. Sync scripts/ (30-33 new scripts) # 4. Sync raw/*--subagent/ (chunks, 634MB — needed for V2 view) # 5. Apply migration 02 (pgvector + chunks_retrieval schema) # 6. Build & start embed-service (BGE-M3 + reranker) — runs in background # 7. Rebuild web container # 8. Report URLs source "$(dirname "$0")/_lib.sh" set -euo pipefail STACK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" UFO_ROOT="${LAPTOP_UFO_ROOT:-/Users/guto/ufo}" echo "════════════════════════════════════════════════════════════════" echo " STAGE 1 — sync infra (compose, embed-service, migration 02)" echo "════════════════════════════════════════════════════════════════" vps_ssh "mkdir -p ${VPS_DEPLOY_ROOT}/{migrations,embed-service}" vps_rsync "${STACK_DIR}/docker-compose.yml" "${VPS_DEPLOY_ROOT}/docker-compose.yml" vps_rsync "${STACK_DIR}/kong.yml" "${VPS_DEPLOY_ROOT}/kong.yml" vps_rsync "${STACK_DIR}/.env" "${VPS_DEPLOY_ROOT}/.env" vps_rsync "${STACK_DIR}/../supabase/migrations/0002_chunks_retrieval.sql" \ "${VPS_DEPLOY_ROOT}/migrations/02-chunks-retrieval.sql" vps_rsync "${STACK_DIR}/../embed-service/Dockerfile" "${VPS_DEPLOY_ROOT}/embed-service/Dockerfile" vps_rsync "${STACK_DIR}/../embed-service/app.py" "${VPS_DEPLOY_ROOT}/embed-service/app.py" vps_rsync "${STACK_DIR}/../embed-service/requirements.txt" "${VPS_DEPLOY_ROOT}/embed-service/requirements.txt" echo "" echo "════════════════════════════════════════════════════════════════" echo " STAGE 2 — sync web/ source (Next.js — V2 + retrieval features)" echo "════════════════════════════════════════════════════════════════" if [ "$VPS_AUTH" = "password" ]; then SSHPASS="$VPS_PASSWORD" sshpass -e rsync -avz --delete \ --exclude node_modules --exclude .next --exclude .env.local \ -e "ssh -o StrictHostKeyChecking=accept-new -p $VPS_PORT" \ "${UFO_ROOT}/web/" "${VPS_USER}@${VPS_HOST}:${VPS_DEPLOY_ROOT}/web/" else rsync -avz --delete \ --exclude node_modules --exclude .next --exclude .env.local \ -e "ssh -o StrictHostKeyChecking=accept-new -p $VPS_PORT -i ${VPS_SSH_KEY/#\~/$HOME}" \ "${UFO_ROOT}/web/" "${VPS_USER}@${VPS_HOST}:${VPS_DEPLOY_ROOT}/web/" fi echo "" echo "════════════════════════════════════════════════════════════════" echo " STAGE 3 — sync scripts/ (30-33 retrieval pipeline scripts)" echo "════════════════════════════════════════════════════════════════" vps_ssh "mkdir -p ${VPS_DEPLOY_ROOT}/scripts" if [ "$VPS_AUTH" = "password" ]; then SSHPASS="$VPS_PASSWORD" sshpass -e rsync -avz \ -e "ssh -o StrictHostKeyChecking=accept-new -p $VPS_PORT" \ "${UFO_ROOT}/scripts/" "${VPS_USER}@${VPS_HOST}:${VPS_DEPLOY_ROOT}/scripts/" else rsync -avz \ -e "ssh -o StrictHostKeyChecking=accept-new -p $VPS_PORT -i ${VPS_SSH_KEY/#\~/$HOME}" \ "${UFO_ROOT}/scripts/" "${VPS_USER}@${VPS_HOST}:${VPS_DEPLOY_ROOT}/scripts/" fi echo "" echo "════════════════════════════════════════════════════════════════" echo " STAGE 4 — sync raw/*--subagent/ (chunks v0.2.0, ~634MB, 116 docs)" echo "════════════════════════════════════════════════════════════════" echo "Note: only --subagent/ archives are synced (chunks + crops + index)" echo " Raw PDFs already on VPS — not re-synced" if [ "$VPS_AUTH" = "password" ]; then SSHPASS="$VPS_PASSWORD" sshpass -e rsync -avz \ --include='*--subagent/' --include='*--subagent/**' --include='*.pdf' --exclude='*' \ -e "ssh -o StrictHostKeyChecking=accept-new -p $VPS_PORT" \ "${UFO_ROOT}/raw/" "${VPS_USER}@${VPS_HOST}:${DATA_RAW:-${VPS_DEPLOY_ROOT}/raw}/" else rsync -avz \ --include='*--subagent/' --include='*--subagent/**' --include='*.pdf' --exclude='*' \ -e "ssh -o StrictHostKeyChecking=accept-new -p $VPS_PORT -i ${VPS_SSH_KEY/#\~/$HOME}" \ "${UFO_ROOT}/raw/" "${VPS_USER}@${VPS_HOST}:${DATA_RAW:-${VPS_DEPLOY_ROOT}/raw}/" fi echo "" echo "════════════════════════════════════════════════════════════════" echo " STAGE 5 — apply migration 02 (pgvector + chunks tables)" echo "════════════════════════════════════════════════════════════════" vps_ssh "cd ${VPS_DEPLOY_ROOT} && docker exec -i disclosure-db psql -U postgres < migrations/02-chunks-retrieval.sql 2>&1 | tail -10" echo "" echo "════════════════════════════════════════════════════════════════" echo " STAGE 6 — build + start embed-service (BGE-M3, ~10min first build)" echo "════════════════════════════════════════════════════════════════" vps_ssh "cd ${VPS_DEPLOY_ROOT} && docker compose build embed 2>&1 | tail -5" vps_ssh "cd ${VPS_DEPLOY_ROOT} && docker compose up -d embed" echo "" echo "════════════════════════════════════════════════════════════════" echo " STAGE 7 — rebuild + restart web (Next.js with V2/search/graph)" echo "════════════════════════════════════════════════════════════════" vps_ssh "cd ${VPS_DEPLOY_ROOT} && docker compose build web 2>&1 | tail -5" vps_ssh "cd ${VPS_DEPLOY_ROOT} && docker compose up -d --force-recreate web" echo "" echo "════════════════════════════════════════════════════════════════" echo " STAGE 8 — status" echo "════════════════════════════════════════════════════════════════" vps_ssh "cd ${VPS_DEPLOY_ROOT} && docker compose ps" echo "" echo "✓ Incremental deploy complete." echo "" echo "Test URLs:" echo " https://${DOMAIN_MAIN:-disclosure.top}/ (home with filters + summaries + ✨v2 badges)" echo " https://${DOMAIN_MAIN:-disclosure.top}/d/doc-342-.../v2 (rich chunks view)" echo " https://${DOMAIN_MAIN:-disclosure.top}/search (hybrid search palette)" echo " https://${DOMAIN_MAIN:-disclosure.top}/timeline (decade timeline)" echo " https://${DOMAIN_MAIN:-disclosure.top}/graph (force-directed)" echo " https://${DOMAIN_MAIN:-disclosure.top}/admin/stats (corpus analytics)" echo " https://${DOMAIN_MAIN:-disclosure.top}/admin/batch (batch monitor)" echo " https://${DOMAIN_MAIN:-disclosure.top}/admin/indexer (retrieval health)" echo "" echo "Embed-service may take ~5 minutes to download BGE-M3 weights on first /embed call." echo "Indexer (next step, not run by this script):" echo " ssh vps && docker exec disclosure-embed curl -s http://localhost:8000/health" echo " # then run: scripts/30-index-chunks-to-db.py from a container with internal network"