Skip to content

ci: enforce prisma migration ordering and schema drift#1400

Merged
brendan-kellam merged 4 commits into
mainfrom
brendan-kellam/enforce-prisma-migration-order
Jun 29, 2026
Merged

ci: enforce prisma migration ordering and schema drift#1400
brendan-kellam merged 4 commits into
mainfrom
brendan-kellam/enforce-prisma-migration-order

Conversation

@brendan-kellam

@brendan-kellam brendan-kellam commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Summary

Enforces that Prisma migrations are always in order, gating both PRs and release builds.

The logic lives in a reusable composite action, .github/actions/check-prisma-migrations, embedded in two existing build jobs:

  • pr-gate.yml (build job — the required PR check): fails the PR before merge if migrations are out of order. Runs early, before the Docker build, to fail fast.
  • _build.yml (build job, amd64 only): release-time backstop. Fails release-dev/release-prod if schema drift slips through.

Checks

  1. Schema drift — starts Postgres, runs prisma migrate deploy (fails if migrations are broken or apply out of sequence), then prisma migrate diff --from-database --to-schema-datamodel --exit-code to confirm the applied history reproduces schema.prisma. Catches schema edits with no migration and hand-edited migrations.
  2. Out-of-order timestamps (PRs only) — fails if a migration added in the PR has a 14-digit timestamp earlier than the latest already on the base branch (the merge-race problem where a late-merged migration lands "before" one already applied to prod).

