#!/usr/bin/env bash # scripts/3_cleanup.sh — Step 3: Post-migration cleanup # Run ONLY after verifying that the Docker deployment is working correctly. # Frees disk space by removing build artifacts and source files now in volumes. # # Usage: # bash 3_cleanup.sh # interactive — prompts before each rm # bash 3_cleanup.sh --yes # skip confirmation prompts (use with care) set -euo pipefail REPO_DIR="/opt/sandbox-notebookllamalm-nextjs" VOLUME_PREFIX="sandbox-notebookllamalm-nextjs_" BACKEND_SVC="notebookllama-backend" FRONTEND_SVC="notebookllama-frontend" BACKEND_PORT=9000 FRONTEND_PORT=4000 AUTO_YES=false # ── Parse args ──────────────────────────────────────────────────────────────── for arg in "$@"; do [[ "$arg" == "--yes" ]] && AUTO_YES=true done # ── Colors ────────────────────────────────────────────────────────────────── RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; NC='\033[0m' info() { echo -e "${GREEN}[INFO]${NC} $*"; } warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } error() { echo -e "${RED}[ERROR]${NC} $*" >&2; } step() { echo -e "\n${CYAN}══ $* ${NC}"; } # ── Confirm helper ──────────────────────────────────────────────────────────── confirm() { local prompt="$1" if $AUTO_YES; then info "${prompt} — auto-confirmed (--yes)" return 0 fi echo -en "${YELLOW}${prompt} [y/N]: ${NC}" read -r answer [[ "$answer" =~ ^[Yy]$ ]] } cd "$REPO_DIR" # ── Step 1: Verify all containers are running ───────────────────────────────── step "1 / Verify Docker containers" EXPECTED_SERVICES=(postgres redis backend frontend) ALL_RUNNING=true for svc in "${EXPECTED_SERVICES[@]}"; do STATUS=$(docker compose ps --format '{{.Service}} {{.State}}' 2>/dev/null \ | grep "^${svc} " | awk '{print $2}' || echo "missing") if [[ "$STATUS" == "running" ]]; then info " ${svc}: running" else error " ${svc}: ${STATUS:-not found}" ALL_RUNNING=false fi done if ! $ALL_RUNNING; then error "Not all containers are running. Fix the deployment before cleaning up." echo " docker compose ps" exit 1 fi # Double-check via HTTP check_alive() { local name="$1"; local url="$2" local code code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "$url" 2>/dev/null || echo "000") if [[ "$code" == "200" ]]; then info " ${name}: HTTP 200 OK" else warn " ${name}: HTTP ${code} — proceeding anyway (container is up)" fi } check_alive "Backend" "http://localhost:${BACKEND_PORT}/api/health" check_alive "Frontend" "http://localhost:${FRONTEND_PORT}/notebookllama" # ── Step 2: Verify data in podcasts_data volume ──────────────────────────────── step "2 / Verify data in Docker volumes" PODCASTS_VOLUME="${VOLUME_PREFIX}podcasts_data" UPLOADS_VOLUME="${VOLUME_PREFIX}uploads_data" VOL_MP3=$(docker run --rm -v "${PODCASTS_VOLUME}:/d:ro" alpine \ sh -c 'find /d -name "*.mp3" | wc -l' 2>/dev/null || echo "0") SRC_MP3=$(find backend/conversations -name '*.mp3' 2>/dev/null | wc -l | tr -d ' ') info "podcasts_data volume: ${VOL_MP3} MP3 file(s)" info "backend/conversations source: ${SRC_MP3} MP3 file(s)" if [[ "$VOL_MP3" -lt "$SRC_MP3" ]]; then error "Volume has fewer MP3s (${VOL_MP3}) than source (${SRC_MP3}). Copy may be incomplete." error "Aborting cleanup. Re-run 2_deploy.sh or copy manually." exit 1 fi if [[ "$SRC_MP3" -gt 0 && "$VOL_MP3" -ge "$SRC_MP3" ]]; then info "Volume data matches source — safe to remove source files" elif [[ "$SRC_MP3" -eq 0 ]]; then info "No MP3 files in source directory (already cleaned or never populated)" fi # ── Step 3: Remove source MP3s (now in volume) ──────────────────────────────── step "3 / Remove backend/conversations/ (data is in Docker volume)" if [[ -d "backend/conversations" ]] && [[ -n "$(ls -A backend/conversations 2>/dev/null)" ]]; then CONV_SIZE=$(du -sh backend/conversations | cut -f1) warn "Will delete: backend/conversations/ (${CONV_SIZE})" if confirm "Delete backend/conversations/?"; then sudo rm -rf backend/conversations/ info "Deleted backend/conversations/" else info "Skipped — keeping backend/conversations/" fi else info "backend/conversations/ is empty or does not exist — nothing to remove" fi # ── Step 4: Remove build artifacts ──────────────────────────────────────────── step "4 / Remove build artifacts (Docker builds its own)" remove_dir() { local path="$1" local label="$2" if [[ -d "$path" ]]; then local size size=$(du -sh "$path" 2>/dev/null | cut -f1) warn "Will delete: ${path} (${size}) — ${label}" if confirm "Delete ${path}?"; then sudo rm -rf "$path" info "Deleted ${path}" else info "Skipped — keeping ${path}" fi else info "${path} does not exist — skipping" fi } remove_dir "frontend/.next" "Next.js build cache (Docker builds its own)" remove_dir "frontend/node_modules" "Node.js deps (Docker installs its own)" remove_dir "backend/.venv" "Python virtualenv (Docker uses uv)" # ── Step 5: Prune dangling Docker images ────────────────────────────────────── step "5 / Prune dangling Docker images" if confirm "Run docker image prune -f (remove dangling images)?"; then docker image prune -f info "Dangling images removed" else info "Skipped image prune" fi # ── Step 6: Show freed disk space ──────────────────────────────────────────── step "6 / Disk usage" df -h /opt echo "" info "Docker disk usage:" docker system df # ── Step 7: Finalize systemd ────────────────────────────────────────────────── step "7 / Finalize systemd" for svc in "$BACKEND_SVC" "$FRONTEND_SVC"; do if sudo systemctl is-enabled --quiet "$svc" 2>/dev/null; then warn "${svc} is still enabled — disabling now..." sudo systemctl disable "$svc" info " → disabled" else info "${svc}: already disabled" fi done sudo systemctl daemon-reload info "systemd daemon reloaded" # ── Done ────────────────────────────────────────────────────────────────────── echo "" info "── Cleanup complete ──────────────────────────────────────────────" echo "" info "Docker containers (should all be running):" docker compose ps echo "" info "Docker volumes (should still exist):" docker volume ls | grep "$VOLUME_PREFIX" || true echo "" info "Note: .service files in the repository have NOT been deleted." echo " They serve as a reference in case you need to roll back to systemd." echo "" info "Docker containers are configured with 'restart: unless-stopped' and will" info "start automatically when the server reboots — no systemd required." echo "" info "Cleanup finished at $(date)"