#!/usr/bin/env bash set -euo pipefail MIGRATION_DIR="${1:-db/migrations}" DB_CONNECTION_STRING="${2:-}" if [ ! -d "$MIGRATION_DIR" ]; then echo "Migration directory not found: $MIGRATION_DIR" >&2 exit 1 fi mapfile -t files < <(find "$MIGRATION_DIR" -maxdepth 1 -type f -name 'V*.sql' | sort) if [ "${#files[@]}" -eq 0 ]; then echo "No migration files found in $MIGRATION_DIR" >&2 exit 1 fi declare -A seen_versions=() duplicate_found=0 for file in "${files[@]}"; do name="$(basename "$file")" version="${name#V}" version="${version%%__*}" if [ -n "${seen_versions[$version]:-}" ]; then echo "Duplicate migration version detected: V$version" >&2 echo " - ${seen_versions[$version]}" >&2 echo " - $file" >&2 duplicate_found=1 else seen_versions["$version"]="$file" fi done if [ "$duplicate_found" -ne 0 ]; then exit 1 fi if [ -z "$DB_CONNECTION_STRING" ]; then echo "Duplicate version check passed." exit 0 fi if ! command -v psql >/dev/null 2>&1; then echo "psql is required for database dry-run validation." >&2 exit 1 fi mapfile -t executed_versions < <( psql "$DB_CONNECTION_STRING" -Atc "SELECT version FROM schema_migrations ORDER BY version;" 2>/dev/null || true ) declare -A executed_map=() for version in "${executed_versions[@]}"; do if [ -n "$version" ]; then executed_map["$version"]=1 fi done for file in "${files[@]}"; do name="$(basename "$file")" version="${name#V}" version="${version%%__*}" if [ -n "${executed_map[$version]:-}" ]; then continue fi echo "Dry-run migration V$version: $name" psql "$DB_CONNECTION_STRING" -v ON_ERROR_STOP=1 <