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 +72 -29
- package/dist/cli.js +564 -134
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +22 -2
- package/dist/index.js +419 -56
- package/dist/index.js.map +1 -1
- package/docs/commands.md +4 -4
- package/docs/expand-contract.md +2 -2
- package/docs/state-model.md +2 -2
- package/package.json +11 -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-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.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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`
|
|
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`
|
|
62
|
-
|
|
63
|
-
| Lint AST | libpg-query (38 rules) | node-sql-parser (17 rules) |
|
|
64
|
-
| `deps` / DAG extraction | ✅
|
|
65
|
-
| `apply
|
|
66
|
-
|
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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.
|
|
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
|
-
|
|
|
558
|
-
|
|
|
559
|
-
|
|
|
560
|
-
|
|
|
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
|