#!/usr/bin/env bash set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ROOT_DIR="$(cd "${SCRIPT_DIR}/../.." && pwd)" load_database_url_from_env_file() { local env_file="$1" local line value while IFS= read -r line || [[ -n "${line}" ]]; do line="${line#"${line%%[![:space:]]*}"}" [[ -z "${line}" || "${line}" == \#* ]] && continue line="${line#export }" if [[ "${line}" == DATABASE_URL=* ]]; then value="${line#DATABASE_URL=}" value="${value%$'\r'}" value="${value%\"}" value="${value#\"}" value="${value%\'}" value="${value#\'}" printf '%s' "${value}" return 0 fi done < "${env_file}" return 1 } DATABASE_URL="${DATABASE_URL:-}" if [[ -z "${DATABASE_URL}" && -f "${ROOT_DIR}/.env" ]]; then DATABASE_URL="$(load_database_url_from_env_file "${ROOT_DIR}/.env" || true)" fi if [[ -z "${DATABASE_URL}" ]]; then echo "DATABASE_URL is required (export it, or set it in ${ROOT_DIR}/.env)" exit 1 fi if ! command -v psql >/dev/null 2>&1; then echo "psql not found in PATH" exit 127 fi CONFIRM="${CONFIRM:-0}" if [[ "${CONFIRM}" != "1" ]]; then echo "Refusing to run rollback without CONFIRM=1" exit 1 fi TARGET_VERSION="${TARGET_VERSION:-}" STEPS="${STEPS:-1}" if ! psql "${DATABASE_URL}" -v ON_ERROR_STOP=1 -c "SELECT 1 FROM iam_schema_migrations LIMIT 1" >/dev/null 2>&1; then echo "No iam_schema_migrations table found; nothing to rollback" exit 0 fi applied_versions=() while IFS= read -r v; do [[ -n "${v}" ]] && applied_versions+=( "${v}" ) done < <(psql "${DATABASE_URL}" -At -c "SELECT version FROM iam_schema_migrations ORDER BY version DESC") if [[ ${#applied_versions[@]} -eq 0 ]]; then echo "No applied migrations; nothing to rollback" exit 0 fi to_rollback=() if [[ -n "${TARGET_VERSION}" ]]; then for v in "${applied_versions[@]}"; do if [[ "${v}" > "${TARGET_VERSION}" ]]; then to_rollback+=( "${v}" ) fi done else count=0 for v in "${applied_versions[@]}"; do to_rollback+=( "${v}" ) count=$((count + 1)) if [[ "${count}" -ge "${STEPS}" ]]; then break fi done fi if [[ ${#to_rollback[@]} -eq 0 ]]; then echo "No migrations to rollback" exit 0 fi for v in "${to_rollback[@]}"; do down_file="${SCRIPT_DIR}/rollback/${v}.down.sql" if [[ ! -f "${down_file}" ]]; then echo "Missing rollback script: ${down_file}" exit 1 fi echo "Rolling back ${v}" psql "${DATABASE_URL}" -v ON_ERROR_STOP=1 -f "${down_file}" psql "${DATABASE_URL}" -v ON_ERROR_STOP=1 -c "DELETE FROM iam_schema_migrations WHERE version='${v}'" done echo "Rollback completed"