From 9a9a73dde2a008f755da485774db6703d9ddb425 Mon Sep 17 00:00:00 2001 From: Matt Date: Thu, 7 May 2026 17:10:44 +0200 Subject: [PATCH] fix(docker): query _prisma_migrations directly for failed-migration auto-resolve MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous regex against `prisma migrate status` output silently drifted out of sync with Prisma 6's wording, so today's failed migration on prod was never auto-resolved — the container crash-looped and required a manual DELETE on _prisma_migrations to recover. Truth lives in the table: a row with `finished_at IS NULL AND rolled_back_at IS NULL` is an unresolved failure. Query that directly via the Prisma client (already shelled out for the user-count check below) and loop until none remain (with a 5-iteration safety bound). Co-Authored-By: Claude Opus 4.7 (1M context) --- docker/docker-entrypoint.sh | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/docker/docker-entrypoint.sh b/docker/docker-entrypoint.sh index 7c89216..4822b40 100644 --- a/docker/docker-entrypoint.sh +++ b/docker/docker-entrypoint.sh @@ -6,15 +6,38 @@ MIGRATION_RETRY_DELAY_SECONDS="${MIGRATION_RETRY_DELAY_SECONDS:-2}" ATTEMPT=1 # Auto-resolve any previously failed migrations so deploy can proceed. -# This handles the case where a migration partially applied and was fixed -# in a subsequent deploy — without this, Prisma refuses to run anything. +# This handles the case where a migration failed mid-flight and was then +# fixed in a subsequent deploy — without this, Prisma refuses to run +# anything else (P3009). +# +# We query `_prisma_migrations` directly rather than parsing the output of +# `prisma migrate status`, because that output's wording has shifted between +# Prisma versions and any drift means failed migrations slip through and +# the container crash-loops. Truth lives in the table: a row with +# `finished_at IS NULL AND rolled_back_at IS NULL` is an unresolved failure. echo "==> Checking for failed migrations..." -MIGRATE_STATUS=$(npx prisma migrate status 2>&1 || true) -FAILED=$(echo "$MIGRATE_STATUS" | sed -n 's/.*The `\([^`]*\)` migration.*failed.*/\1/p' | head -1) -if [ -n "$FAILED" ]; then +RESOLVE_ATTEMPTS=0 +while [ "$RESOLVE_ATTEMPTS" -lt 5 ]; do + FAILED=$(node -e " + const { PrismaClient } = require('@prisma/client'); + const p = new PrismaClient(); + p.\$queryRaw\` + SELECT migration_name FROM _prisma_migrations + WHERE finished_at IS NULL AND rolled_back_at IS NULL + ORDER BY started_at ASC LIMIT 1 + \`.then(r => { console.log(r[0]?.migration_name || ''); p.\$disconnect(); }) + .catch(() => { console.log(''); p.\$disconnect(); }); + " 2>/dev/null || echo "") + if [ -z "$FAILED" ]; then + break + fi echo "==> Found failed migration: $FAILED — marking as rolled back..." - npx prisma migrate resolve --rolled-back "$FAILED" -fi + npx prisma migrate resolve --rolled-back "$FAILED" || { + echo "WARNING: prisma migrate resolve failed for $FAILED" + break + } + RESOLVE_ATTEMPTS=$((RESOLVE_ATTEMPTS + 1)) +done echo "==> Running database migrations (with retry)..." until npx prisma migrate deploy; do