Skip to content

Latest commit

 

History

History
577 lines (421 loc) · 17.8 KB

File metadata and controls

577 lines (421 loc) · 17.8 KB

prisma-strong-migrations Workflow

This document explains the actual development workflow using prisma-strong-migrations.

Basic Workflow

┌─────────────────────────────────────────────────────────────┐
│  1. Modify schema.prisma                                    │
└─────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────┐
│  2. Generate migration (without applying)                   │
│     npx prisma migrate dev --create-only --name xxx         │
└─────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────┐
│  3. Safety check                                            │
│     npx prisma-strong-migrations check                      │
└─────────────────────────────────────────────────────────────┘
                              ↓
                    ┌─────────────────┐
                    │  Warnings?      │
                    └─────────────────┘
                     ↓ Yes          ↓ No
        ┌────────────────────┐    ┌────────────────────┐
        │ 4a. Review warning │    │ 4b. Apply          │
        │     Decide action  │    │     migration      │
        └────────────────────┘    └────────────────────┘
                     ↓
        ┌────────────────────┐
        │ 5. Take action     │
        │    - Staged migration │
        │    - Add comment   │
        └────────────────────┘
                     ↓
        ┌────────────────────┐
        │ 6. Re-check        │
        │    → Pass          │
        └────────────────────┘
                     ↓
        ┌────────────────────┐
        │ 7. Apply           │
        │    migration       │
        └────────────────────┘

Handling Warnings

When a warning is issued, there are two approaches:

Approach 1: Staged Migration (Recommended)

Follow the warning instructions to perform the migration safely.

Example: Column Removal

Warning:

=== ❌ Dangerous operation detected ===

📍 Line 1: ALTER TABLE "users" DROP COLUMN "name"

Removing a column may cause errors in your application.

💡 Recommended approach:
   1. Remove all usages of 'name' field from your code
   2. Run 'npx prisma generate' to update Prisma Client
   3. Deploy the code changes
   4. Then apply this migration

Action:

  1. Remove references to name field from application code
  2. Run npx prisma generate
  3. Deploy code changes
  4. Apply the migration
# After code changes, apply migration
npx prisma migrate dev

Approach 2: Skip After Review

If you understand the risks and want to proceed, add a comment.

Comment Format

-- prisma-strong-migrations-disable-next-line <rule_name>
-- Reason: <reason>
<SQL statement>

Example

-- prisma-strong-migrations-disable-next-line remove_column
-- Reason: This is a new table with no production data yet
ALTER TABLE "temp_users" DROP COLUMN "name";

Skip Multiple Rules

-- prisma-strong-migrations-disable-next-line remove_column rename_column
-- Reason: Refactoring unused legacy table
ALTER TABLE "legacy_users" DROP COLUMN "old_name";

Skip All Rules

-- prisma-strong-migrations-disable-next-line
-- Reason: Emergency hotfix, reviewed by @senior-dev
ALTER TABLE "users" DROP COLUMN "name";

Auto-fix (--fix)

Some rules can automatically rewrite the migration SQL to a safer equivalent. Use --fix to apply these fixes without editing the file manually.

Supported rules

Rule What --fix does
addIndex CREATE INDEXCREATE INDEX CONCURRENTLY + adds disable-transaction header
removeIndex DROP INDEXDROP INDEX CONCURRENTLY + adds disable-transaction header
addForeignKey Adds NOT VALID + appends VALIDATE CONSTRAINT as a second statement
addCheckConstraint Adds NOT VALID + appends VALIDATE CONSTRAINT as a second statement
setNotNull Expands into: ADD CONSTRAINT … CHECK NOT VALIDVALIDATE CONSTRAINTSET NOT NULLDROP CONSTRAINT
addUniqueConstraint Replaces with CREATE UNIQUE INDEX CONCURRENTLY + ADD CONSTRAINT … USING INDEX + adds disable-transaction header

Rules that require application code changes (removeColumn, renameColumn, etc.) or human-supplied values cannot be auto-fixed.

Usage

With migrate dev

# 1. Modify schema.prisma, then:
npx prisma-strong-migrations migrate dev --fix
# → Fixes are written to the migration SQL file.
#   The migration is NOT applied yet.

# 2. Review the rewritten SQL, then apply:
npx prisma-strong-migrations migrate dev

Important: --fix is fix-only. Even if all errors were resolved, the migration is never applied in the same run. Always re-run without --fix to apply.

With check

# Fix all auto-fixable issues across all migration files:
npx prisma-strong-migrations check --fix

# Fix a specific migration file:
npx prisma-strong-migrations check prisma/migrations/20240320_add_index/migration.sql --fix

Example: Adding an index

# After schema.prisma change, prisma-strong-migrations generates and checks:
npx prisma-strong-migrations migrate dev --fix

