#!/usr/bin/env bash set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ROOT_DIR="$(cd "${SCRIPT_DIR}/../.." && pwd)" MIGRATIONS_DIR="${ROOT_DIR}/migrations" 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 checksum_hex_of_file() { local file="$1" if command -v sha384sum >/dev/null 2>&1; then sha384sum "${file}" | awk '{print $1}' return 0 fi if command -v openssl >/dev/null 2>&1; then openssl dgst -sha384 -binary "${file}" | xxd -p -c 256 return 0 fi echo "sha384sum or openssl is required to compute sqlx checksum" >&2 return 127 } TARGET_VERSION="${TARGET_VERSION:-}" DRY_RUN="${DRY_RUN:-0}" psql "${DATABASE_URL}" -v ON_ERROR_STOP=1 -c "CREATE TABLE IF NOT EXISTS _sqlx_migrations (version BIGINT PRIMARY KEY, description TEXT NOT NULL, installed_on TIMESTAMPTZ NOT NULL DEFAULT now(), success BOOLEAN NOT NULL, checksum BYTEA NOT NULL, execution_time BIGINT NOT NULL)" shopt -s nullglob migrations=( "${MIGRATIONS_DIR}/"*.sql ) if [[ ${#migrations[@]} -eq 0 ]]; then echo "No migration files found: ${MIGRATIONS_DIR}/*.sql" exit 1 fi for file in "${migrations[@]}"; do base="$(basename "${file}")" version_str="${base%%_*}" version_num="$((10#${version_str}))" description="${base#*_}" description="${description%.sql}" if [[ -n "${TARGET_VERSION}" && "${version_num}" -gt "${TARGET_VERSION}" ]]; then continue fi applied="$(psql "${DATABASE_URL}" -At -c "SELECT 1 FROM _sqlx_migrations WHERE version=${version_num} AND success=true LIMIT 1" || true)" if [[ "${applied}" == "1" ]]; then continue fi echo "Applying ${version_str} (${base})" if [[ "${DRY_RUN}" == "1" ]]; then continue fi checksum="$(checksum_hex_of_file "${file}")" psql "${DATABASE_URL}" -v ON_ERROR_STOP=1 -f "${file}" psql "${DATABASE_URL}" -v ON_ERROR_STOP=1 -c "INSERT INTO _sqlx_migrations(version, description, success, checksum, execution_time) VALUES (${version_num}, '${description}', true, decode('${checksum}','hex'), 0) ON CONFLICT (version) DO NOTHING" done echo "Migrations completed"