run_migrations.sh auto-discovers numbered SQL files (NN_*.sql), tracks applied migrations in tracksolid.schema_migrations table, and skips already-applied files — safe to run on every deployment. Usage: bash /app/run_migrations.sh Coolify: add to Post-deployment Command in service settings. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
102 lines
4.2 KiB
Bash
Executable file
102 lines
4.2 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
# run_migrations.sh — Tracksolid DB Migration Runner
|
|
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
# Applies any pending numbered SQL migration files in order.
|
|
# Safe to run on every deployment — skips already-applied migrations.
|
|
#
|
|
# Usage:
|
|
# ./run_migrations.sh # auto-detect container + DB
|
|
# ./run_migrations.sh --dry-run # show what would run, don't apply
|
|
#
|
|
# Called by Coolify post-deployment command:
|
|
# bash /app/run_migrations.sh
|
|
#
|
|
# How it tracks applied migrations:
|
|
# Creates tracksolid.schema_migrations table on first run.
|
|
# Records the filename of every successfully applied migration.
|
|
# Skips any file already recorded in that table.
|
|
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
|
|
set -euo pipefail
|
|
|
|
# ── Config ────────────────────────────────────────────────────────────────────
|
|
DB_NAME="${DB_NAME:-tracksolid_db}"
|
|
DB_USER="${DB_USER:-postgres}"
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
DRY_RUN=false
|
|
|
|
# ── Argument parsing ──────────────────────────────────────────────────────────
|
|
for arg in "$@"; do
|
|
case $arg in
|
|
--dry-run) DRY_RUN=true ;;
|
|
esac
|
|
done
|
|
|
|
# ── Resolve TimescaleDB container ─────────────────────────────────────────────
|
|
TS_DB=$(docker ps --filter "name=timescale_db" --format "{{.Names}}" | head -1)
|
|
if [[ -z "$TS_DB" ]]; then
|
|
echo "ERROR: no running timescale_db container found." >&2
|
|
exit 1
|
|
fi
|
|
echo "Using container: $TS_DB"
|
|
|
|
# ── Helper: run SQL against the DB ───────────────────────────────────────────
|
|
run_sql() {
|
|
docker exec -i "$TS_DB" psql -U "$DB_USER" -d "$DB_NAME" "$@"
|
|
}
|
|
|
|
# ── Ensure migration tracking table exists ───────────────────────────────────
|
|
run_sql -c "
|
|
CREATE TABLE IF NOT EXISTS tracksolid.schema_migrations (
|
|
filename TEXT PRIMARY KEY,
|
|
applied_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
);
|
|
" > /dev/null
|
|
|
|
# ── Find and apply pending migrations ────────────────────────────────────────
|
|
MIGRATION_FILES=$(find "$SCRIPT_DIR" -maxdepth 1 -name '[0-9][0-9]_*.sql' | sort)
|
|
|
|
if [[ -z "$MIGRATION_FILES" ]]; then
|
|
echo "No migration files found in $SCRIPT_DIR"
|
|
exit 0
|
|
fi
|
|
|
|
APPLIED=0
|
|
SKIPPED=0
|
|
|
|
for filepath in $MIGRATION_FILES; do
|
|
filename=$(basename "$filepath")
|
|
|
|
# Check if already applied
|
|
already_applied=$(run_sql -t -c \
|
|
"SELECT COUNT(*) FROM tracksolid.schema_migrations WHERE filename = '$filename';" \
|
|
2>/dev/null | tr -d '[:space:]')
|
|
|
|
if [[ "$already_applied" == "1" ]]; then
|
|
echo " SKIP $filename (already applied)"
|
|
((SKIPPED++)) || true
|
|
continue
|
|
fi
|
|
|
|
if [[ "$DRY_RUN" == "true" ]]; then
|
|
echo " PENDING $filename (would apply)"
|
|
continue
|
|
fi
|
|
|
|
echo " APPLY $filename ..."
|
|
if run_sql < "$filepath"; then
|
|
# Record successful application
|
|
run_sql -c \
|
|
"INSERT INTO tracksolid.schema_migrations (filename) VALUES ('$filename');" \
|
|
> /dev/null
|
|
echo " OK $filename"
|
|
((APPLIED++)) || true
|
|
else
|
|
echo " FAIL $filename — aborting migration run" >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
echo ""
|
|
echo "Migrations complete: $APPLIED applied, $SKIPPED skipped."
|