# Output:
# ✔ Auto-fixed 1 issue(s) in prisma/migrations/20240320_.../migration.sql
# ✅ Auto-fix applied. Run the same command again (without --fix) to apply the migration.

# The SQL is now:
# -- prisma-migrate-disable-next-transaction
# CREATE INDEX CONCURRENTLY "User_email_idx" ON "User"("email");

# Review and apply:
npx prisma-strong-migrations migrate dev

When not all issues can be fixed

If a migration contains both auto-fixable and non-fixable errors, --fix applies what it can and then exits with an error listing the remaining issues. Edit those manually before re-running.

Specific Scenarios

Scenario 1: Column Removal

# 1. Remove field from schema.prisma
# model User {
#   id    Int    @id
#   // name  String  ← removed
#   email String
# }

# 2. Generate migration
npx prisma migrate dev --create-only --name remove_user_name

# 3. Check
npx prisma-strong-migrations check
# → remove_column warning

# 4. Choose approach
#    A) Staged migration: Remove references from code → Deploy → Migration
#    B) Skip: Add comment

# For 4B: Add comment
cat >> prisma/migrations/20240320_remove_user_name/migration.sql << 'EOF'
-- prisma-strong-migrations-disable-next-line remove_column
-- Reason: Field unused, confirmed no references in codebase (grep -r "user.name" returned 0 results)
EOF

# 5. Re-check
npx prisma-strong-migrations check
# → Pass

# 6. Apply migration
npx prisma migrate dev

Scenario 2: Adding Index

# 1. Add index to schema.prisma
# model User {
#   id    Int    @id
#   email String
#   @@index([email])  ← added
# }

# 2. Generate migration
npx prisma migrate dev --create-only --name add_user_email_index

# 3. Check
npx prisma-strong-migrations check
# → add_index warning (non-CONCURRENTLY)

# 4. Manually edit migration.sql
vim prisma/migrations/20240320_add_user_email_index/migration.sql

# Before:
# CREATE INDEX "User_email_idx" ON "User"("email");

# After:
# CREATE INDEX CONCURRENTLY "User_email_idx" ON "User"("email");

# 5. Re-check
npx prisma-strong-migrations check
# → Pass

# 6. Apply migration
npx prisma migrate dev

Scenario 3: Adding Foreign Key

# 1. Add relation to schema.prisma
# model Post {
#   id       Int  @id
#   userId   Int
#   user     User @relation(fields: [userId], references: [id])  ← added
# }

# 2. Generate migration
npx prisma migrate dev --create-only --name add_post_user_fk

# 3. Check
npx prisma-strong-migrations check
# → add_foreign_key warning (no NOT VALID)

# 4. Manually edit migration.sql and split into two migrations

# Migration 1: prisma/migrations/20240320_add_post_user_fk/migration.sql
# ALTER TABLE "Post"
# ADD CONSTRAINT "Post_userId_fkey"
# FOREIGN KEY ("userId") REFERENCES "User"("id")
# NOT VALID;

# Migration 2: prisma/migrations/20240321_validate_post_user_fk/migration.sql
# ALTER TABLE "Post"
# VALIDATE CONSTRAINT "Post_userId_fkey";

# 5. Re-check
npx prisma-strong-migrations check
# → Pass

# 6. Apply migration
npx prisma migrate dev

CI/CD Usage

GitHub Actions

name: Migration Check

on:
  pull_request:
    paths:
      - "prisma/schema.prisma"
      - "prisma/migrations/**"

jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Vite+
        uses: voidzero-dev/setup-vp@v1
        with:
          node-version: "22"
          cache: true

      - name: Install dependencies
        run: vp install

      - name: Check migrations
        run: npx prisma-strong-migrations check

PR Warning Display

When check fails, you can also add a comment to the PR:

- name: Check migrations
  id: check
  run: |
    npx prisma-strong-migrations check --format json > report.json
    echo "result=$(cat report.json)" >> $GITHUB_OUTPUT
  continue-on-error: true

- name: Comment on PR
  if: steps.check.outcome == 'failure'
  uses: actions/github-script@v7
  with:
    script: |
      const report = JSON.parse('${{ steps.check.outputs.result }}');
      const body = `## ⚠️ Migration Safety Check Failed

      ${report.errors.map(e => `- **${e.code}**: ${e.message}`).join('\n')}

      Please review the warnings and either:
      1. Follow the recommended safe approach
      2. Add a disable comment with a reason
      `;
      github.rest.issues.createComment({
        issue_number: context.issue.number,
        owner: context.repo.owner,
        repo: context.repo.repo,
        body: body
      });

Best Practices

1. Always Use --create-only

# ✅ Good: Separate generation and application
npx prisma migrate dev --create-only --name xxx
npx prisma-strong-migrations check
npx prisma migrate dev

