{"id":28470222,"url":"https://github.com/mileszim/prisma-strong-migrations","last_synced_at":"2026-06-09T02:01:30.463Z","repository":{"id":296294060,"uuid":"992898646","full_name":"mileszim/prisma-strong-migrations","owner":"mileszim","description":"A linter for Prisma migrations to ensure safe SQL deployments","archived":false,"fork":false,"pushed_at":"2026-06-09T00:21:20.000Z","size":268,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-09T00:22:23.539Z","etag":null,"topics":["ci-cd","linter","migrations","prisma","prisma-orm","sql"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mileszim.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-05-29T22:18:01.000Z","updated_at":"2026-06-09T00:20:56.000Z","dependencies_parsed_at":"2025-05-29T23:28:14.495Z","dependency_job_id":"cf8b01b7-41a8-4a81-9903-5a7e4c1f273c","html_url":"https://github.com/mileszim/prisma-strong-migrations","commit_stats":null,"previous_names":["mileszim/prisma-strong-migrations"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/mileszim/prisma-strong-migrations","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mileszim%2Fprisma-strong-migrations","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mileszim%2Fprisma-strong-migrations/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mileszim%2Fprisma-strong-migrations/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mileszim%2Fprisma-strong-migrations/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mileszim","download_url":"https://codeload.github.com/mileszim/prisma-strong-migrations/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mileszim%2Fprisma-strong-migrations/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34088013,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-09T02:00:06.510Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ci-cd","linter","migrations","prisma","prisma-orm","sql"],"created_at":"2025-06-07T09:10:26.067Z","updated_at":"2026-06-09T02:01:30.453Z","avatar_url":"https://github.com/mileszim.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Prisma Strong Migrations\n\nLint the SQL in your Prisma migrations for patterns that are dangerous to apply\nto a live production database — **before** they merge.\n\nPrisma generates a `migration.sql` file for every migration. Most are harmless,\nbut a few patterns can take your database down, lose data, or break the app\nmid-deploy. This tool reads those SQL files, understands them via a real SQL\nparser (not string matching), and flags the dangerous ones with an explanation\nand a safer alternative. It runs as a **GitHub Action** on your pull requests, or\nas a **CLI** anywhere.\n\nIt's the [`strong_migrations`](https://github.com/ankane/strong_migrations) /\n[Squawk](https://squawkhq.com) idea, tuned to the SQL Prisma emits and to how\nPrisma applies migrations.\n\n\u003e **Scope:** PostgreSQL today. The architecture is dialect-aware so more engines\n\u003e can be added; until then non-Postgres projects aren't supported.\n\n## What it catches\n\nEvery rule maps to a real way a migration hurts you in production:\n\n| Failure mode | Example | Why it's dangerous |\n| --- | --- | --- |\n| **Destructive** | `DROP TABLE`, `DROP COLUMN` | Data is gone, and old code still reading it errors. |\n| **Backwards-incompatible** | renames, column type changes | The previously-deployed app breaks during the rolling deploy. |\n| **Locking** | `CREATE INDEX`, `SET NOT NULL`, validating constraints | Heavy locks or full-table scans stall reads/writes. |\n| **Correctness** | `ADD COLUMN ... NOT NULL` with no default | The migration fails outright on a non-empty table. |\n\nSee the [full rule list](#rules) below.\n\n## Quick start (GitHub Action)\n\nAdd a workflow that runs on pull requests. Findings show up as inline\nannotations on the diff.\n\n```yaml\n# .github/workflows/migration-safety.yml\nname: Migration Safety\non: pull_request\n\npermissions:\n  contents: read\n\njobs:\n  lint-migrations:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          fetch-depth: 0 # needed for changed-only detection\n      - uses: mileszim/prisma-strong-migrations@v1\n        with:\n          changed-only: true # only lint migrations added in this PR\n```\n\n### Action inputs\n\n| Input | Default | Description |\n| --- | --- | --- |\n| `migrations-dir` | `./prisma/migrations` | Where your migrations live. |\n| `changed-only` | `false` | Only lint migrations changed in this event. On a PR it diffs the base branch; on a push it diffs the pre-push commit. Use `fetch-depth: 0` for the most precise comparison. |\n| `base` | auto | Git ref to diff against for `changed-only`. Defaults to the PR base branch (PRs) or the pre-push commit (pushes). |\n| `fail-on` | `error` | Severity that fails the check: `error` or `warning`. |\n| `reporter` | `github` | `github` (inline annotations), `stylish`, or `json`. |\n| `config` | _auto_ | Path to a config file. |\n| `working-directory` | `.` | Directory containing the Prisma project. |\n| `node-version` | `20` | Node.js version used to run the linter. |\n\n## Quick start (CLI)\n\n```bash\nnpm install --save-dev prisma-strong-migrations\n\n# Lint every migration\nnpx prisma-strong-migrations lint\n\n# Lint only what changed versus a base branch (great locally and in CI)\nnpx prisma-strong-migrations lint --changed --base origin/main\n\n# Lint specific files\nnpx prisma-strong-migrations lint prisma/migrations/20240101_init/migration.sql\n\n# Other reporters\nnpx prisma-strong-migrations lint --reporter json\n\n# See every rule and its default\nnpx prisma-strong-migrations list-rules\n\n# Write a starter config\nnpx prisma-strong-migrations init\n```\n\nThe CLI exits non-zero when it finds an issue at or above `--fail-on` (default\n`error`), so it fails CI on its own.\n\nExample output:\n\n```\nprisma/migrations/20240201_risky/migration.sql\n  2:20  error    Dropping column \"bio\" from \"User\" is destructive.  no-drop-column\n                 why: The column data is lost, and any deployed code that still selects it will error until redeployed.\n                 fix: Remove the field from your Prisma schema and deploy that first, then drop the column in a follow-up migration.\n\n✖ 1 problem (1 error)\n```\n\n## Configuration\n\nConfiguration is optional — the defaults are sensible. To customize, run\n`prisma-strong-migrations init` or create a `prisma-strong-migrations.config.js`\n(also supports `.cjs`, `.json`, an `.prisma-strong-migrationsrc`, or a\n`\"prisma-strong-migrations\"` key in `package.json`):\n\n```js\n/** @type {import('prisma-strong-migrations').UserConfig} */\nmodule.exports = {\n  migrationsDir: './prisma/migrations',\n  dialect: 'postgresql',\n  failOn: 'error', // 'error' | 'warning'\n  rules: {\n    // Set a severity...\n    'no-data-manipulation': 'error',\n    // ...turn a rule off...\n    'require-concurrent-index': 'off',\n    // ...or enable an opt-in rule.\n    'require-pii-comment': 'warning',\n  },\n};\n```\n\nEach rule can be set to `'error'`, `'warning'`, `'off'`, `true` (its default\nseverity), `false` (off), or `{ severity, enabled }`.\n\n## Rules\n\nRun `prisma-strong-migrations list-rules` for the live list. Rules marked\n**opt-in** are off by default; enable them in config.\n\n### Destructive\n\n- **`no-drop-table`** (error) — Dropping a table deletes its data and breaks code still reading it.\n- **`no-drop-column`** (error) — Dropping a column deletes its data; Prisma keeps selecting every scalar field, so old code errors.\n\n### Backwards-incompatible\n\n- **`no-rename-column`** (error) — A rename is a drop + add to the running app; old code queries a column that's gone. Use expand-and-contract.\n- **`no-rename-table`** (error) — The deployed app keeps querying the old name. Use `@@map` or expand-and-contract.\n- **`no-change-column-type`** (error) — Usually rewrites the table under an exclusive lock and may break the running app. A `USING` clause doesn't make it safe.\n\n### Correctness\n\n- **`no-add-not-null-column-without-default`** (error) — `ADD COLUMN ... NOT NULL` with no default aborts on a table that already has rows.\n\n### Locking\n\n- **`no-set-not-null`** (warning) — `SET NOT NULL` scans the whole table under an exclusive lock and fails if any value is null. Use a validated `CHECK (... IS NOT NULL)` first.\n- **`require-concurrent-index`** (warning) — `CREATE INDEX` without `CONCURRENTLY` blocks writes while it builds.\n- **`constraint-missing-not-valid`** (warning) — Adding a `CHECK` or `FOREIGN KEY` validates every existing row under a lock. Add it `NOT VALID`, then `VALIDATE` separately.\n- **`no-data-manipulation`** (warning) — `INSERT`/`UPDATE`/`DELETE` inside a schema migration runs under the migration lock and can stall the deploy.\n\n### Opinionated (opt-in)\n\n- **`require-explicit-not-null`** (off) — Require new columns to declare `NULL` or `NOT NULL` explicitly.\n- **`require-pii-comment`** (off) — Flag columns whose names look like personal data, for a compliance review.\n- **`no-unindexed-foreign-key`** (off) — Flag foreign keys whose referencing column has no covering index in the same migration.\n\n### A note on `CREATE INDEX CONCURRENTLY` and Prisma\n\nPrisma wraps each migration in a transaction, and `CREATE INDEX CONCURRENTLY`\ncannot run inside one. To follow `require-concurrent-index`, put the concurrent\nindex in its own migration and apply it outside the transactional batch (for\nexample with `prisma db execute`). The rule's suggestion text spells this out.\n\n## Programmatic API\n\n```ts\nimport { lintProject, shouldFail, stylish } from 'prisma-strong-migrations';\n\nconst { result, config } = lintProject({\n  overrides: { migrationsDir: './prisma/migrations' },\n  changedSince: 'origin/main', // optional\n});\n\nconsole.log(stylish(result));\nprocess.exit(shouldFail(result, config.failOn) ? 1 : 0);\n```\n\nLower-level building blocks are exported too: `parseSql`, `loadMigrations`,\n`lint`, `loadConfig`, `ALL_RULES`, and the reporters.\n\n## How it works\n\n1. Find every `migration.sql` under `migrationsDir` (or just the changed ones).\n2. Parse each file with [`sql-parser-cst`](https://github.com/nene/sql-parser-cst)\n   into a concrete syntax tree. Statements that don't parse are reported, never\n   silently skipped.\n3. Run each enabled rule against the tree. Rules read the structure of the SQL\n   (statement kind, table, columns, clauses) — not substrings — so they don't\n   trip over identifiers that happen to contain keywords.\n4. Report findings with the configured severity, sorted by location.\n\n## Limitations\n\n- **PostgreSQL only** for now.\n- Static analysis can't know table size or row contents, so locking/correctness\n  rules flag the _pattern_; whether it actually causes pain depends on your data.\n  Tune severities per rule.\n\n## Development\n\n```bash\nnpm install\nnpm run build      # compile TypeScript to dist/\nnpm test           # run the vitest suite\nnpm run lint       # eslint\nnpm run typecheck  # tsc --noEmit\n```\n\nEach rule is a small module in `src/rules/` that reads facts from the parsed\ntree via helpers in `src/rules/ast.ts` and calls `ctx.report(...)`. Add a rule by\ncreating the module, registering it in `src/rules/index.ts`, and adding a test in\n`test/rules.test.ts`.\n\n## License\n\nMIT — see [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmileszim%2Fprisma-strong-migrations","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmileszim%2Fprisma-strong-migrations","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmileszim%2Fprisma-strong-migrations/lists"}