migraguard 0.6.2 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +68 -48
- package/dist/cli.js +1382 -205
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +25 -2
- package/dist/index.js +625 -99
- package/dist/index.js.map +1 -1
- package/package.json +12 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/migraguard) [](https://opensource.org/licenses/MIT)
|
|
4
4
|
|
|
5
|
-
PostgreSQL schema-aware deployment control. Idempotent SQL migrations with CI-enforced integrity checks, expand/contract migration orchestration, schema drift detection, and unified gating across database, application, and infrastructure rollouts.
|
|
5
|
+
PostgreSQL-first schema-aware deployment control. Idempotent SQL migrations with CI-enforced integrity checks, expand/contract migration orchestration, schema drift detection, and unified gating across database, application, and infrastructure rollouts. **Optional feature-limited linting** for MySQL and SQLite uses a generic SQL parser ([node-sql-parser](https://github.com/taozhi8833998/node-sql-parser)); full rule coverage and all runtime DB features remain PostgreSQL-only.
|
|
6
6
|
|
|
7
7
|
**Prevented accidents:**
|
|
8
8
|
|
|
@@ -50,7 +50,22 @@ npx migraguard dump
|
|
|
50
50
|
- **One release = one file**: Migration files are squashed into a single file before release, simplifying error recovery. In DAG mode, independent DDL can be released individually
|
|
51
51
|
- **Parallel releases via dependency tree**: DDL dependencies are analyzed to build a DAG, enabling parallel releases for independent changes
|
|
52
52
|
- **Shift verification left**: Linting, checksum-based tamper detection, and schema dump diffs run at the PR stage
|
|
53
|
-
- **Minimal footprint**: Two CLI tools (`psql`, `pg_dump`) and one npm library ([libpg-query](https://github.com/pganalyze/libpg-query)). No external linter required — lint rules are built in via AST analysis
|
|
53
|
+
- **Minimal footprint**: Two CLI tools (`psql`, `pg_dump`) and one npm library ([libpg-query](https://github.com/pganalyze/libpg-query)) for the primary PostgreSQL path. No external linter required — lint rules are built in via AST analysis. MySQL/SQLite linting adds [node-sql-parser](https://github.com/taozhi8833998/node-sql-parser) as a parallel, feature-limited engine
|
|
54
|
+
|
|
55
|
+
## Dialect support
|
|
56
|
+
|
|
57
|
+
PostgreSQL remains the **primary, full-featured** target: `libpg-query` powers all built-in lint rules (38) and unchanged behavior when `dialect` is `postgresql` (default).
|
|
58
|
+
|
|
59
|
+
Setting `dialect` to `mysql` or `sqlite` selects the **generic** engine (`src/generic/`), which uses **node-sql-parser** and enforces **17 generic lint rules** only. Parser coverage and PostgreSQL-specific semantics are not replicated; this mode is for teams that want migraguard’s file workflow and a subset of safety checks on non-PostgreSQL SQL, not parity with the PG toolchain.
|
|
60
|
+
|
|
61
|
+
| Concern | `postgresql` (default) | `mysql` / `sqlite` |
|
|
62
|
+
|---------|------------------------|---------------------|
|
|
63
|
+
| Lint AST | libpg-query (38 rules) | node-sql-parser (17 rules) |
|
|
64
|
+
| `deps` / DAG extraction | ✅ Same object/relationship extraction | ✅ Same categories (e.g. CREATE TABLE, CREATE INDEX, ALTER TABLE, CREATE VIEW, FK references) |
|
|
65
|
+
| `apply`, `dump`, `diff`, `verify`, DB connection | ✅ | ❌ Not supported (PostgreSQL only) |
|
|
66
|
+
| File-based commands (`check`, `new`, `squash`, `editable`, `status`, …) | ✅ | ✅ |
|
|
67
|
+
|
|
68
|
+
Omitting `dialect` is equivalent to `"postgresql"` — existing projects require no config change.
|
|
54
69
|
|
|
55
70
|
## Core Concepts
|
|
56
71
|
|
|
@@ -204,6 +219,8 @@ See [docs/expand-contract.md](docs/expand-contract.md) for the complete guide: f
|
|
|
204
219
|
| `gate` | Evaluate deployment gate conditions |
|
|
205
220
|
| `baseline` | Squash applied migrations into `schema.sql` |
|
|
206
221
|
|
|
222
|
+
`lint` and `deps` honor `dialect` (PostgreSQL vs generic). Commands that open a DB connection (`apply`, `dump`, `diff`, `verify`, and related) remain **PostgreSQL-only**. See [Dialect support](#dialect-support).
|
|
223
|
+
|
|
207
224
|
See [docs/commands.md](docs/commands.md) for detailed usage, options, and examples.
|
|
208
225
|
|
|
209
226
|
## CI Integration
|
|
@@ -267,6 +284,7 @@ jobs:
|
|
|
267
284
|
|
|
268
285
|
```json
|
|
269
286
|
{
|
|
287
|
+
"dialect": "postgresql",
|
|
270
288
|
"model": "dag",
|
|
271
289
|
"migrationsDirs": ["db/migrations"],
|
|
272
290
|
"schemaFile": "db/schema.sql",
|
|
@@ -304,6 +322,7 @@ jobs:
|
|
|
304
322
|
|
|
305
323
|
| Key | Default | Description |
|
|
306
324
|
|-----|---------|-------------|
|
|
325
|
+
| `dialect` | `"postgresql"` | SQL dialect for lint and dependency analysis: `"postgresql"` (libpg-query, full rules), `"mysql"` or `"sqlite"` (node-sql-parser, 17 generic rules). Omitted means `"postgresql"` |
|
|
307
326
|
| `model` | _(unset = linear)_ | Set to `"dag"` to enable DAG mode. When set in config, takes precedence over `metadata.json` |
|
|
308
327
|
|
|
309
328
|
### Naming Configuration
|
|
@@ -368,48 +387,48 @@ CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_users_email ON users (email);
|
|
|
368
387
|
UPDATE users SET status = 'active' WHERE status IS NULL;
|
|
369
388
|
```
|
|
370
389
|
|
|
371
|
-
`migraguard lint` enforces these patterns with built-in rules (no external tools required):
|
|
372
|
-
|
|
373
|
-
| Rule | Detects |
|
|
374
|
-
|
|
375
|
-
| `require-if-not-exists` | CREATE/DROP without IF NOT EXISTS / IF EXISTS |
|
|
376
|
-
| `require-concurrent-index` | CREATE INDEX without CONCURRENTLY on existing tables |
|
|
377
|
-
| `require-drop-index-concurrently` | DROP INDEX without CONCURRENTLY |
|
|
378
|
-
| `require-lock-timeout` | DDL without prior SET lock_timeout |
|
|
379
|
-
| `require-statement-timeout` | DDL without prior SET statement_timeout |
|
|
380
|
-
| `require-reset-timeouts` | SET lock/statement_timeout without RESET at end |
|
|
381
|
-
| `require-analyze-after-index` | CREATE INDEX without subsequent ANALYZE \<table\> |
|
|
382
|
-
| `require-create-or-replace-view` | CREATE VIEW without OR REPLACE |
|
|
383
|
-
| `require-unique-via-concurrent-index` | UNIQUE constraint added directly (not via USING INDEX) |
|
|
384
|
-
| `ban-concurrent-index-in-transaction` | CONCURRENTLY inside BEGIN...COMMIT |
|
|
385
|
-
| `ban-drop-cascade` | DROP
|
|
386
|
-
| `ban-truncate` | TRUNCATE |
|
|
387
|
-
| `ban-update-without-where` | UPDATE without WHERE |
|
|
388
|
-
| `ban-delete-without-where` | DELETE without WHERE |
|
|
389
|
-
| `ban-drop-column` | ALTER TABLE
|
|
390
|
-
| `ban-alter-column-type` | ALTER TABLE
|
|
391
|
-
| `ban-validate-constraint-same-file` | VALIDATE CONSTRAINT in same file as NOT VALID |
|
|
392
|
-
| `ban-bare-analyze` | ANALYZE without table name |
|
|
393
|
-
| `adding-not-nullable-field` | NOT NULL column without DEFAULT |
|
|
394
|
-
| `constraint-missing-not-valid` | ADD CONSTRAINT (FK/CHECK) without NOT VALID |
|
|
395
|
-
| `ban-select-star-in-view` | SELECT * in VIEW / MATERIALIZED VIEW definitions |
|
|
396
|
-
| `require-if-not-exists-materialized-view` | CREATE MATERIALIZED VIEW without IF NOT EXISTS |
|
|
397
|
-
| `ban-refresh-materialized-view-in-migration` | REFRESH MATERIALIZED VIEW in migration files |
|
|
398
|
-
| `ban-rename-column` | ALTER TABLE
|
|
399
|
-
| `ban-rename-table` | ALTER TABLE
|
|
400
|
-
| `ban-drop-table` | DROP TABLE |
|
|
401
|
-
| `require-pk-via-concurrent-index` | PRIMARY KEY added directly (not via USING INDEX) |
|
|
402
|
-
| `ban-set-not-null` | ALTER COLUMN
|
|
403
|
-
| `ban-alter-enum-in-transaction` | ALTER TYPE
|
|
404
|
-
| `ban-vacuum-full` | VACUUM FULL (table rewrite + ACCESS EXCLUSIVE lock) |
|
|
405
|
-
| `ban-cluster` | CLUSTER (table rewrite + ACCESS EXCLUSIVE lock) |
|
|
406
|
-
| `ban-reindex` | REINDEX (heavy locks — run as operational job) |
|
|
407
|
-
| `ban-alter-system` | ALTER SYSTEM (cluster-wide config change) |
|
|
408
|
-
| `ban-set-session-replication-role` | SET session_replication_role (disables triggers/FK) |
|
|
409
|
-
| `expand-requires-idempotent-pattern` | CREATE without IF NOT EXISTS in expand phase *(expand only)* |
|
|
410
|
-
| `backfill-requires-where-clause` | UPDATE/DELETE without WHERE in backfill phase *(backfill only)* |
|
|
411
|
-
| `backfill-ban-ddl` | DDL statements in backfill phase *(backfill only)* |
|
|
412
|
-
| `contract-requires-allow-directive` | DROP without migraguard:allow in contract phase *(contract only)* |
|
|
390
|
+
`migraguard lint` enforces these patterns with built-in rules (no external tools required). **Scope** indicates which `dialect` values run the rule: **Generic** — enforced for `mysql` and `sqlite` (17 rules total on the node-sql-parser path); also part of the full PostgreSQL ruleset when `dialect` is `postgresql`. **PostgreSQL** — only when `dialect` is `postgresql` (libpg-query).
|
|
391
|
+
|
|
392
|
+
| Rule | Scope | Detects |
|
|
393
|
+
|------|-------|---------|
|
|
394
|
+
| `require-if-not-exists` | Generic | CREATE/DROP without IF NOT EXISTS / IF EXISTS |
|
|
395
|
+
| `require-concurrent-index` | PostgreSQL | CREATE INDEX without CONCURRENTLY on existing tables |
|
|
396
|
+
| `require-drop-index-concurrently` | PostgreSQL | DROP INDEX without CONCURRENTLY |
|
|
397
|
+
| `require-lock-timeout` | PostgreSQL | DDL without prior SET lock_timeout |
|
|
398
|
+
| `require-statement-timeout` | PostgreSQL | DDL without prior SET statement_timeout |
|
|
399
|
+
| `require-reset-timeouts` | PostgreSQL | SET lock/statement_timeout without RESET at end |
|
|
400
|
+
| `require-analyze-after-index` | PostgreSQL | CREATE INDEX without subsequent ANALYZE \<table\> |
|
|
401
|
+
| `require-create-or-replace-view` | Generic | CREATE VIEW without OR REPLACE |
|
|
402
|
+
| `require-unique-via-concurrent-index` | PostgreSQL | UNIQUE constraint added directly (not via USING INDEX) |
|
|
403
|
+
| `ban-concurrent-index-in-transaction` | PostgreSQL | CONCURRENTLY inside BEGIN...COMMIT |
|
|
404
|
+
| `ban-drop-cascade` | Generic | DROP … CASCADE (generic engine: regex; parser does not model CASCADE) |
|
|
405
|
+
| `ban-truncate` | Generic | TRUNCATE |
|
|
406
|
+
| `ban-update-without-where` | Generic | UPDATE without WHERE |
|
|
407
|
+
| `ban-delete-without-where` | Generic | DELETE without WHERE |
|
|
408
|
+
| `ban-drop-column` | Generic | ALTER TABLE … DROP COLUMN |
|
|
409
|
+
| `ban-alter-column-type` | Generic | ALTER TABLE … ALTER COLUMN TYPE |
|
|
410
|
+
| `ban-validate-constraint-same-file` | PostgreSQL | VALIDATE CONSTRAINT in same file as NOT VALID |
|
|
411
|
+
| `ban-bare-analyze` | PostgreSQL | ANALYZE without table name |
|
|
412
|
+
| `adding-not-nullable-field` | Generic | NOT NULL column without DEFAULT |
|
|
413
|
+
| `constraint-missing-not-valid` | PostgreSQL | ADD CONSTRAINT (FK/CHECK) without NOT VALID |
|
|
414
|
+
| `ban-select-star-in-view` | Generic | SELECT * in VIEW / MATERIALIZED VIEW definitions |
|
|
415
|
+
| `require-if-not-exists-materialized-view` | PostgreSQL | CREATE MATERIALIZED VIEW without IF NOT EXISTS |
|
|
416
|
+
| `ban-refresh-materialized-view-in-migration` | PostgreSQL | REFRESH MATERIALIZED VIEW in migration files |
|
|
417
|
+
| `ban-rename-column` | Generic | ALTER TABLE … RENAME COLUMN |
|
|
418
|
+
| `ban-rename-table` | Generic | ALTER TABLE … RENAME TO |
|
|
419
|
+
| `ban-drop-table` | Generic | DROP TABLE |
|
|
420
|
+
| `require-pk-via-concurrent-index` | PostgreSQL | PRIMARY KEY added directly (not via USING INDEX) |
|
|
421
|
+
| `ban-set-not-null` | PostgreSQL | ALTER COLUMN … SET NOT NULL (use CHECK NOT VALID pattern) |
|
|
422
|
+
| `ban-alter-enum-in-transaction` | PostgreSQL | ALTER TYPE … ADD VALUE inside BEGIN…COMMIT |
|
|
423
|
+
| `ban-vacuum-full` | PostgreSQL | VACUUM FULL (table rewrite + ACCESS EXCLUSIVE lock) |
|
|
424
|
+
| `ban-cluster` | PostgreSQL | CLUSTER (table rewrite + ACCESS EXCLUSIVE lock) |
|
|
425
|
+
| `ban-reindex` | PostgreSQL | REINDEX (heavy locks — run as operational job) |
|
|
426
|
+
| `ban-alter-system` | PostgreSQL | ALTER SYSTEM (cluster-wide config change) |
|
|
427
|
+
| `ban-set-session-replication-role` | PostgreSQL | SET session_replication_role (disables triggers/FK) |
|
|
428
|
+
| `expand-requires-idempotent-pattern` | Generic | CREATE without IF NOT EXISTS in expand phase *(expand only)* |
|
|
429
|
+
| `backfill-requires-where-clause` | Generic | UPDATE/DELETE without WHERE in backfill phase *(backfill only)* |
|
|
430
|
+
| `backfill-ban-ddl` | Generic | DDL statements in backfill phase *(backfill only)* |
|
|
431
|
+
| `contract-requires-allow-directive` | Generic | DROP without migraguard:allow in contract phase *(contract only)* |
|
|
413
432
|
|
|
414
433
|
Each rule can be set to `"error"` (default — fail lint), `"warn"` (report but pass), or `"off"` (skip). Phase-specific rules (marked above) only activate for the corresponding expand/contract phase file. Per-file exceptions use a comment directive:
|
|
415
434
|
|
|
@@ -470,7 +489,7 @@ Start with the linear model. Switch to DAG when:
|
|
|
470
489
|
|
|
471
490
|
### How It Works
|
|
472
491
|
|
|
473
|
-
Each migration SQL is parsed into an AST
|
|
492
|
+
Each migration SQL is parsed into an AST to extract object creation/reference relationships and build the DAG: **postgresql** uses `libpg-query`; **mysql** / **sqlite** use node-sql-parser. Auto-extraction covers `CREATE TABLE`, `ALTER TABLE`, `CREATE INDEX`, `CREATE VIEW`, and FK references (exact coverage differs by dialect). For cases beyond auto-extraction (dynamic SQL, `DO` blocks, business-logic ordering), explicit dependency declarations are available:
|
|
474
493
|
|
|
475
494
|
```sql
|
|
476
495
|
-- migraguard:depends-on 20260228_120000__create_users_table.sql
|
|
@@ -493,7 +512,7 @@ migraguard embeds operational policies into the tool and prevents incidents via
|
|
|
493
512
|
| **Failure handling** | DB-recorded, explicit resolve | repair overwrites | manual fix | revert scripts | manual fix |
|
|
494
513
|
| **Execution** | psql (plain SQL) | Java / JDBC | Go / DB driver | psql / sqitch | pg (Node.js) |
|
|
495
514
|
|
|
496
|
-
**vs Flyway / Liquibase**: migraguard adds offline CI tamper detection, regression detection, idempotency proof, and apply mutual exclusion. Trade-off:
|
|
515
|
+
**vs Flyway / Liquibase**: migraguard adds offline CI tamper detection, regression detection, idempotency proof, and apply mutual exclusion. Trade-off: execution and verification remain PostgreSQL-oriented; optional **feature-limited** lint/`deps` for MySQL/SQLite do not imply full multi-DB runtime support. No GUI or rich generic execution engine.
|
|
497
516
|
|
|
498
517
|
**vs Atlas**: Atlas drives migration from a "desired state" declaration. migraguard focuses on preventing release-level operational incidents via explicit CI gates, plus parallel releases via DAG. Choose Atlas for declarative schema generation; choose migraguard for teams writing DDL directly with incident guardrails.
|
|
499
518
|
|
|
@@ -536,8 +555,9 @@ No. `verify` creates a temporary shadow DB, applies migrations twice, then drops
|
|
|
536
555
|
| Language | TypeScript (Node.js) |
|
|
537
556
|
| DB execution | `psql` CLI |
|
|
538
557
|
| Schema dump | `pg_dump --schema-only` |
|
|
539
|
-
| SQL lint | Built-in rules via [libpg-query](https://github.com/pganalyze/libpg-query) AST analysis |
|
|
540
|
-
| SQL parser | [libpg-query](https://github.com/pganalyze/libpg-query) (PostgreSQL real parser WASM build) |
|
|
558
|
+
| SQL lint (PostgreSQL) | Built-in rules via [libpg-query](https://github.com/pganalyze/libpg-query) AST analysis (38 rules) |
|
|
559
|
+
| SQL parser (PostgreSQL) | [libpg-query](https://github.com/pganalyze/libpg-query) (PostgreSQL real parser WASM build) |
|
|
560
|
+
| SQL lint / parser (MySQL, SQLite) | [node-sql-parser](https://github.com/taozhi8833998/node-sql-parser) — generic AST, 17 rules, parallel to PG engine |
|
|
541
561
|
| Package manager | npm |
|
|
542
562
|
|
|
543
563
|
## Detailed Documentation
|