migraguard 0.0.1 → 0.2.2
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/COMMANDS.md +9 -0
- package/README.md +113 -38
- package/bin/migraguard.js +0 -0
- package/dist/cli.js +2103 -45
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +11 -4
package/COMMANDS.md
CHANGED
|
@@ -70,6 +70,15 @@ Run Squawk lint on migration files to detect idempotency and safety rule violati
|
|
|
70
70
|
migraguard lint
|
|
71
71
|
```
|
|
72
72
|
|
|
73
|
+
### `migraguard verify`
|
|
74
|
+
|
|
75
|
+
Verify migration idempotency using a shadow DB. Dumps the current DB schema, restores it to a temporary shadow database, then applies each pending migration twice — checking for errors and schema drift.
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
migraguard verify # incremental: restore current DB, verify pending only
|
|
79
|
+
migraguard verify --all # full: empty shadow, verify all migrations from scratch
|
|
80
|
+
```
|
|
81
|
+
|
|
73
82
|
## Schema management
|
|
74
83
|
|
|
75
84
|
### `migraguard dump`
|
package/README.md
CHANGED
|
@@ -4,12 +4,42 @@ PostgreSQL 向けの SQL マイグレーション管理ツールチェイン。
|
|
|
4
4
|
|
|
5
5
|
DDL を直 SQL で記述し、`psql` で実行するシンプルな構成を前提に、冪等性の担保・改ざん検知・スキーマ drift チェックを提供する。
|
|
6
6
|
|
|
7
|
+
## Guarantees
|
|
8
|
+
|
|
9
|
+
migraguard は以下を保証する。
|
|
10
|
+
|
|
11
|
+
- **editable ノード以外の変更を CI で検出して落とす** — 線形モデルでは末尾ファイル、DAG モデルでは葉ノードのみが編集可能。それ以外のファイルが変更されていれば `check` がエラーを返す
|
|
12
|
+
- **意図しない巻き戻し(regression)を検出してエラー** — hotfix 済みのファイルが古いチェックサムに戻っている場合、`apply` が即座にエラーを返す
|
|
13
|
+
- **`apply` は advisory lock で排他制御** — 同一環境への同時適用を防止し、競合状態を排除する
|
|
14
|
+
- **`apply --verify` は事前 drift 検知 → 適用 → dump 更新を一貫実行** — スキーマの期待状態と実 DB の乖離を検出してから適用し、適用後に dump を自動更新する
|
|
15
|
+
- **失敗状態は DB に記録され、未解決のまま先に進めない** — `failed` 状態のファイルが残っている限り後続の適用はブロックされる。`resolve` による明示的な人間の判断を要求する
|
|
16
|
+
|
|
17
|
+
## Quick Start
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# インストール
|
|
21
|
+
npm install --save-dev migraguard
|
|
22
|
+
|
|
23
|
+
# 新規マイグレーション作成 → SQL 編集 → ローカル DB に適用
|
|
24
|
+
npx migraguard new create_users_table
|
|
25
|
+
vim db/migrations/20260301_120000__create_users_table.sql
|
|
26
|
+
npx migraguard apply
|
|
27
|
+
|
|
28
|
+
# リリース前: squash → lint + check → dump 更新
|
|
29
|
+
npx migraguard squash
|
|
30
|
+
npx migraguard lint && npx migraguard check
|
|
31
|
+
npx migraguard dump
|
|
32
|
+
|
|
33
|
+
# PR では CI が lint + check(+ 任意で verify)を実行
|
|
34
|
+
# リリース前に squash で 1 ファイルにまとめてからマージ
|
|
35
|
+
```
|
|
36
|
+
|
|
7
37
|
## 設計思想
|
|
8
38
|
|
|
9
39
|
- **直 SQL**: マイグレーションは `psql -f` で実行可能な SQL ファイルとして管理する。ORM やマイグレーションフレームワーク固有の DSL を排除し、トランザクション境界を SQL に明示する
|
|
10
40
|
- **forward-only**: 適用済みマイグレーションの変更を原則禁止し、常に前方向へ積み上げる。最新のマイグレーションファイルのみ、冪等性が担保されている前提で上書き更新・再適用を許容する
|
|
11
|
-
- **リリース単位は 1 ファイル**:
|
|
12
|
-
-
|
|
41
|
+
- **リリース単位は 1 ファイル**: 依存関係のあるマイグレーションファイルはリリース前に `squash` で 1 ファイルにまとめる。1 ファイル = 1 リリース単位とすることで、エラー時の修正・再適用を単純化する。DAG モデルでは独立した DDL は個別にリリース可能で、`squash` は依存チェーンごとに自動グループ化して実行する
|
|
42
|
+
- **依存ツリーによる並行リリース**: DDL の依存関係を解析して DAG を構築し、独立した変更の並行作業・並行リリースを可能にする。線形モデルの制約を緩和し、大規模システムでの運用を改善する
|
|
13
43
|
- **検証を左に寄せる**: Squawk による lint、チェックサムによる改ざん検知、スキーマ dump の diff を CI(PR 段階)で実行し、本番到達前にリスクを排除する
|
|
14
44
|
- **最小構成**: `psql` + SQL ファイル + メタデータ JSON + DB 状態テーブルによる管理。ツール固有のロックイン・ブラックボックスを避ける
|
|
15
45
|
|
|
@@ -30,7 +60,7 @@ metadata.json は「どのファイルが存在すべきか」を、schema_migra
|
|
|
30
60
|
|
|
31
61
|
| 機能 | 説明 |
|
|
32
62
|
|------|------|
|
|
33
|
-
| `migraguard new <name>` |
|
|
63
|
+
| `migraguard new <name>` | ローカルタイムゾーンのタイムスタンプ(またはシリアル番号)付きの新規マイグレーション SQL ファイルを生成 |
|
|
34
64
|
| `migraguard squash` | 未適用の複数マイグレーションファイルを 1 ファイルにマージ。リリース前に実行する |
|
|
35
65
|
| `migraguard apply` | 未適用マイグレーションを順番に `psql` で実行。対象 DB の `schema_migrations` テーブルで適用済みを判定 |
|
|
36
66
|
| `migraguard resolve <file>` | 失敗したマイグレーションを明示的にスキップ済みとしてマーク。後続の forward migration で修正済みであることを人間が判断した上で実行する |
|
|
@@ -43,6 +73,8 @@ metadata.json は「どのファイルが存在すべきか」を、schema_migra
|
|
|
43
73
|
|------|------|
|
|
44
74
|
| `migraguard check` | metadata.json とファイル本体のチェックサム比較。最新ファイル以外の変更・追加を検出しエラーとする。DB 接続不要 |
|
|
45
75
|
| `migraguard lint` | Squawk を使用した SQL lint。冪等性・安全性に関するルール違反を検出 |
|
|
76
|
+
| `migraguard verify` | shadow DB を使用して各マイグレーションの冪等性を動的に検証する。既存 DB をダンプして復元し、未適用分を2回適用してエラーなし・スキーマ不変を確認する |
|
|
77
|
+
| `migraguard verify --all` | 空の shadow DB で全マイグレーションを最初から冪等性検証する |
|
|
46
78
|
|
|
47
79
|
### スキーマ管理
|
|
48
80
|
|
|
@@ -51,12 +83,14 @@ metadata.json は「どのファイルが存在すべきか」を、schema_migra
|
|
|
51
83
|
| `migraguard dump` | `pg_dump --schema-only` を実行し、正規化したスキーマを出力。diff が取れる形式で保存 |
|
|
52
84
|
| `migraguard diff` | 現在の DB スキーマと保存済みスキーマ dump の差分を表示 |
|
|
53
85
|
|
|
54
|
-
###
|
|
86
|
+
### 依存関係解析・DAG モデル
|
|
55
87
|
|
|
56
88
|
| 機能 | 説明 |
|
|
57
89
|
|------|------|
|
|
58
|
-
| `migraguard deps` |
|
|
59
|
-
| `migraguard deps --
|
|
90
|
+
| `migraguard deps` | マイグレーション間の依存関係をツリー形式で表示。◆=editable(葉ノード)、◇=locked(非葉ノード)のマーク付き |
|
|
91
|
+
| `migraguard deps --html <path>` | GitGraph.js による依存グラフの HTML を生成 |
|
|
92
|
+
|
|
93
|
+

