#!/usr/bin/env bash # Initialise the XAMXAM SQLite database from schema.sql, then run incremental migrations. # Safe to run on existing databases — schema uses IF NOT EXISTS / INSERT OR IGNORE. # Usage: # scripts/migrate.sh # xamxam.db (default) # # Before any schema/migration is applied, a WAL-safe snapshot of the database is # written (via sqlite3 .backup) so a deploy is always recoverable if a migration # goes wrong. This augments the hourly cron backups with a pre-deploy checkpoint. set -euo pipefail REPO_ROOT="${REPO_ROOT:-$(cd "$(dirname "$0")/.." && pwd)}" # Detect layout: local dev has app/ subdir, server has files at repo root if [ -d "$REPO_ROOT/app/storage" ]; then SCHEMA="$REPO_ROOT/app/storage/schema.sql" PROD_DB="$REPO_ROOT/app/storage/xamxam.db" MIGRATIONS="$REPO_ROOT/app/migrations/run.php" # Local dev DB is disposable — no pre-migration backup needed here. BACKUP_DIR="" elif [ -f "$REPO_ROOT/storage/schema.sql" ]; then SCHEMA="$REPO_ROOT/storage/schema.sql" PROD_DB="$REPO_ROOT/storage/xamxam.db" MIGRATIONS="$REPO_ROOT/migrations/run.php" BACKUP_DIR="/var/backups/xamxam" else echo "ERROR: cannot find storage/schema.sql" >&2 exit 1 fi # ── Pre-migration backup (production remote only) ─────────────────────────── backup_before_migrate() { [ -n "$BACKUP_DIR" ] || return 0 [ -f "$PROD_DB" ] || return 0 command -v sqlite3 >/dev/null 2>&1 || { echo " [backup] sqlite3 not found — skipping"; return 0; } command -v gzip >/dev/null 2>&1 || { echo " [backup] gzip not found — skipping"; return 0; } if ! mkdir -p "$BACKUP_DIR" 2>/dev/null; then echo " [backup] WARNING: cannot write to $BACKUP_DIR — skipping pre-deploy backup" >&2 return 0 fi local snapshot snapshot="/tmp/xamxam-predeploy-$$.db" if sqlite3 "$PROD_DB" ".backup '$snapshot'" 2>/dev/null; then local out out="$BACKUP_DIR/db-$(date +%Y-%m-%dT%H-%M-%S).db.gz" gzip -c "$snapshot" > "$out" rm -f "$snapshot" echo " [backup] pre-deploy snapshot: $out" else rm -f "$snapshot" echo " [backup] WARNING: sqlite3 .backup failed — continuing without a pre-deploy snapshot" >&2 fi } if [ -n "$BACKUP_DIR" ]; then echo "──────────────────────────────────────────────" echo " [backup] taking pre-migration snapshot…" backup_before_migrate fi echo "──────────────────────────────────────────────" echo " [schema] applying schema…" if command -v syntaqlite &>/dev/null; then syntaqlite analyze "$SCHEMA" || { echo "ERROR: schema.sql validation failed" >&2; exit 1; } fi sqlite3 "$PROD_DB" < "$SCHEMA" echo " [schema] done" if [ -f "$MIGRATIONS" ]; then echo "──────────────────────────────────────────────" php "$MIGRATIONS" "$PROD_DB" else echo " [migrations] run.php not found — skipping" fi