Commit 7dd2b66
committed
ci(migrations): add migration safety linter
Adds a custom Python AST-based linter that fails CI on PRs introducing
the four anti-patterns the migration runtime defaults cannot reliably
catch on a quiet hour:
* op.create_index (and raw CREATE INDEX) without CONCURRENTLY,
or with CONCURRENTLY but outside an autocommit_block().
* op.create_foreign_key (and raw ADD CONSTRAINT FOREIGN KEY)
without NOT VALID.
* op.execute("UPDATE ...") / DELETE in-band data backfills.
* SET / RESET of lock_timeout, statement_timeout, or
idle_in_transaction_session_timeout inside a migration file.
Each rule maps to a documented anti-pattern in CLAUDE.md.
Why custom AST instead of squawk: squawk operates on rendered SQL,
which loses the surrounding Alembic context (autocommit_block wrapping,
kwarg options on op.create_index/op.create_foreign_key) — exactly the
signals needed to distinguish safe and unsafe shapes here. AST
inspection of the migration source is tighter, has zero external deps,
runs in milliseconds, and lets each rule produce a message that points
at the correct fix.
Escape hatch: a `# migration-unsafe-ack: <one-line reason>` directive
at the top of the migration file suppresses all rules for that file.
The GitHub Actions workflow additionally checks for a
`migration-unsafe-ack` PR label; when present it runs the linter in
--warn-only mode so violations surface in logs without blocking merge.
Both signals are required to deploy a migration that intentionally
overrides the runner defaults.
The CI step lints only files changed in the PR (via
`--base-ref origin/<base>`), so existing migrations are not
retro-flagged.
Includes 12 unit tests covering each rule, the ack-directive escape
hatch (including that bare directives without a reason do not
suppress), and the autocommit_block detection.1 parent b3ea63a commit 7dd2b66
6 files changed
Lines changed: 923 additions & 18 deletions
File tree
- .github/workflows
- agentex
- scripts
- ci_tools
- git_hooks
- tests/unit/scripts
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
25 | 25 | | |
26 | 26 | | |
27 | 27 | | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
0 commit comments