|
|
60
94
|
|
|
61
95
|
## ディレクトリ構成
|
|
62
96
|
|
|
@@ -64,32 +98,48 @@ metadata.json は「どのファイルが存在すべきか」を、schema_migra
|
|
|
64
98
|
project-root/
|
|
65
99
|
├── migraguard.config.json # 設定ファイル
|
|
66
100
|
├── db/
|
|
67
|
-
│ ├── migrations/ # マイグレーション SQL
|
|
101
|
+
│ ├── migrations/ # マイグレーション SQL ファイル(デフォルト)
|
|
68
102
|
│ │ ├── 20260301_120000__create_users_table.sql
|
|
69
103
|
│ │ ├── 20260302_093000__add_email_index.sql
|
|
70
104
|
│ │ └── ...
|
|
71
105
|
│ ├── schema.sql # 正規化されたスキーマ dump(生成物)
|
|
72
106
|
│ └── .migraguard/
|
|
73
107
|
│ └── metadata.json # ファイル一覧 + チェックサム(適用状態は含まない)
|
|
108
|
+
├── services/ # モノレポ構成の場合
|
|
109
|
+
│ ├── auth/migrations/ # migrationsDirs で追加の検索パスを指定可能
|
|
110
|
+
│ │ └── ...
|
|
111
|
+
│ └── billing/migrations/
|
|
112
|
+
│ └── ...
|
|
74
113
|
└── ...
|
|
75
114
|
```
|
|
76
115
|
|
|
116
|
+
`migrationsDirs` で複数の検索パスを指定すると、全ディレクトリからマイグレーションファイルをスキャンし、タイムスタンプ(またはシリアル番号)順にソートして一元管理する。`new` / `squash` は配列の先頭ディレクトリに書き込む。
|
|
117
|
+
|
|
77
118
|
### schema_migrations テーブル(各環境の DB に作成)
|
|
78
119
|
|
|
79
120
|
```sql
|
|
80
121
|
CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
122
|
+
id BIGSERIAL PRIMARY KEY,
|
|
81
123
|
file_name VARCHAR(256) NOT NULL,
|
|
82
124
|
checksum VARCHAR(64) NOT NULL,
|
|
83
125
|
status VARCHAR(16) NOT NULL DEFAULT 'applied', -- applied / failed / skipped
|
|
84
126
|
applied_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
85
|
-
resolved_at TIMESTAMPTZ
|
|
86
|
-
PRIMARY KEY (file_name, checksum)
|
|
127
|
+
resolved_at TIMESTAMPTZ -- skipped 時の解決日時
|
|
87
128
|
);
|
|
88
129
|
```
|
|
89
130
|
|
|
90
131
|
`migraguard apply` の初回実行時に自動作成される。
|
|
91
132
|
|
|
92
|
-
|
|
133
|
+
**履歴方針: 完全 INSERT-only**
|
|
134
|
+
|
|
135
|
+
PK は `BIGSERIAL`(自動採番)であり、同一ファイル・同一チェックサムであっても毎回新しいレコードが INSERT される。UPDATE は行わない。
|
|
136
|
+
|
|
137
|
+
- 同一ファイルの再適用(hotfix でチェックサム変更後の再適用など)→ 新しいチェックサムで別レコードを INSERT
|
|
138
|
+
- 同一チェックサムでの再実行(冪等性による再適用)→ 同じチェックサムで別レコードを INSERT(`applied_at` が異なる)
|
|
139
|
+
- `failed` → 修正後の再実行 → `failed` レコードは残したまま、新しい `applied` レコードを INSERT
|
|
140
|
+
- `resolve` → `failed` レコードは残したまま、同じチェックサムで `skipped` レコードを INSERT
|
|
141
|
+
|
|
142
|
+
あるファイルの「最新状態」は、そのファイル名の最新レコード(`applied_at` が最大)の `status` で判定する。過去のレコードは監査ログとして残り続ける。
|
|
93
143
|
|
|
94
144
|
status の意味:
|
|
95
145
|
|
|
@@ -99,9 +149,11 @@ status の意味:
|
|
|
99
149
|
| `failed` | 適用を試みたがエラーで失敗。未解決 |
|
|
100
150
|
| `skipped` | `migraguard resolve` により明示的にスキップ。後続の forward migration で修正済みという人間の判断 |
|
|
101
151
|
|
|
102
|
-
###
|
|
152
|
+
### Hotfix 済みマイグレーションの意図しない巻き戻し検知(regression detection)
|
|
153
|
+
|
|
154
|
+
**防いでいる事故**: hotfix で修正済みのマイグレーションファイルが、git revert・ブランチ切り替え・マージミスなどにより古いバージョンに戻ってしまい、修正前の DDL が本番に再適用されること。
|
|
103
155
|
|
|
104
|
-
apply 時、ファイルの現在のチェックサムが **過去のレコード(最新以外)のチェックサムと一致**
|
|
156
|
+
apply 時、ファイルの現在のチェックサムが **過去のレコード(最新以外)のチェックサムと一致** した場合、意図しない巻き戻しとしてエラーにする。
|
|
105
157
|
|
|
106
158
|
```
|
|
107
159
|
例:
|
|
@@ -111,16 +163,14 @@ apply 時、ファイルの現在のチェックサムが **過去のレコー
|
|
|
111
163
|
|
|
112
164
|
ファイルのチェックサムが checksum_v1 に戻っている
|
|
113
165
|
→ 最新レコードは checksum_v2 → 不一致 → さらに過去の checksum_v1 と一致
|
|
114
|
-
→
|
|
166
|
+
→ 巻き戻しとしてエラー
|
|
115
167
|
```
|
|
116
168
|
|
|
117
|
-
これにより、ファイルを誤って古いバージョンに戻してしまった場合を検知できる。
|
|
118
|
-
|
|
119
169
|
## 設定ファイル(migraguard.config.json)
|
|
120
170
|
|
|
121
171
|
```json
|
|
122
172
|
{
|
|
123
|
-
"
|
|
173
|
+
"migrationsDirs": ["db/migrations"],
|
|
124
174
|
"schemaFile": "db/schema.sql",
|
|
125
175
|
"metadataFile": "db/.migraguard/metadata.json",
|
|
126
176
|
"naming": {
|
|
@@ -151,7 +201,7 @@ apply 時、ファイルの現在のチェックサムが **過去のレコー
|
|
|
151
201
|
| キー | デフォルト | 説明 |
|
|
152
202
|
|------|-----------|------|
|
|
153
203
|
| `pattern` | `{timestamp}__{description}.sql` | ファイル名のテンプレート。`{timestamp}`, `{prefix}`, `{description}` を使用可能 |
|
|
154
|
-
| `timestamp` | `YYYYMMDD_HHMMSS` |
|
|
204
|
+
| `timestamp` | `YYYYMMDD_HHMMSS` | タイムスタンプのフォーマット(ローカルタイムゾーン)。`NNNN` 等の `N` のみの形式でシリアル番号モード(既存ファイルの最大番号 + 1 で自動採番) |
|
|
155
205
|
| `prefix` | `""` | 全ファイルに付与する固定プレフィックス。カテゴリやサービス名の識別に使用 |
|
|
156
206
|
| `sortKey` | `timestamp` | ファイルのソート順を決定するキー。`timestamp`(タイムスタンプ部分で昇順)が標準 |
|
|
157
207
|
|
|
@@ -189,6 +239,19 @@ apply 時、ファイルの現在のチェックサムが **過去のレコー
|
|
|
189
239
|
|
|
190
240
|
`migraguard new` はこの設定に従ってファイル名を生成する。`migraguard check` / `apply` はパターンに基づいてタイムスタンプ部分を抽出し、ソート順を決定する。
|
|
191
241
|
|
|
242
|
+
`migrationsDirs` には複数の検索パスを指定可能。モノレポでマイクロサービスごとにマイグレーションディレクトリを分けている場合に使用する。後方互換のため `migrationsDir`(単数)も受け付ける。`new` / `squash` は配列の先頭ディレクトリに書き込む。
|
|
243
|
+
|
|
244
|
+
```json
|
|
245
|
+
// モノレポでの複数ディレクトリ構成例
|
|
246
|
+
{
|
|
247
|
+
"migrationsDirs": [
|
|
248
|
+
"db/migrations",
|
|
249
|
+
"services/auth/migrations",
|
|
250
|
+
"services/billing/migrations"
|
|
251
|
+
]
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
|
|
192
255
|
`connection` は環境変数(`PGHOST`, `PGPORT`, `PGDATABASE`, `PGUSER`, `PGPASSWORD`)でオーバーライド可能。
|
|
193
256
|
|
|
194
257
|
## マイグレーションファイルの規約
|
|
@@ -207,7 +270,7 @@ YYYYMMDD_HHMMSS__<description>.sql
|
|
|
207
270
|
<prefix>_YYYYMMDD_HHMMSS__<description>.sql
|
|
208
271
|
```
|
|
209
272
|
|
|
210
|
-
-
|
|
273
|
+
- タイムスタンプはローカルタイムゾーン(`naming.timestamp` で形式を変更可能。`NNNN` でシリアル番号モード)
|
|
211
274
|
- description は英数字とアンダースコアのみ
|
|
212
275
|
- 操作種別を先頭に付与: `create_`, `add_`, `alter_`, `drop_`, `backfill_`, `create_index_`
|
|
213
276
|
- prefix はカテゴリ・マイクロサービス名等の識別子(`naming.prefix` で設定)
|
|
@@ -233,6 +296,8 @@ UPDATE users SET status = 'active' WHERE status IS NULL;
|
|
|
233
296
|
npx migraguard squash
|
|
234
297
|
```
|
|
235
298
|
|
|
299
|
+
### 線形モデルでの動作
|
|
300
|
+
|
|
236
301
|
1. metadata.json を読み込む
|
|
237
302
|
2. `migrationsDir` のファイルのうち、metadata.json に記録されていない新規ファイルを特定
|
|
238
303
|
3. 新規ファイルが 2 つ以上ある場合、タイムスタンプ順に連結して 1 ファイルにまとめる
|
|
@@ -248,7 +313,26 @@ squash 後:
|
|
|
248
313
|
20260301_093000__add_user_email_and_email_index.sql (1ファイルに統合)
|
|
249
314
|
```
|
|
250
315
|
|
|
251
|
-
|
|
316
|
+
`migraguard check` は新規ファイル(metadata.json に未記録)が 2 つ以上ある場合にエラーとする。squash を実行してから push する運用を強制する。
|
|
317
|
+
|
|
318
|
+
### DAG モデルでの動作
|
|
319
|
+
|
|
320
|
+
DAG モードでは、新規ファイルを依存関係に基づいて連結成分(グループ)に自動分割し、グループごとに squash する。独立した DDL(互いに依存関係がない)は個別のファイルとして残す。
|
|
321
|
+
|
|
322
|
+
```
|
|
323
|
+
squash 前:
|
|
324
|
+
20260308_100000__create_follows.sql (新規 — users に依存)
|
|
325
|
+
20260308_110000__add_follow_index.sql (新規 — follows に依存)
|
|
326
|
+
20260309_100000__create_notifications.sql (新規 — users に依存、follows とは独立)
|
|
327
|
+
|
|
328
|
+
squash 後:
|
|
329
|
+
20260308_110000__create_follows_and_add_follow_index.sql (依存チェーンを統合)
|
|
330
|
+
20260309_100000__create_notifications.sql (独立 — そのまま)
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
各グループ内ではタイムスタンプの古い順に連結し、依存先が先・依存元が後の順序を保証する。
|
|
334
|
+
|
|
335
|
+
### なぜ依存チェーンを 1 ファイルにまとめるか
|
|
252
336
|
|
|
253
337
|
複数ファイルが未適用の状態でリリースすると、以下の問題が起きる。
|
|
254
338
|
|
|
@@ -260,9 +344,9 @@ squash 後:
|
|
|
260
344
|
|
|
261
345
|
**1 ファイルなら単純**:
|
|
262
346
|
- squash 後の 1 ファイルがエラー → そのファイルを修正 → 再適用
|
|
263
|
-
-
|
|
347
|
+
- 葉ノードルールに適合し、冪等性により安全に再実行できる
|
|
264
348
|
|
|
265
|
-
|
|
349
|
+
独立した DDL はこの問題が発生しないため、無理にまとめる必要はない。
|
|
266
350
|
|
|
267
351
|
## apply のフロー
|
|
268
352
|
|
|
@@ -670,24 +754,15 @@ SET lock_timeout = '5s';
|
|
|
670
754
|
| エラーの影響範囲 | 全後続ファイルがブロック | 依存するファイルのみブロック |
|
|
671
755
|
| 複数チームの作業 | 直列化(1 チームずつ) | 独立したテーブルなら並行作業可能 |
|
|
672
756
|
|
|
673
|
-
###
|
|
674
|
-
|
|
675
|
-
依存ツリーモデルは線形モデルの上位互換として段階的に導入する。
|
|
676
|
-
|
|
677
|
-
| フェーズ | 内容 |
|
|
678
|
-
|---------|------|
|
|
679
|
-
| Phase 1 | 線形モデルで基本機能(new / apply / check / squash / lint / dump)を実装 |
|
|
680
|
-
| Phase 2 | `libpg_query` による DDL 依存抽出と `migraguard deps` コマンドを実装。情報表示のみで動作は変更しない |
|
|
681
|
-
| Phase 3 | check と apply を依存ツリー対応に拡張。葉ノード判定、トポロジカルソート適用 |
|
|
682
|
-
|
|
683
|
-
### 依存解析の候補ライブラリ
|
|
757
|
+
### 実装状況
|
|
684
758
|
|
|
685
|
-
|
|
686
|
-
|-----------|------|
|
|
687
|
-
| [libpg_query](https://www.npmjs.com/package/libpg_query) | PostgreSQL 実パーサの Node.js バインディング。SQL → AST のパースを提供 |
|
|
688
|
-
| [@pg-nano/pg-parser](https://www.npmjs.com/package/@pg-nano/pg-parser) | TypeScript ファーストの PostgreSQL パーサ。型定義と AST ユーティリティ(`walk()`, `select()`)を提供 |
|
|
759
|
+
依存ツリーモデルは線形モデルの上位互換として段階的に導入された。
|
|
689
760
|
|
|
690
|
-
|
|
761
|
+
| フェーズ | 内容 | 状態 |
|
|
762
|
+
|---------|------|------|
|
|
763
|
+
| Phase 1 | 線形モデルで基本機能(new / apply / check / squash / lint / dump / verify)を実装 | ✅ 完了 |
|
|
764
|
+
| Phase 2 | `@pg-nano/pg-parser` による DDL 依存抽出と `migraguard deps` コマンドを実装 | ✅ 完了 |
|
|
765
|
+
| Phase 3 | check / apply / editable / squash を依存ツリー対応に拡張。葉ノード判定、トポロジカルソート適用、部分失敗時の独立ブランチ続行 | ✅ 完了 |
|
|
691
766
|
|
|
692
767
|
## 既存ツールとの比較
|
|
693
768
|
|
|
@@ -815,5 +890,5 @@ metadata.json の移行例:
|
|
|
815
890
|
| DB 接続 | `psql` CLI(DDL ファイルを直接渡す) |
|
|
816
891
|
| スキーマ dump | `pg_dump --schema-only` |
|
|
817
892
|
| SQL lint | [Squawk](https://squawkhq.com/) |
|
|
818
|
-
| SQL パーサ | [
|
|
893
|
+
| SQL パーサ | [@pg-nano/pg-parser](https://www.npmjs.com/package/@pg-nano/pg-parser)(DDL 依存解析用。PostgreSQL 実パーサの TypeScript バインディング) |
|
|
819
894
|
| パッケージ管理 | npm |
|
package/bin/migraguard.js
CHANGED
|
File without changes
|