migraguard 0.7.0 → 0.8.1

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 CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![npm version](https://badge.fury.io/js/migraguard.svg)](https://www.npmjs.com/package/migraguard) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
4
4
 
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.
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. MySQL and SQLite are supported as secondary dialects: all DB runtime commands (`apply`, `dump`, `diff`, `verify`) work via `mysql`/`mysqldump` and `sqlite3` CLIs respectively, with 17 generic lint rules powered by [node-sql-parser](https://github.com/taozhi8833998/node-sql-parser). PostgreSQL retains full rule coverage (38 rules) via [libpg-query](https://github.com/pganalyze/libpg-query).
6
6
 
7
7
  **Prevented accidents:**
8
8
 
@@ -12,7 +12,7 @@ PostgreSQL-first schema-aware deployment control. Idempotent SQL migrations with
12
12
  - **Concurrent apply race conditions** — parallel CI pipelines or manual executions collide
13
13
  - **Schema drift** — unauthorized manual DDL diverges the DB from expected state
14
14
 
15
- Execution is deliberately simple: plain SQL files executed via `psql`. migraguard focuses on **what to forbid**, not on providing a rich execution engine.
15
+ Execution is deliberately simple: plain SQL files executed via the database's native CLI (`psql`, `mysql`, or `sqlite3`). migraguard focuses on **what to forbid**, not on providing a rich execution engine.
16
16
 
17
17
  ## Key Guarantees
18
18
 
@@ -20,15 +20,21 @@ Execution is deliberately simple: plain SQL files executed via `psql`. migraguar
20
20
  - **Regression detection** — If a hotfixed file reverts to an old checksum, `apply` raises an error immediately
21
21
  - **Failure blocking with explicit resolve** — A `failed` migration blocks all progress until a human explicitly judges and resolves it
22
22
  - **Drift gate + Idempotency proof** — two [verification mechanisms](#verification-two-distinct-mechanisms): `apply --with-drift-check` detects local schema divergence before applying; `diff` verifies post-deploy schema consistency; `verify` proves migrations are safely re-executable on a shadow DB
23
- - **Mutual exclusion** — `apply` uses PostgreSQL advisory locks to prevent concurrent execution
23
+ - **Mutual exclusion** — `apply` uses advisory locks to prevent concurrent execution (PostgreSQL `pg_advisory_lock`, MySQL `GET_LOCK`, SQLite file-level locking)
24
24
  - **One release at a time** — the next migration cannot be added until the current release is deployed to all environments, ensuring the latest file is always hotfix-ready
25
25
 
26
26
  ## Quick Start
27
27
 
28
28
  ```bash
29
- # Install
29
+ # Install (PostgreSQL — no extra deps needed)
30
30
  npm install --save-dev migraguard
31
31
 
32
+ # For MySQL, also install the driver:
33
+ npm install mysql2
34
+
35
+ # For SQLite, also install the driver:
36
+ npm install better-sqlite3
37
+
32
38
  # Create a new migration → edit the generated file → apply to local DB
33
39
  npx migraguard new create_users_table
34
40
  # → Created: db/migrations/20260301_120000__create_users_table.sql
@@ -45,25 +51,30 @@ npx migraguard dump
45
51
 
46
52
  ## Design Philosophy
47
53
 
48
- - **Plain SQL**: Migrations are SQL files executable via `psql -f`. No ORM or DSL; transaction boundaries are explicit in SQL
54
+ - **Plain SQL**: Migrations are SQL files executable via the database's native CLI (`psql -f`, `mysql`, `sqlite3`). No ORM or DSL; transaction boundaries are explicit in SQL
49
55
  - **Forward-only**: Modifying applied migrations is prohibited by default; changes always build forward. Only the latest migration file may be overwritten and re-applied, assuming idempotency
50
56
  - **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
57
  - **Parallel releases via dependency tree**: DDL dependencies are analyzed to build a DAG, enabling parallel releases for independent changes
52
58
  - **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)) 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
59
+ - **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. MySQL uses `mysql` + `mysqldump` CLIs + [mysql2](https://github.com/sidorares/node-mysql2) (optional peer dep); SQLite uses `sqlite3` CLI + [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (optional peer dep). No external linter required — lint rules are built in via AST analysis. MySQL/SQLite linting uses [node-sql-parser](https://github.com/taozhi8833998/node-sql-parser) as a parallel, feature-limited engine
54
60
 
55
61
  ## Dialect support
56
62
 
57
63
  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
64
 
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.
65
+ Setting `dialect` to `mysql` or `sqlite` switches the entire tool chain to the corresponding database engine. Lint coverage is limited to **17 generic rules** (vs 38 for PostgreSQL); PostgreSQL-specific semantics (e.g. CONCURRENTLY, advisory lock timeout rules) 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
66
 
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
+ | Concern | `postgresql` (default) | `mysql` | `sqlite` |
68
+ |---------|------------------------|---------|----------|
69
+ | Lint AST | libpg-query (38 rules) | node-sql-parser (17 rules) | node-sql-parser (17 rules) |
70
+ | `deps` / DAG extraction | ✅ | | ✅ |
71
+ | `apply` (SQL execution) | `psql` CLI | `mysql` CLI | `sqlite3 -bail` CLI |
72
+ | `dump` / `diff` (schema dump) | `pg_dump --schema-only` | `mysqldump --no-data` | `sqlite3 .schema` |
73
+ | `verify` (shadow DB) | CREATE/DROP DATABASE | CREATE/DROP DATABASE | File copy/delete |
74
+ | State management (`schema_migrations`) | `pg` npm | `mysql2` (optional peer dep) | `better-sqlite3` (optional peer dep) |
75
+ | Advisory locking | `pg_advisory_lock` | `GET_LOCK` / `RELEASE_LOCK` | File-level (built-in) |
76
+ | Environment variables | `PGHOST`, `PGPORT`, `PGDATABASE`, `PGUSER`, `PGPASSWORD` | `MYSQL_HOST`, `MYSQL_TCP_PORT`, `MYSQL_DATABASE`, `MYSQL_USER`, `MYSQL_PWD` | `SQLITE_DATABASE` |
77
+ | File-based commands (`check`, `new`, `squash`, `editable`, …) | ✅ | ✅ | ✅ |
67
78
 
68
79
  Omitting `dialect` is equivalent to `"postgresql"` — existing projects require no config change.
69
80
 
@@ -76,7 +87,7 @@ migraguard separates file integrity and application state into two layers.
76
87
  | Layer | Location | Role |
77
88
  |-------|----------|------|
78
89
  | **metadata.json** (repository) | `db/.migraguard/metadata.json` | File list and checksums. Used for CI integrity checks. Environment-independent |
79
- | **schema_migrations** (per DB) | Each environment's PostgreSQL | Applied files and checksums per environment. Used by `apply` to determine pending migrations |
90
+ | **schema_migrations** (per DB) | Each environment's database | Applied files and checksums per environment. Used by `apply` to determine pending migrations |
80
91
 
81
92
  metadata.json represents "which files should exist"; schema_migrations represents "what has been applied." This separation enables correct staged rollout from a single repository to multiple environments (staging, production).
82
93
 
@@ -86,7 +97,7 @@ migraguard treats **migration SQL files** as the **Single Source of Truth (SSoT)
86
97
  They capture not only the end state, but also the *intent, ordering, and operational safety tactics* required for production changes.
87
98
 
88
99
  `schema.sql` is a **derived artifact**:
89
- - Generated from a real database via `dump` (pg_dump), and updated locally by `apply --with-drift-check`
100
+ - Generated from a real database via `dump` (`pg_dump`, `mysqldump`, or `sqlite3 .schema`), and updated locally by `apply --with-drift-check`
90
101
  - Used as an **expected-state snapshot** for drift detection (`diff`) and human review
91
102
  - Not intended to be hand-edited or treated as the authoritative desired state
92
103
 
@@ -103,7 +114,10 @@ Checksums are computed on **normalized SQL** (SHA-256): comments are stripped (`
103
114
 
104
115
  ### schema_migrations Table
105
116
 
117
+ The DDL varies by dialect. Below is the PostgreSQL version; MySQL uses `BIGINT AUTO_INCREMENT` / `TIMESTAMP(6)` / `ENGINE=InnoDB`, SQLite uses `INTEGER PRIMARY KEY AUTOINCREMENT` / `TEXT` columns / `datetime('now')`.
118
+
106
119
  ```sql
120
+ -- PostgreSQL
107
121
  CREATE TABLE IF NOT EXISTS schema_migrations (
108
122
  id BIGSERIAL PRIMARY KEY,
109
123
  file_name VARCHAR(256) NOT NULL,
@@ -200,7 +214,7 @@ See [docs/expand-contract.md](docs/expand-contract.md) for the complete guide: f
200
214
  | `new <name>` | Generate a new migration SQL file |
201
215
  | `new --expand-contract <name>` | Create an expand/contract migration group |
202
216
  | `squash` | Merge pending files into one for release |
203
- | `apply` | Execute pending migrations via `psql` |
217
+ | `apply` | Execute pending migrations via native CLI (`psql` / `mysql` / `sqlite3`) |
204
218
  | `apply --with-drift-check` | Local: drift check → apply → dump update |
205
219
  | `apply --from-baseline` | Apply `schema.sql` baseline, then remaining migrations |
206
220
  | `resolve <file>` | Mark a failed migration as skipped (explicit judgment) |
@@ -215,11 +229,11 @@ See [docs/expand-contract.md](docs/expand-contract.md) for the complete guide: f
215
229
  | `deps --html <path>` | Generate HTML dependency visualization |
216
230
  | `group-status [group]` | Show Migration Group phase states |
217
231
  | `advance <group> <phase> <status>` | Record phase state transition (executor) |
218
- | `apply-phase <group> <phase>` | Apply a specific phase via `psql` |
232
+ | `apply-phase <group> <phase>` | Apply a specific phase via native CLI |
219
233
  | `gate` | Evaluate deployment gate conditions |
220
234
  | `baseline` | Squash applied migrations into `schema.sql` |
221
235
 
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).
236
+ All commands honor the `dialect` setting. See [Dialect support](#dialect-support) for per-dialect details.
223
237
 
224
238
  See [docs/commands.md](docs/commands.md) for detailed usage, options, and examples.
225
239
 
@@ -318,11 +332,36 @@ jobs:
318
332
  }
319
333
  ```
320
334
 
335
+ **MySQL example** — connection defaults to `localhost:3306`:
336
+
337
+ ```json
338
+ {
339
+ "dialect": "mysql",
340
+ "connection": {
341
+ "host": "localhost",
342
+ "port": 3306,
343
+ "database": "myapp_dev",
344
+ "user": "root"
345
+ }
346
+ }
347
+ ```
348
+
349
+ **SQLite example** — only `database` (file path) is needed:
350
+
351
+ ```json
352
+ {
353
+ "dialect": "sqlite",
354
+ "connection": {
355
+ "database": "./db/myapp_dev.sqlite3"
356
+ }
357
+ }
358
+ ```
359
+
321
360
  ### Model Configuration
322
361
 
323
362
  | Key | Default | Description |
324
363
  |-----|---------|-------------|
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"` |
364
+ | `dialect` | `"postgresql"` | SQL dialect: `"postgresql"` (libpg-query, 38 rules, `psql`/`pg_dump`), `"mysql"` (node-sql-parser, 17 rules, `mysql`/`mysqldump`), or `"sqlite"` (node-sql-parser, 17 rules, `sqlite3`). Omitted means `"postgresql"` |
326
365
  | `model` | _(unset = linear)_ | Set to `"dag"` to enable DAG mode. When set in config, takes precedence over `metadata.json` |
327
366
 
328
367
  ### Naming Configuration
@@ -357,7 +396,7 @@ jobs:
357
396
  // → auth_20260301_120000__add_users_table.sql
358
397
  ```
359
398
 
360
- `connection` can be overridden via environment variables (`PGHOST`, `PGPORT`, `PGDATABASE`, `PGUSER`, `PGPASSWORD`).
399
+ `connection` can be overridden via dialect-specific environment variables: PostgreSQL (`PGHOST`, `PGPORT`, `PGDATABASE`, `PGUSER`, `PGPASSWORD`), MySQL (`MYSQL_HOST`, `MYSQL_TCP_PORT`, `MYSQL_DATABASE`, `MYSQL_USER`, `MYSQL_PWD`), SQLite (`SQLITE_DATABASE`).
361
400
 
362
401
  `migrationsDirs` accepts multiple paths for monorepo setups. `new` / `squash` write to the first directory. For backward compatibility, `migrationsDir` (singular) is also accepted.
363
402
 
@@ -510,9 +549,9 @@ migraguard embeds operational policies into the tool and prevents incidents via
510
549
  | **Parallel releases** | ✅ DAG | ❌ | ❌ | ⚠️ | ❌ |
511
550
  | **Offline CI gate** | ✅ check | ❌ | ✅ atlas.sum | ❌ | ❌ |
512
551
  | **Failure handling** | DB-recorded, explicit resolve | repair overwrites | manual fix | revert scripts | manual fix |
513
- | **Execution** | psql (plain SQL) | Java / JDBC | Go / DB driver | psql / sqitch | pg (Node.js) |
552
+ | **Execution** | psql / mysql / sqlite3 (plain SQL) | Java / JDBC | Go / DB driver | psql / sqitch | pg (Node.js) |
514
553
 
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.
554
+ **vs Flyway / Liquibase**: migraguard adds offline CI tamper detection, regression detection, idempotency proof, and apply mutual exclusion. MySQL and SQLite are supported as secondary dialects with full DB runtime commands and 17 generic lint rules. No GUI or rich generic execution engine.
516
555
 
517
556
  **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.
518
557
 
@@ -528,7 +567,7 @@ Nothing. Checksums are computed on [normalized SQL](#checksum-normalization) —
528
567
 
529
568
  ### What happens if two CI pipelines run `apply` concurrently?
530
569
 
531
- One acquires the advisory lock and proceeds; the other blocks until the first completes. No race condition occurs.
570
+ One acquires the advisory lock and proceeds; the other blocks until the first completes. No race condition occurs. PostgreSQL uses `pg_advisory_lock`, MySQL uses `GET_LOCK`, and SQLite relies on file-level locking.
532
571
 
533
572
  ### A migration failed in production. How do I fix it?
534
573
 
@@ -546,18 +585,22 @@ See [When to Use DAG](#when-to-use-dag). In short: when multiple teams need para
546
585
 
547
586
  ### Does `verify` run against my production DB?
548
587
 
549
- No. `verify` creates a temporary shadow DB, applies migrations twice, then drops it. Production is never modified.
588
+ No. `verify` creates a temporary shadow DB, applies migrations twice, then drops it. Production is never modified. For PostgreSQL and MySQL, a temporary database is created/dropped; for SQLite, a temporary file copy is used.
550
589
 
551
590
  ## Technology Stack
552
591
 
553
592
  | Component | Technology |
554
593
  |-----------|-----------|
555
594
  | Language | TypeScript (Node.js) |
556
- | DB execution | `psql` CLI |
557
- | Schema dump | `pg_dump --schema-only` |
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 |
595
+ | DB execution (PostgreSQL) | `psql` CLI |
596
+ | DB execution (MySQL) | `mysql` CLI |
597
+ | DB execution (SQLite) | `sqlite3` CLI |
598
+ | Schema dump | `pg_dump` / `mysqldump` / `sqlite3 .schema` |
599
+ | DB state management (PostgreSQL) | [pg](https://github.com/brianc/node-postgres) |
600
+ | DB state management (MySQL) | [mysql2](https://github.com/sidorares/node-mysql2) (optional peer dep) |
601
+ | DB state management (SQLite) | [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (optional peer dep) |
602
+ | SQL lint / parser (PostgreSQL) | [libpg-query](https://github.com/pganalyze/libpg-query) — 38 built-in rules |
603
+ | SQL lint / parser (MySQL, SQLite) | [node-sql-parser](https://github.com/taozhi8833998/node-sql-parser) — 17 generic rules |
561
604
  | Package manager | npm |
562
605
 
563
606
  ## Detailed Documentation