94 lines
2.7 KiB
Bash
Executable File
94 lines
2.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -Eeuo pipefail
|
|
|
|
ts() { date +"%Y-%m-%d %H:%M:%S"; }
|
|
log() { echo "[$(ts)] [INFO] $*"; }
|
|
warn(){ echo "[$(ts)] [WARN] $*" >&2; }
|
|
err() { echo "[$(ts)] [ERROR] $*" >&2; }
|
|
die() { err "$*"; exit 1; }
|
|
|
|
trap 'err "Migration failed. Showing last 200 log lines:"; docker logs --tail=200 mssql2025 2>/dev/null || true' ERR
|
|
|
|
cd "$(dirname "$0")/.."
|
|
|
|
[[ -f .env ]] || die "Missing .env. Create it first: cp .env.example .env"
|
|
|
|
set -a
|
|
# shellcheck disable=SC1091
|
|
source .env
|
|
set +a
|
|
|
|
[[ -n "${MSSQL_SA_PASSWORD:-}" ]] || die "MSSQL_SA_PASSWORD is empty in .env"
|
|
|
|
if ! docker ps --format '{{.Names}}' | grep -qx 'mssql2025'; then
|
|
die "Container mssql2025 is not running. Start it first: ./scripts/start.sh"
|
|
fi
|
|
|
|
health="$(docker inspect -f '{{.State.Health.Status}}' mssql2025 2>/dev/null || true)"
|
|
[[ "$health" == "healthy" ]] || die "Container is not healthy (health=$health). Run ./scripts/start.sh"
|
|
|
|
# Bootstrap: ensure DevDb + migration table exists
|
|
bootstrap_sql=$(
|
|
cat <<'SQL'
|
|
IF DB_ID('DevDb') IS NULL
|
|
BEGIN
|
|
CREATE DATABASE DevDb;
|
|
END
|
|
GO
|
|
USE DevDb;
|
|
GO
|
|
IF OBJECT_ID('dbo.__SchemaMigrations','U') IS NULL
|
|
BEGIN
|
|
CREATE TABLE dbo.__SchemaMigrations(
|
|
Id int IDENTITY(1,1) NOT NULL PRIMARY KEY,
|
|
Filename nvarchar(260) NOT NULL UNIQUE,
|
|
AppliedAt datetime2 NOT NULL CONSTRAINT DF___SchemaMigrations_AppliedAt DEFAULT (sysutcdatetime())
|
|
);
|
|
END
|
|
GO
|
|
SQL
|
|
)
|
|
|
|
log "Ensuring DevDb + dbo.__SchemaMigrations exist..."
|
|
docker exec -i mssql2025 /opt/mssql-tools18/bin/sqlcmd \
|
|
-S localhost -U sa -P "$MSSQL_SA_PASSWORD" \
|
|
-b -Q "$bootstrap_sql" < /dev/null
|
|
|
|
shopt -s nullglob
|
|
files=(db/migrations/*.sql)
|
|
|
|
if (( ${#files[@]} == 0 )); then
|
|
warn "No db/migrations/*.sql files found. Nothing to do."
|
|
exit 0
|
|
fi
|
|
|
|
log "Applying migrations (forward-only, each file once)."
|
|
for f in "${files[@]}"; do
|
|
bn="$(basename "$f")"
|
|
log "-> migration candidate: $bn"
|
|
|
|
check_sql="SET NOCOUNT ON; USE DevDb; SELECT COUNT(1) FROM dbo.__SchemaMigrations WHERE Filename = N'$bn';"
|
|
applied="$(docker exec -i mssql2025 /opt/mssql-tools18/bin/sqlcmd \
|
|
-S localhost -U sa -P "$MSSQL_SA_PASSWORD" -h -1 -W -Q "$check_sql" < /dev/null \
|
|
| tr -d '\r' | tail -n 1 | xargs || true)"
|
|
|
|
if [[ "${applied:-0}" != "0" ]]; then
|
|
log " (skip) already applied"
|
|
continue
|
|
fi
|
|
|
|
log " (apply) running /migrations/$bn"
|
|
docker exec -i mssql2025 /opt/mssql-tools18/bin/sqlcmd \
|
|
-S localhost -U sa -P "$MSSQL_SA_PASSWORD" \
|
|
-b -i "/migrations/$bn" < /dev/null
|
|
|
|
record_sql="USE DevDb; INSERT INTO dbo.__SchemaMigrations(Filename) VALUES (N'$bn');"
|
|
docker exec -i mssql2025 /opt/mssql-tools18/bin/sqlcmd \
|
|
-S localhost -U sa -P "$MSSQL_SA_PASSWORD" \
|
|
-b -Q "$record_sql" < /dev/null
|
|
|
|
log " applied ✅"
|
|
done
|
|
|
|
log "Migrations completed ✅"
|