# ❌ Bad: Direct application (cannot check)
npx prisma migrate dev --name xxx

2. Always Include Reason in Comments

-- ✅ Good: Clear reason
-- prisma-strong-migrations-disable-next-line remove_column
-- Reason: Column deprecated in v2.0, no references found (verified with grep)
ALTER TABLE "users" DROP COLUMN "legacy_field";

-- ❌ Bad: No reason
-- prisma-strong-migrations-disable-next-line remove_column
ALTER TABLE "users" DROP COLUMN "legacy_field";

3. Prefer Staged Migration

Prioritize using the recommended safe approach over skipping warnings.

4. Share Rules with Team

Commit prisma-strong-migrations.config.js to the repository so the entire team uses the same rules.

// prisma-strong-migrations.config.js
module.exports = {
  // Project-specific settings
  customRulesDir: "./prisma-strong-migrations-rules",
};

Command Reference

check [migration]

Check migration files for dangerous operations.

# Check all migrations in migrationsDir
npx prisma-strong-migrations check

# Check a specific migration file
npx prisma-strong-migrations check prisma/migrations/20240320_add_index/migration.sql
Option Description
-f, --format <format> Output format: console (default) or json
-c, --config <path> Path to config file (default: prisma-strong-migrations.config.js)
--no-fail Always exit with code 0, even if errors are found
--fix Automatically rewrite auto-fixable issues in the SQL files

--format json

Outputs a JSON object suitable for CI tooling:

{
  "errors": [
    {
      "ruleName": "addIndex",
      "severity": "error",
      "migrationPath": "prisma/migrations/20240320_.../migration.sql",
      "line": 3,
      "message": "...",
      "suggestion": "..."
    }
  ],
  "warnings": [],
  "totalErrors": 1,
  "totalWarnings": 0
}

--no-fail

Useful when you want to surface issues as information without blocking CI:

# Report issues but never fail the pipeline
npx prisma-strong-migrations check --no-fail

migrate dev

Create a migration with --create-only, check it, then apply if safe.

npx prisma-strong-migrations migrate dev
Option Description
--name <name> Name passed to prisma migrate dev
--schema <path> Path to schema.prisma, passed to Prisma
-c, --config <path> Path to config file
--fix Auto-fix issues, then exit without applying (re-run without --fix to apply)
--force Skip all safety checks — for local dev environment setup only

Any unknown options are forwarded to prisma migrate dev.


migrate deploy

Check all migrations, then run prisma migrate deploy if all checks pass.

npx prisma-strong-migrations migrate deploy
Option Description
-c, --config <path> Path to config file
--force Skip all safety checks — for local dev environment setup only

Any unknown options are forwarded to prisma migrate deploy.


init

Interactive setup wizard. Run once when introducing the tool to a project.

npx prisma-strong-migrations init

What it does:

  1. Creates prisma-strong-migrations.config.js in the current directory with all available options and their defaults
  2. Scans package.json scripts for prisma migrate dev / prisma migrate deploy and interactively offers to replace them with prisma-strong-migrations migrate dev / prisma-strong-migrations migrate deploy

init-rule <name>

Generate a custom rule template.

npx prisma-strong-migrations init-rule my-rule-name

Creates ./prisma-strong-migrations-rules/my-rule-name.js with a skeleton rule implementation. Edit the file to implement detect, message, and suggestion.

See README.md for the full custom rules guide.


Adopting in an Existing Project

When introducing prisma-strong-migrations to an already-running application, the first local environment setup presents a specific challenge:

  • Many migration files exist on disk (e.g., 50+ files)
  • The local database is clean (no migration history)
  • All those files are flagged as "pending" and checked
  • Old migrations trigger errors even though they're safely running in production

Solution: --force

Use --force to skip all safety checks and apply migrations directly:

# Local dev environment setup — bypass all checks
npx prisma-strong-migrations migrate dev --force

# Or with deploy:
npx prisma-strong-migrations migrate deploy --force

A warning is printed when --force is used:

⚠️  --force: Safety checks skipped. Use only for local dev environment setup.

After the initial setup is done, run without --force as usual for day-to-day development.

Important: Never use --force in CI/CD pipelines or production deployments. It is intended exclusively for local development environment bootstrap.

Troubleshooting

Check Fails in CI but Passes Locally

  1. Ensure the same Node.js version is used
  2. Check if config file is committed
  3. Verify migration files are committed

Parser Errors

If the SQL parser fails:

  1. Check if the SQL syntax is valid
  2. Some PostgreSQL-specific syntax may not be supported
  3. Report issues with the SQL that failed

False Positives

If a warning is incorrect:

  1. Check if the rule is appropriate for your use case
  2. Consider disabling the rule in config
  3. Report as an issue if it's a bug