Notes

  • The action self-contains Postgres via docker run (job-level services: can't be shared across workflows) and detects whether packages/db/prisma/** changed, skipping fast on PRs that don't touch migrations — so the required build check always reports and never sits pending.
  • The ordering check is PR-only (it needs a base branch); release builds run the drift check unconditionally.

Summary by CodeRabbit

  • New Features
    • Added automated Prisma migration validation to CI and PR checks.
    • PRs now verify migration order and detect schema drift before merge.
    • The check runs only when Prisma-related files change and applies to the main build path.

Add a GitHub Actions workflow that runs on PRs touching
packages/db/prisma/**. It verifies migrations apply cleanly in
sequence and reproduce schema.prisma (drift check via prisma
migrate diff), and that no newly added migration predates the
latest migration on main (out-of-order timestamp check).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

This comment has been minimized.

@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

A new composite GitHub Action (check-prisma-migrations) is added that detects Prisma file changes, starts a local Postgres container, applies migrations, checks schema drift, and enforces migration timestamp ordering. This action is integrated into the PR gate workflow (with full history fetch and base-ref) and the build workflow (conditionally on linux/amd64).

Changes

Prisma Migrations CI Enforcement

Layer / File(s) Summary
Composite action: detection, setup, drift, and ordering
.github/actions/check-prisma-migrations/action.yml
Defines the action with optional base-ref input; detects changes under packages/db/prisma/; starts Postgres 16 and waits for readiness; installs deps; runs prisma migrate deploy then prisma migrate diff for drift detection; enforces that new migration timestamps are not older than the latest base-branch migration.
Workflow integration
.github/workflows/pr-gate.yml, .github/workflows/_build.yml
PR gate adds fetch-depth: 0 and invokes the action with base-ref; build workflow invokes the action conditionally for linux/amd64 only.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main CI change: enforcing Prisma migration ordering and schema drift checks.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch brendan-kellam/enforce-prisma-migration-order

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/prisma-migrations.yml:
- Around line 31-42: Disable persisted checkout credentials before the Install
step in the prisma-migrations workflow so install-time scripts cannot access the
GitHub token. Update the actions/checkout@v4 configuration to avoid leaving
credentials in .git/config, and keep the existing checkout setup for submodules
and fetch-depth while preserving the later yarn install --frozen-lockfile step.
- Around line 44-58: The current prisma-migrations workflow only checks that the
final schema matches, so it can miss edits to existing applied migration files.
Update the “Apply migrations” / “Check for schema drift” flow in the
prisma-migrations job to also detect rewrites of files under
packages/db/prisma/migrations by comparing the migration directory against main
or by verifying migration checksums/history, so any modification to an existing
timestamped migration fails even if the end schema is unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bd8dea3e-b511-4e8b-b9ff-2f0f52347a2a

📥 Commits

Reviewing files that changed from the base of the PR and between 649bfbf and 01f8e7f.

📒 Files selected for processing (1)
  • .github/workflows/prisma-migrations.yml

Comment thread .github/workflows/prisma-migrations.yml Outdated
Comment thread .github/workflows/prisma-migrations.yml Outdated
brendan-kellam and others added 2 commits June 29, 2026 16:16
Replace the standalone migration workflow with a composite action
(.github/actions/check-prisma-migrations) and embed it in:
- pr-gate.yml's build job, so migration drift or out-of-order
  timestamps fail the required PR check before merge.
- _build.yml's build job (amd64 only) as a release-time backstop
  for schema drift.

The action self-contains Postgres via docker run, detects whether
Prisma files changed (skipping fast on PRs that don't), verifies
migrations apply in sequence and reproduce schema.prisma, and
checks no new migration predates the latest on the base branch.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@brendan-kellam brendan-kellam merged commit c40f931 into main Jun 29, 2026
8 of 9 checks passed
@brendan-kellam brendan-kellam deleted the brendan-kellam/enforce-prisma-migration-order branch June 29, 2026 23:19

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/actions/check-prisma-migrations/action.yml:
- Around line 103-109: The migration check in check-prisma-migrations/action.yml
is collapsing directory names to bare timestamps before comparing new migrations
against the base branch, which can hide same-timestamp migrations with different
suffixes. Update the NEW detection logic in the shell block to compare full
migration directory names first, using the existing MIG_DIR and LATEST_ON_BASE
flow, then extract timestamps only for the age check. Keep the final guard in
the same loop, but reject any new migration whose timestamp is less than or
equal to LATEST_ON_BASE so timestamp-colliding directories are caught.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ff6b4629-0677-4ecf-bce0-ea5ea66cdd7f

📥 Commits

Reviewing files that changed from the base of the PR and between 01f8e7f and 43bb910.

📒 Files selected for processing (3)
  • .github/actions/check-prisma-migrations/action.yml
  • .github/workflows/_build.yml
  • .github/workflows/pr-gate.yml

Comment on lines +103 to +109
NEW=$(comm -23 \
<(ls "$MIG_DIR" | sed -n 's/^\([0-9]\{14\}\)_.*/\1/p' | sort -u) \
<(git ls-tree -r --name-only "$BASE" -- "$MIG_DIR" | sed -n "s#$MIG_DIR/\([0-9]\{14\}\)_.*#\1#p" | sort -u))
FAIL=0
for ts in $NEW; do
if [ -n "$LATEST_ON_BASE" ] && [ "$ts" -lt "$LATEST_ON_BASE" ]; then
echo "❌ New migration $ts predates latest migration on ${{ inputs.base-ref }} ($LATEST_ON_BASE). Rename it with a current timestamp."

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Don't collapse new migrations to timestamps.

Line 103 drops the directory suffix before comm, so a PR migration like 20260629010101_add_index is invisible if the base branch already has a different 20260629010101_* directory. That skips the exact merge-race this guard is supposed to catch. Diff full directory names first, then reject any new migration whose timestamp is <= LATEST_ON_BASE.

Suggested fix
-        NEW=$(comm -23 \
-          <(ls "$MIG_DIR" | sed -n 's/^\([0-9]\{14\}\)_.*/\1/p' | sort -u) \
-          <(git ls-tree -r --name-only "$BASE" -- "$MIG_DIR" | sed -n "s#$MIG_DIR/\([0-9]\{14\}\)_.*#\1#p" | sort -u))
+        NEW=$(comm -23 \
+          <(find "$MIG_DIR" -mindepth 1 -maxdepth 1 -type d -printf '%f\n' | sort) \
+          <(git ls-tree -d --name-only "$BASE" -- "$MIG_DIR" | sed "s#^$MIG_DIR/##" | sort))
         FAIL=0
-        for ts in $NEW; do
-          if [ -n "$LATEST_ON_BASE" ] && [ "$ts" -lt "$LATEST_ON_BASE" ]; then
-            echo "❌ New migration $ts predates latest migration on ${{ inputs.base-ref }} ($LATEST_ON_BASE). Rename it with a current timestamp."
+        for migration in $NEW; do
+          ts=${migration%%_*}
+          if [ -n "$LATEST_ON_BASE" ] && [ "$ts" -le "$LATEST_ON_BASE" ]; then
+            echo "❌ New migration $migration does not sort after latest migration on ${{ inputs.base-ref }} ($LATEST_ON_BASE). Rename it with a newer timestamp."
             FAIL=1
           fi
         done
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/actions/check-prisma-migrations/action.yml around lines 103 - 109,
The migration check in check-prisma-migrations/action.yml is collapsing
directory names to bare timestamps before comparing new migrations against the
base branch, which can hide same-timestamp migrations with different suffixes.
Update the NEW detection logic in the shell block to compare full migration
directory names first, using the existing MIG_DIR and LATEST_ON_BASE flow, then
extract timestamps only for the age check. Keep the final guard in the same
loop, but reject any new migration whose timestamp is less than or equal to
LATEST_ON_BASE so timestamp-colliding directories are caught.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant