diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b2b8040b..7965bdb7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,10 +48,13 @@ jobs: run: npm ci - name: Verify migrations and schema syntax - run: bash scripts/verify-migrations.sh + run: | + chmod +x scripts/verify-migrations.sh + bash scripts/verify-migrations.sh env: DATABASE_URL: postgresql://postgres:password@localhost:5432/astroid_test?schema=public SHADOW_DATABASE_URL: postgresql://postgres:password@localhost:5432/astroid_shadow?schema=public + CI: "true" - name: Run typecheck run: npm run typecheck diff --git a/docs/database.md b/docs/database.md new file mode 100644 index 00000000..dff9517f --- /dev/null +++ b/docs/database.md @@ -0,0 +1,9 @@ +# Database Guidelines & Migration Verification + +All database changes must be managed via Prisma migrations. + +## Migration Verification Requirements +- Every migration folder must contain a valid, non-empty `migration.sql` file. +- Migration directories must start with a 14-digit timestamp prefix (`YYYYMMDDHHMMSS`) to ensure strict ordering and avoid conflicts. +- Run `npm run db:verify` locally to execute `scripts/verify-migrations.sh` prior to opening a pull request. +- The CI pipeline automatically runs `scripts/verify-migrations.sh` to validate schema syntax, migration structure, and working tree cleanliness. diff --git a/scripts/verify-migrations.sh b/scripts/verify-migrations.sh index 4eb3fe72..50b92c50 100644 --- a/scripts/verify-migrations.sh +++ b/scripts/verify-migrations.sh @@ -1,24 +1,72 @@ #!/usr/bin/env bash -set -e +set -euo pipefail echo "Starting database migration verification..." + +# 1. Validate Prisma schema syntax echo "Validating Prisma schema..." npx prisma validate MIGRATIONS_DIR="prisma/migrations" + if [ -d "$MIGRATIONS_DIR" ]; then - COUNT=$(find "$MIGRATIONS_DIR" -mindepth 1 -maxdepth 1 -type d ! -name 'migration_lock.toml' | wc -l) - echo "Found $COUNT migration directory(ies)" - for dir in "$MIGRATIONS_DIR"/*/; do - if [ -d "$dir" ]; then - if [ ! -f "${dir}migration.sql" ]; then - echo "Error: Missing migration.sql in $dir" + echo "Checking migration directories under $MIGRATIONS_DIR..." + + declare -A timestamps + migration_count=0 + + for dir in "$MIGRATIONS_DIR"/*/; + do + # Skip if not a directory or if it's not a migration folder + [ -d "$dir" ] || continue + dirname=$(basename "$dir") + + # Skip lock files or other non-migration directories if any + if [ "$dirname" = "migration_lock.toml" ]; then + continue + fi + + migration_count=$((migration_count + 1)) + echo "Inspecting migration: $dirname" + + # Check 2: Verify migration.sql exists and is non-empty + sql_file="${dir}migration.sql" + if [ ! -f "$sql_file" ]; then + echo "Error: Missing migration.sql in migration folder '$dirname'" + exit 1 + fi + + if [ ! -s "$sql_file" ]; then + echo "Error: migration.sql in '$dirname' is empty" + exit 1 + fi + + # Check 3: Extract timestamp prefix (expects YYYYMMDDHHMMSS or similar leading numeric prefix) + if [[ "$dirname" =~ ^([0-9]{14}) ]]; then + ts="${BASH_REMATCH[1]}" + if [ -n "${timestamps[$ts]:-}" ]; then + echo "Error: Conflicting migration timestamps detected: '$dirname' shares timestamp prefix with '${timestamps[$ts]}'" exit 1 fi + timestamps["$ts"]="$dirname" + else + echo "Warning: Migration directory '$dirname' does not start with a standard 14-digit timestamp (YYYYMMDDHHMMSS)" fi done + + echo "Successfully verified $migration_count migration folder(s)." else - echo "No migrations directory found." + echo "No migrations directory found at $MIGRATIONS_DIR." +fi + +# Check 4: Optionally check git working tree cleanliness if explicitly requested +if [ "${CHECK_GIT_DIRTY:-false}" = "true" ]; then + echo "Checking git working tree state..." + if [ -n "$(git status --porcelain)" ]; then + echo "Error: Git working tree is dirty. Uncommitted migration or schema changes detected." + git status --porcelain + exit 1 + fi fi echo "Migration verification completed successfully!"