migraguard 0.0.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/COMMANDS.md ADDED
@@ -0,0 +1,100 @@
1
+ # migraguard commands
2
+
3
+ ## Migration management
4
+
5
+ ### `migraguard new <name>`
6
+
7
+ Create a new migration SQL file with a UTC timestamp prefix.
8
+
9
+ ```bash
10
+ migraguard new add_users_email_index
11
+ # → db/migrations/20260301_120000__add_users_email_index.sql
12
+ ```
13
+
14
+ ### `migraguard squash`
15
+
16
+ Squash multiple new (unrecorded in metadata.json) migration files into a single file. Run before merging to a release branch.
17
+
18
+ ```bash
19
+ migraguard squash
20
+ ```
21
+
22
+ ### `migraguard apply`
23
+
24
+ Apply pending migrations to the target DB via `psql`. Checks `schema_migrations` table for applied/failed/skipped status.
25
+
26
+ ```bash
27
+ migraguard apply
28
+ migraguard apply --verify # verify schema dump before and after
29
+ ```
30
+
31
+ ### `migraguard resolve <file>`
32
+
33
+ Mark a failed migration as skipped. Use when a subsequent forward migration covers the fix. Requires human judgment.
34
+
35
+ ```bash
36
+ migraguard resolve 20260301_093000__add_user_email.sql
37
+ ```
38
+
39
+ ### `migraguard status`
40
+
41
+ Show the status of all migration files: applied, pending, failed, or skipped. Requires DB connection.
42
+
43
+ ```bash
44
+ migraguard status
45
+ ```
46
+
47
+ ### `migraguard editable`
48
+
49
+ List migration files that are currently editable (modifiable and re-appliable). In the linear model this is the latest file; in the DAG model these are leaf nodes. With DB connection, also shows failed files eligible for retry.
50
+
51
+ ```bash
52
+ migraguard editable
53
+ ```
54
+
55
+ ## Integrity checks
56
+
57
+ ### `migraguard check`
58
+
59
+ Verify file integrity against metadata.json. No DB connection required. Detects: checksum mismatches on non-latest files, mid-sequence insertions, and multiple new files (enforces squash).
60
+
61
+ ```bash
62
+ migraguard check
63
+ ```
64
+
65
+ ### `migraguard lint`
66
+
67
+ Run Squawk lint on migration files to detect idempotency and safety rule violations.
68
+
69
+ ```bash
70
+ migraguard lint
71
+ ```
72
+
73
+ ## Schema management
74
+
75
+ ### `migraguard dump`
76
+
77
+ Dump the current DB schema via `pg_dump --schema-only`, normalize it, and save as `schema.sql`.
78
+
79
+ ```bash
80
+ migraguard dump
81
+ ```
82
+
83
+ ### `migraguard diff`
84
+
85
+ Show the diff between the current DB schema and the saved `schema.sql`.
86
+
87
+ ```bash
88
+ migraguard diff
89
+ ```
90
+
91
+ ## Dependency analysis (extension)
92
+
93
+ ### `migraguard deps`
94
+
95
+ Analyze and display the dependency graph between migration files.
96
+
97
+ ```bash
98
+ migraguard deps
99
+ migraguard deps --dot # output in DOT format for Graphviz
100
+ ```
package/README.md ADDED
@@ -0,0 +1,819 @@
1
+ # migraguard
2
+
3
+ PostgreSQL 向けの SQL マイグレーション管理ツールチェイン。
4
+
5
+ DDL を直 SQL で記述し、`psql` で実行するシンプルな構成を前提に、冪等性の担保・改ざん検知・スキーマ drift チェックを提供する。
6
+
7
+ ## 設計思想
8
+
9
+ - **直 SQL**: マイグレーションは `psql -f` で実行可能な SQL ファイルとして管理する。ORM やマイグレーションフレームワーク固有の DSL を排除し、トランザクション境界を SQL に明示する
10
+ - **forward-only**: 適用済みマイグレーションの変更を原則禁止し、常に前方向へ積み上げる。最新のマイグレーションファイルのみ、冪等性が担保されている前提で上書き更新・再適用を許容する
11
+ - **リリース単位は 1 ファイル**: 複数のマイグレーションファイルはリリース前に `squash` で 1 ファイルにまとめる。1 ファイル = 1 リリース単位とすることで、エラー時の修正・再適用を単純化する
12
+ - **依存ツリーによる並行リリース**(拡張): DDL の依存関係を解析して DAG を構築し、独立した変更の並行作業・並行リリースを可能にする。線形モデルの制約を緩和し、大規模システムでの運用を改善する
13
+ - **検証を左に寄せる**: Squawk による lint、チェックサムによる改ざん検知、スキーマ dump の diff を CI(PR 段階)で実行し、本番到達前にリスクを排除する
14
+ - **最小構成**: `psql` + SQL ファイル + メタデータ JSON + DB 状態テーブルによる管理。ツール固有のロックイン・ブラックボックスを避ける
15
+
16
+ ## 二層の状態管理
17
+
18
+ migraguard はファイル整合性と適用状態を分離して管理する。
19
+
20
+ | レイヤ | 保存場所 | 役割 |
21
+ |--------|----------|------|
22
+ | **metadata.json**(リポジトリ) | `db/.migraguard/metadata.json` | マイグレーションファイルの一覧とチェックサム。CI での整合性チェックに使用。環境に依存しない |
23
+ | **schema_migrations テーブル**(各 DB) | 各環境の PostgreSQL | その環境に適用済みのファイルとチェックサムを記録。`apply` 時に未適用分を判定する |
24
+
25
+ metadata.json は「どのファイルが存在すべきか」を、schema_migrations は「どの環境に何が適用済みか」を表す。この分離により、同一のリポジトリから複数環境(検証・商用)への段階的リリースが正しく動作する。
26
+
27
+ ## 機能一覧
28
+
29
+ ### マイグレーション管理
30
+
31
+ | 機能 | 説明 |
32
+ |------|------|
33
+ | `migraguard new <name>` | UTC タイムスタンプ付きの新規マイグレーション SQL ファイルを生成 |
34
+ | `migraguard squash` | 未適用の複数マイグレーションファイルを 1 ファイルにマージ。リリース前に実行する |
35
+ | `migraguard apply` | 未適用マイグレーションを順番に `psql` で実行。対象 DB の `schema_migrations` テーブルで適用済みを判定 |
36
+ | `migraguard resolve <file>` | 失敗したマイグレーションを明示的にスキップ済みとしてマーク。後続の forward migration で修正済みであることを人間が判断した上で実行する |
37
+ | `migraguard status` | 適用済み・未適用・失敗・スキップのマイグレーション一覧を表示 |
38
+ | `migraguard editable` | 現在編集可能なマイグレーションファイルを一覧表示。線形モデルでは末尾ファイル、DAG モデルでは葉ノードが対象。DB 接続時は `schema_migrations` も参照し、failed 状態のリトライ可能ファイルも表示する |
39
+
40
+ ### 整合性チェック
41
+
42
+ | 機能 | 説明 |
43
+ |------|------|
44
+ | `migraguard check` | metadata.json とファイル本体のチェックサム比較。最新ファイル以外の変更・追加を検出しエラーとする。DB 接続不要 |
45
+ | `migraguard lint` | Squawk を使用した SQL lint。冪等性・安全性に関するルール違反を検出 |
46
+
47
+ ### スキーマ管理
48
+
49
+ | 機能 | 説明 |
50
+ |------|------|
51
+ | `migraguard dump` | `pg_dump --schema-only` を実行し、正規化したスキーマを出力。diff が取れる形式で保存 |
52
+ | `migraguard diff` | 現在の DB スキーマと保存済みスキーマ dump の差分を表示 |
53
+
54
+ ### 依存関係解析(拡張)
55
+
56
+ | 機能 | 説明 |
57
+ |------|------|
58
+ | `migraguard deps` | マイグレーション間の依存関係グラフを解析・表示 |
59
+ | `migraguard deps --dot` | DOT 形式で出力(Graphviz で可視化可能) |
60
+
61
+ ## ディレクトリ構成
62
+
63
+ ```
64
+ project-root/
65
+ ├── migraguard.config.json # 設定ファイル
66
+ ├── db/
67
+ │ ├── migrations/ # マイグレーション SQL ファイル
68
+ │ │ ├── 20260301_120000__create_users_table.sql
69
+ │ │ ├── 20260302_093000__add_email_index.sql
70
+ │ │ └── ...
71
+ │ ├── schema.sql # 正規化されたスキーマ dump(生成物)
72
+ │ └── .migraguard/
73
+ │ └── metadata.json # ファイル一覧 + チェックサム(適用状態は含まない)
74
+ └── ...
75
+ ```
76
+
77
+ ### schema_migrations テーブル(各環境の DB に作成)
78
+
79
+ ```sql
80
+ CREATE TABLE IF NOT EXISTS schema_migrations (
81
+ file_name VARCHAR(256) NOT NULL,
82
+ checksum VARCHAR(64) NOT NULL,
83
+ status VARCHAR(16) NOT NULL DEFAULT 'applied', -- applied / failed / skipped
84
+ applied_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
85
+ resolved_at TIMESTAMPTZ, -- skipped 時の解決日時
86
+ PRIMARY KEY (file_name, checksum)
87
+ );
88
+ ```
89
+
90
+ `migraguard apply` の初回実行時に自動作成される。
91
+
92
+ 同一ファイルの再適用(チェックサム変更後の hotfix 再適用など)は別レコードとして記録される。これにより、あるファイルがいつ・どのバージョンで適用されたかの履歴が全て残る。
93
+
94
+ status の意味:
95
+
96
+ | status | 意味 |
97
+ |--------|------|
98
+ | `applied` | 正常に適用済み |
99
+ | `failed` | 適用を試みたがエラーで失敗。未解決 |
100
+ | `skipped` | `migraguard resolve` により明示的にスキップ。後続の forward migration で修正済みという人間の判断 |
101
+
102
+ ### 先祖返り検知
103
+
104
+ apply 時、ファイルの現在のチェックサムが **過去のレコード(最新以外)のチェックサムと一致** した場合、先祖返り(意図しない巻き戻し)としてエラーにする。
105
+
106
+ ```
107
+ 例:
108
+ schema_migrations に以下の履歴がある場合:
109
+ (S1.sql, checksum_v1, applied) ← 初回適用
110
+ (S1.sql, checksum_v2, applied) ← hotfix で再適用
111
+
112
+ ファイルのチェックサムが checksum_v1 に戻っている
113
+ → 最新レコードは checksum_v2 → 不一致 → さらに過去の checksum_v1 と一致
114
+ → 先祖返りとしてエラー
115
+ ```
116
+
117
+ これにより、ファイルを誤って古いバージョンに戻してしまった場合を検知できる。
118
+
119
+ ## 設定ファイル(migraguard.config.json)
120
+
121
+ ```json
122
+ {
123
+ "migrationsDir": "db/migrations",
124
+ "schemaFile": "db/schema.sql",
125
+ "metadataFile": "db/.migraguard/metadata.json",
126
+ "naming": {
127
+ "pattern": "{timestamp}__{description}.sql",
128
+ "timestamp": "YYYYMMDD_HHMMSS",
129
+ "prefix": "",
130
+ "sortKey": "timestamp"
131
+ },
132
+ "connection": {
133
+ "host": "localhost",
134
+ "port": 5432,
135
+ "database": "myapp_dev",
136
+ "user": "postgres"
137
+ },
138
+ "dump": {
139
+ "normalize": true,
140
+ "excludeOwners": true,
141
+ "excludePrivileges": true
142
+ },
143
+ "lint": {
144
+ "squawk": true
145
+ }
146
+ }
147
+ ```
148
+
149
+ ### naming 設定
150
+
151
+ | キー | デフォルト | 説明 |
152
+ |------|-----------|------|
153
+ | `pattern` | `{timestamp}__{description}.sql` | ファイル名のテンプレート。`{timestamp}`, `{prefix}`, `{description}` を使用可能 |
154
+ | `timestamp` | `YYYYMMDD_HHMMSS` | タイムスタンプのフォーマット。ソート順の基準になる |
155
+ | `prefix` | `""` | 全ファイルに付与する固定プレフィックス。カテゴリやサービス名の識別に使用 |
156
+ | `sortKey` | `timestamp` | ファイルのソート順を決定するキー。`timestamp`(タイムスタンプ部分で昇順)が標準 |
157
+
158
+ **カスタマイズ例**:
159
+
160
+ ```json
161
+ // マイクロサービス別にプレフィックスを付ける
162
+ {
163
+ "naming": {
164
+ "pattern": "{prefix}_{timestamp}__{description}.sql",
165
+ "prefix": "auth"
166
+ }
167
+ }
168
+ // → auth_20260301_120000__add_users_table.sql
169
+
170
+ // 連番ベースにする
171
+ {
172
+ "naming": {
173
+ "pattern": "{prefix}_{timestamp}__{description}.sql",
174
+ "timestamp": "NNNN",
175
+ "prefix": "billing"
176
+ }
177
+ }
178
+ // → billing_0001__create_invoices_table.sql
179
+
180
+ // カテゴリ + タイムスタンプ
181
+ {
182
+ "naming": {
183
+ "pattern": "{prefix}_{timestamp}__{description}.sql",
184
+ "prefix": "order-service"
185
+ }
186
+ }
187
+ // → order-service_20260301_120000__add_shipping_status.sql
188
+ ```
189
+
190
+ `migraguard new` はこの設定に従ってファイル名を生成する。`migraguard check` / `apply` はパターンに基づいてタイムスタンプ部分を抽出し、ソート順を決定する。
191
+
192
+ `connection` は環境変数(`PGHOST`, `PGPORT`, `PGDATABASE`, `PGUSER`, `PGPASSWORD`)でオーバーライド可能。
193
+
194
+ ## マイグレーションファイルの規約
195
+
196
+ ### ファイル命名
197
+
198
+ デフォルトのパターン:
199
+
200
+ ```
201
+ YYYYMMDD_HHMMSS__<description>.sql
202
+ ```
203
+
204
+ `naming` 設定でカスタマイズした場合:
205
+
206
+ ```
207
+ <prefix>_YYYYMMDD_HHMMSS__<description>.sql
208
+ ```
209
+
210
+ - タイムスタンプは UTC 固定(`naming.timestamp` で形式を変更可能)
211
+ - description は英数字とアンダースコアのみ
212
+ - 操作種別を先頭に付与: `create_`, `add_`, `alter_`, `drop_`, `backfill_`, `create_index_`
213
+ - prefix はカテゴリ・マイクロサービス名等の識別子(`naming.prefix` で設定)
214
+
215
+ ### 冪等性の担保
216
+
217
+ マイグレーション SQL は冪等に書く。途中失敗後の再実行で安全に完了すること。
218
+
219
+ ```sql
220
+ -- IF NOT EXISTS / IF EXISTS を活用
221
+ CREATE TABLE IF NOT EXISTS users (...);
222
+ CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_users_email ON users (email);
223
+
224
+ -- backfill は WHERE 句で冪等条件を付ける
225
+ UPDATE users SET status = 'active' WHERE status IS NULL;
226
+ ```
227
+
228
+ ## squash のフロー
229
+
230
+ `migraguard squash` は、開発中に作成した複数のマイグレーションファイルを 1 ファイルにまとめる。リリースブランチへのマージ前に実行する。
231
+
232
+ ```bash
233
+ npx migraguard squash
234
+ ```
235
+
236
+ 1. metadata.json を読み込む
237
+ 2. `migrationsDir` のファイルのうち、metadata.json に記録されていない新規ファイルを特定
238
+ 3. 新規ファイルが 2 つ以上ある場合、タイムスタンプ順に連結して 1 ファイルにまとめる
239
+ 4. マージ後のファイル名は最新のタイムスタンプを採用し、description は結合する
240
+ 5. 元のファイルを削除し、metadata.json を更新
241
+
242
+ ```
243
+ squash 前:
244
+ 20260228_120000__add_user_email.sql (新規)
245
+ 20260301_093000__add_email_index.sql (新規)
246
+
247
+ squash 後:
248
+ 20260301_093000__add_user_email_and_email_index.sql (1ファイルに統合)
249
+ ```
250
+
251
+ ### なぜ 1 リリース = 1 ファイルか
252
+
253
+ 複数ファイルが未適用の状態でリリースすると、以下の問題が起きる。
254
+
255
+ **エラー時に修正できない**:
256
+ - `20260228_` と `20260301_` が未適用の状態で検証環境に反映
257
+ - `20260228_` でエラー発生
258
+ - 修正したいが、最新ファイルは `20260301_` なので `20260228_` の変更は check でブロックされる
259
+ - `20260301_` は `20260228_` に依存しているため、両方の修正が必要になる
260
+
261
+ **1 ファイルなら単純**:
262
+ - squash 後の 1 ファイルがエラー → そのファイルを修正 → 再適用
263
+ - 最新ファイルルールに適合し、冪等性により安全に再実行できる
264
+
265
+ `migraguard check` は新規ファイル(metadata.json に未記録)が 2 つ以上ある場合にエラーとする。squash を実行してから push する運用を強制する。
266
+
267
+ ## apply のフロー
268
+
269
+ `migraguard apply` は対象 DB の `schema_migrations` テーブルを参照して未適用分を判定・実行する。
270
+
271
+ 1. DB の `schema_migrations` テーブルから全レコードを取得
272
+ 2. `migrationsDir` のファイルをタイムスタンプ順にソート
273
+ 3. 各ファイルについて、**そのファイル名の最新レコード**(`applied_at` が最大のもの)を参照して判定:
274
+ - 最新レコードなし → 新規ファイル → `psql -v ON_ERROR_STOP=1 -f <file>` で実行
275
+ - 最新レコード status=`applied` + チェックサム一致 → スキップ
276
+ - 最新レコード status=`applied` + チェックサム不一致:
277
+ - 過去レコードのチェックサムと一致 → 即エラー(先祖返り検知)
278
+ - 最新ファイル(末尾) → 再適用(冪等性前提)
279
+ - 最新以外 → 即エラー(改ざん検知)
280
+ - 最新レコード status=`skipped` → スキップ(`resolve` 済み)
281
+ - 最新レコード status=`failed`:
282
+ - 最新ファイル(末尾) → リトライ(修正済みの想定)
283
+ - 最新以外 → 即エラー(未解決の失敗。`migraguard resolve` または squash が必要)
284
+ 4. 実行結果に応じて `schema_migrations` へ **新規レコードを INSERT**:
285
+ - 成功 → status=`applied`
286
+ - 失敗 → status=`failed`、以降のファイルは実行しない
287
+ 5. 最新ファイル(末尾)は更新があっても再適用を許容する(開発中の繰り返し反映、および本番エラー時の hotfix 対応を想定)
288
+
289
+ metadata.json はこのフローでは参照しない。apply は DB の状態のみを信頼する。
290
+
291
+ ### 履歴の蓄積例
292
+
293
+ ```
294
+ schema_migrations テーブルの状態推移:
295
+
296
+ ── 初回適用 ──
297
+ (S1.sql, checksum_v1, applied, 2026-03-01 12:00)
298
+
299
+ ── S1 を修正して再適用(hotfix) ──
300
+ (S1.sql, checksum_v1, applied, 2026-03-01 12:00) ← 過去レコード(残る)
301
+ (S1.sql, checksum_v2, applied, 2026-03-01 15:00) ← 最新レコード
302
+
303
+ ── 誤って checksum_v1 に戻してしまった場合 ──
304
+ → 過去レコード checksum_v1 と一致 → 先祖返りエラー
305
+ ```
306
+
307
+ ### 失敗時のリカバリ
308
+
309
+ ```
310
+ ケース 1: 最新ファイル S1 が失敗(S1 の後にファイルがない)
311
+ → S1 を修正して再度 apply(S1 は最新なので修正可能、failed → リトライ)
312
+ → 新しいチェックサムで applied レコードが追加される
313
+
314
+ ケース 2: S1 が失敗した後に S2 が追加された(S1 は最新ではなくなった)
315
+ → apply は「S1 が未解決」でエラー停止
316
+ → 選択肢 A: migraguard resolve S1 → S1 を skipped にし、S2 で修正内容をカバー
317
+ → 選択肢 B: squash で S1 + S2 を 1 ファイルにまとめ直す
318
+ (DB 上の S1 の failed 記録は孤立するが、apply はファイル基準で動くため無害)
319
+ ```
320
+
321
+ ### resolve の動作
322
+
323
+ ```bash
324
+ npx migraguard resolve 20260301_093000__add_user_email.sql
325
+ ```
326
+
327
+ - 対象 DB の `schema_migrations` に、該当ファイルの最新 failed レコードと同じチェックサムで status=`skipped` のレコードを INSERT
328
+ - `resolved_at` に現在時刻を記録
329
+ - 該当ファイルの最新レコードが `failed` 以外の場合はエラー
330
+ - 後続の forward migration で修正済みであることを人間が確認した上で実行する操作
331
+
332
+ ### スキーマ dump による事前検証(apply 時)
333
+
334
+ `--verify` オプションを付けた場合、apply 前に以下の検証を行う。
335
+
336
+ 1. 現在の DB スキーマを dump し、保存済み `schema.sql` と比較
337
+ 2. 一致すれば apply を実行
338
+ 3. apply 完了後、新しいスキーマ dump を生成し `schema.sql` を更新
339
+
340
+ ```bash
341
+ migraguard apply --verify
342
+ ```
343
+
344
+ ## check のフロー(CI 向け)
345
+
346
+ `migraguard check` は CI 環境で実行することを想定したファイル整合性チェック。DB 接続は不要。
347
+
348
+ 1. metadata.json を読み込む
349
+ 2. `migrationsDir` の全ファイルのチェックサムを計算
350
+ 3. 以下をチェック:
351
+ - metadata.json に記録されたファイルのチェックサムが実ファイルと一致するか(最新ファイル以外)
352
+ - 最新ファイル以外に変更がないか
353
+ - 新しいファイルが途中に挿入されていないか(タイムスタンプ順で末尾以外に追加されていないか)
354
+ - **新規ファイル(metadata.json に未記録)が 2 つ以上ないか**
355
+ 4. 違反があればエラーコード 1 で終了し、差分の詳細を出力
356
+
357
+ ### check の限界と運用規約
358
+
359
+ check は DB に接続しないため、「どの環境に何が適用済みか」は判定できない。apply は DB 側で `failed` 状態を検知してエラー停止するが、理想的にはこの状況自体を避けるべきである。以下のケースは運用規約で予防する。
360
+
361
+ **問題が起きるケース**:
362
+
363
+ ```
364
+ 1. S1 を作成 → dev に反映(S1 適用済み)
365
+ 2. S1 を pro に反映する前に S2 を追加
366
+ 3. pro で S1 を apply → エラー発生 → schema_migrations に failed で記録
367
+ 4. S1 を修正したいが、最新は S2 なので check がブロック
368
+ 5. apply も「S1 が未解決」でエラー停止
369
+ 6. リカバリ: migraguard resolve S1 → S2 を apply(S2 で S1 の修正をカバー)
370
+ ```
371
+
372
+ リカバリは可能だが、S2 が S1 の意図を正しくカバーしているかは人間が判断する必要がある。この状況を避けるための運用規約を設ける。
373
+
374
+ **運用規約: 1 リリースの全環境デプロイが完了するまで、次のマイグレーションを追加しない**
375
+
376
+ ```
377
+ S1 作成 → dev 反映 → pro 反映 → 全環境完了
378
+
379
+ ここで初めて S2 を追加可能
380
+ ```
381
+
382
+ この規約に反して S2 を追加した後に S1 の修正が必要になった場合は、S1 を直接修正するのではなく、**新しい forward migration(S3)で修正内容を記述する**。S1 は変更せず、S3 で S1 の問題を補正する。
383
+
384
+ ### check と apply の役割分担
385
+
386
+ | | check | apply |
387
+ |---|---|---|
388
+ | 参照先 | metadata.json(リポジトリ) | schema_migrations テーブル(DB) |
389
+ | DB 接続 | 不要 | 必要 |
390
+ | 用途 | CI での事前検証(PR チェック) | 環境への実適用 |
391
+ | 検出対象 | ファイルの改ざん・不正な追加・複数新規ファイル | 未適用ファイルの特定・適用 |
392
+
393
+ ## リリースフロー
394
+
395
+ リリースブランチが `db_dev`(検証)、`db_pro`(商用)の場合の典型的なフロー。
396
+
397
+ ```
398
+ feature ブランチで開発:
399
+ migraguard new add_user_email → 20260228_120000__add_user_email.sql
400
+ migraguard new add_email_index → 20260301_093000__add_email_index.sql
401
+ (個別に apply してローカルで動作確認)
402
+
403
+
404
+ リリース準備:
405
+ migraguard squash → 1 ファイルにマージ
406
+ migraguard lint → lint チェック
407
+ migraguard check → 整合性チェック
408
+ git commit
409
+
410
+
411
+ db_dev にマージ → CI が apply → 検証環境に反映
412
+
413
+ │ エラー時: ファイル修正 → 再 apply(最新 = 唯一のファイルなので修正可能)
414
+
415
+
416
+ db_pro にマージ → CI が apply → 商用環境に反映
417
+
418
+ │ エラー時: 同じファイルを修正 → 再 apply
419
+ │ (検証環境は冪等性により修正版を再適用しても安全)
420
+
421
+
422
+ 全環境完了 ← ここで初めて次のマイグレーションを追加可能
423
+ ```
424
+
425
+ **重要**: 1 リリース(1 ファイル)の全環境デプロイが完了するまで、次のマイグレーションファイルを追加しない。この規約により、最新ファイルの修正・再適用が常に可能な状態を維持する。
426
+
427
+ ### 各環境の状態推移
428
+
429
+ ```
430
+ metadata.json db_dev schema_migrations db_pro schema_migrations
431
+ (リポジトリ) (検証 DB) (商用 DB)
432
+
433
+ squash後 [A, B, S] [A, B] [A, B]
434
+ dev反映後 [A, B, S] [A, B, S ✓] [A, B]
435
+ pro反映後 [A, B, S] [A, B, S ✓] [A, B, S ✓]
436
+ ↑ 次のリリース解禁
437
+
438
+ A, B = 過去の適用済みファイル
439
+ S = squash で生成されたファイル
440
+ ```
441
+
442
+ metadata.json はファイル一覧のみを持ち、どの環境に適用済みかは各 DB が管理する。同一の metadata.json で複数環境への段階的リリースが正しく動作する。
443
+
444
+ ### 規約に反した場合のリカバリ
445
+
446
+ 全環境デプロイ前に次のファイルを追加してしまい、過去ファイルの修正が必要になった場合:
447
+
448
+ 1. 過去ファイルを直接修正しない(check がブロックする)
449
+ 2. 新しい forward migration を作成し、過去ファイルの問題を補正する SQL を記述する
450
+ 3. squash で現在の新規ファイルと forward migration を 1 つにまとめる
451
+
452
+ ## GitHub Actions との連携
453
+
454
+ ### PR 時のチェック(例)
455
+
456
+ ```yaml
457
+ name: DB Migration Check
458
+ on:
459
+ pull_request:
460
+ paths:
461
+ - 'db/**'
462
+
463
+ jobs:
464
+ check:
465
+ runs-on: ubuntu-latest
466
+ steps:
467
+ - uses: actions/checkout@v4
468
+
469
+ - name: Setup Node.js
470
+ uses: actions/setup-node@v4
471
+ with:
472
+ node-version: '20'
473
+
474
+ - name: Install migraguard
475
+ run: npm ci
476
+
477
+ - name: Lint migrations
478
+ run: npx migraguard lint
479
+
480
+ - name: Check metadata integrity
481
+ run: npx migraguard check
482
+
483
+ - name: Verify schema dump
484
+ run: |
485
+ # shadow DB で全マイグレーション適用後の dump と比較
486
+ npx migraguard dump --connection-string "$SHADOW_DB_URL" > /tmp/actual_schema.sql
487
+ diff db/schema.sql /tmp/actual_schema.sql
488
+ ```
489
+
490
+ ### マイグレーション自動実行(例)
491
+
492
+ ```yaml
493
+ name: Apply Migrations
494
+ on:
495
+ push:
496
+ branches: [db_dev]
497
+ paths:
498
+ - 'db/migrations/**'
499
+
500
+ jobs:
501
+ apply:
502
+ runs-on: ubuntu-latest
503
+ environment: dev
504
+ steps:
505
+ - uses: actions/checkout@v4
506
+
507
+ - name: Setup Node.js
508
+ uses: actions/setup-node@v4
509
+ with:
510
+ node-version: '20'
511
+
512
+ - name: Install migraguard
513
+ run: npm ci
514
+
515
+ - name: Check integrity
516
+ run: npx migraguard check
517
+
518
+ - name: Apply with verification
519
+ run: npx migraguard apply --verify
520
+ env:
521
+ PGHOST: ${{ secrets.DB_HOST }}
522
+ PGDATABASE: ${{ secrets.DB_NAME }}
523
+ PGUSER: ${{ secrets.DB_USER }}
524
+ PGPASSWORD: ${{ secrets.DB_PASSWORD }}
525
+ ```
526
+
527
+ ## ローカル開発フロー
528
+
529
+ ```bash
530
+ # 1. 新規マイグレーションファイルを作成
531
+ npx migraguard new add_user_email
532
+
533
+ # 2. 生成された SQL ファイルを編集
534
+ vim db/migrations/20260301_120000__add_user_email.sql
535
+
536
+ # 3. ローカル DB に適用(最新ファイルは何度でも再適用可能)
537
+ npx migraguard apply
538
+
539
+ # 4. 必要に応じて追加ファイルを作成・適用
540
+ npx migraguard new add_email_index
541
+ vim db/migrations/20260302_093000__add_email_index.sql
542
+ npx migraguard apply
543
+
544
+ # 5. リリース前に squash で 1 ファイルにまとめる
545
+ npx migraguard squash
546
+
547
+ # 6. lint + 整合性チェック
548
+ npx migraguard lint
549
+ npx migraguard check
550
+
551
+ # 7. スキーマ dump を更新
552
+ npx migraguard dump
553
+
554
+ # 8. コミット
555
+ git add db/
556
+ git commit -m "add user email column and index"
557
+ ```
558
+
559
+ ## 依存ツリーモデル(拡張)
560
+
561
+ 線形順序モデルでは「末尾の 1 ファイルだけが修正・再適用可能」という制約がある。依存ツリーモデルはこの制約を緩和し、独立した変更の並行作業を可能にする。
562
+
563
+ ### 線形モデル vs 依存ツリーモデル
564
+
565
+ ```
566
+ 線形モデル:
567
+ A → B → C → D
568
+ ↑ D だけが修正可能。C のエラーが D のリリースをブロック
569
+
570
+ 依存ツリーモデル:
571
+ A (users テーブル作成)
572
+ / \
573
+ B C (B: users にカラム追加, C: orders テーブル作成)
574
+ | \
575
+ D E (D: users にインデックス, E: orders にインデックス)
576
+
577
+ D と E は互いに独立 → 両方とも修正・再適用可能
578
+ D のエラーが E のリリースをブロックしない
579
+ ```
580
+
581
+ ### 依存関係の解析方法
582
+
583
+ 各マイグレーション SQL を PostgreSQL パーサ(`libpg_query`)で AST にパースし、オブジェクトの生成・参照関係を抽出して DAG(有向非巡回グラフ)を構築する。
584
+
585
+ ```
586
+ SQL 文から抽出する情報:
587
+
588
+ CREATE TABLE users (...)
589
+ → 生成: users
590
+
591
+ ALTER TABLE users ADD COLUMN email VARCHAR(256)
592
+ → 依存: users → 生成: users.email
593
+
594
+ CREATE INDEX CONCURRENTLY ON users (email)
595
+ → 依存: users, users.email
596
+
597
+ CREATE TABLE orders (user_id INT REFERENCES users(id))
598
+ → 依存: users → 生成: orders
599
+ ```
600
+
601
+ 抽出可能な DDL と依存の種類:
602
+
603
+ | DDL | 生成 | 依存 |
604
+ |-----|------|------|
605
+ | `CREATE TABLE` | テーブル | なし(REFERENCES があれば参照先テーブル) |
606
+ | `ALTER TABLE ADD COLUMN` | カラム | テーブル |
607
+ | `ALTER TABLE ADD CONSTRAINT` | 制約 | テーブル、カラム、参照先テーブル |
608
+ | `CREATE INDEX` | インデックス | テーブル、カラム |
609
+ | `CREATE VIEW` | ビュー | 参照先テーブル |
610
+ | `CREATE FUNCTION` | 関数 | 参照先テーブル(本体の解析が必要) |
611
+ | `DROP *` | なし | 削除対象のオブジェクト |
612
+
613
+ ### check と apply への影響
614
+
615
+ 依存ツリーモデルでは「最新ファイル(末尾)」の概念が「葉ノード(他から依存されていないファイル)」に置き換わる。
616
+
617
+ **check の変更**:
618
+ - 変更可能なファイル: 葉ノードのみ(線形モデルの「末尾」に相当)
619
+ - 葉ノード以外のファイルが変更されていたらエラー
620
+ - 新規ファイルが既存の葉ノードに依存する場合、その葉ノードはもう葉ではなくなる → ロックされる
621
+
622
+ **apply の変更**:
623
+ - トポロジカルソート順にファイルを適用(依存先が先)
624
+ - 独立したファイル同士は順序不問
625
+ - 失敗時: 失敗したファイルに依存するファイルのみブロック。独立したファイルは影響を受けない
626
+
627
+ ```
628
+ 例: D が失敗した場合
629
+
630
+ A
631
+ / \
632
+ B C
633
+ | \
634
+ [D] E ← D と独立なので apply 可能
635
+
636
+ D の失敗は E のリリースをブロックしない
637
+ ```
638
+
639
+ ### 明示的依存の宣言
640
+
641
+ AST からの自動抽出には限界がある(動的 SQL、業務ロジック上の依存など)。自動抽出できない依存は SQL ファイル内のコメントで明示的に宣言する。
642
+
643
+ ```sql
644
+ -- migraguard:depends-on 20260228_120000__create_users_table.sql
645
+
646
+ SET lock_timeout = '5s';
647
+ ...
648
+ ```
649
+
650
+ または `migraguard.config.json` で宣言する。
651
+
652
+ ```json
653
+ {
654
+ "dependencies": {
655
+ "20260301_093000__backfill_user_status.sql": [
656
+ "20260228_120000__add_user_status_column.sql"
657
+ ]
658
+ }
659
+ }
660
+ ```
661
+
662
+ 自動抽出と明示的宣言をマージして最終的な DAG を構築する。明示的宣言は自動抽出の結果を上書きせず、追加の依存として合成される。
663
+
664
+ ### 大規模システムでの効果
665
+
666
+ | 制約 | 線形モデル | 依存ツリーモデル |
667
+ |------|-----------|-----------------|
668
+ | 同時に修正可能なファイル | 1(末尾のみ) | 葉ノードの数(独立した変更の数) |
669
+ | 並行リリース | 不可(全環境完了まで次を追加できない) | 独立したブランチは並行リリース可能 |
670
+ | エラーの影響範囲 | 全後続ファイルがブロック | 依存するファイルのみブロック |
671
+ | 複数チームの作業 | 直列化(1 チームずつ) | 独立したテーブルなら並行作業可能 |
672
+
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
+ ### 依存解析の候補ライブラリ
684
+
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()`)を提供 |
689
+
690
+ Squawk は単一ファイル内の lint に特化しており、ファイル間の依存抽出機能は持たない。依存解析は上記ライブラリを使って自前で構築する。
691
+
692
+ ## 既存ツールとの比較
693
+
694
+ migraguard の特徴は「運用規約そのものをツールに埋め込んで、CI で事故を未然に潰す」点にある。既存ツール(Flyway / Atlas / Sqitch / Graphile Migrate など)との差分を整理する。
695
+
696
+ ### 1. 二層の状態管理で多環境の段階的リリースを設計の中心に置いている
697
+
698
+ リポジトリ側の整合性(metadata.json)と各 DB 側の適用状態(schema_migrations)を明確に分離し、「同一リポジトリから dev → pro の段階的リリース」を設計として破綻しにくくしている。
699
+
700
+ [Atlas](https://atlasgo.io/concepts/migration-directory-integrity) も `atlas.sum` でディレクトリ整合性(Merkle hash tree 風)を担保するが、migraguard はさらに「環境ごとの差(applied / failed / skipped)を DB 側に寄せる」ことを明示的に設計している。
701
+
702
+ ### 2. 「最新版だけ編集 OK」をルール + 履歴設計で安全側に倒している
703
+
704
+ 単に「最新版だけ編集可」ではなく、apply 時に以下の判定ロジックまで踏み込んでいる。
705
+
706
+ - 最新レコードが applied だが checksum 不一致 → 過去 checksum と一致したら「先祖返り」としてエラー
707
+ - 最新ノード(線形なら末尾、DAG なら葉ノード)だけは再適用許可
708
+ - それ以外は改ざんとして即エラー
709
+
710
+ Flyway / Liquibase にも checksum 検証はあるが、「先祖返りを明確にエラー扱いする」を標準の運用モデルとして提示している例は多くない。
711
+
712
+ ### 3. DB 接続不要の整合性チェックを CI 前提で強制できる
713
+
714
+ `migraguard check` は metadata.json と実ファイルのチェックサム突合で、DB 接続なしに以下を検出する。
715
+
716
+ - 最新以外の変更を検出してエラー
717
+ - 途中挿入を検出してエラー
718
+ - 新規ファイルが 2 つ以上ならエラー(squash を強制)
719
+
720
+ Atlas も整合性ファイル(`atlas.sum`)でディレクトリの整合性を強制できるが、migraguard は「squash による 1 リリース = 1 ファイル」を機械的に強制するところまで実装に落としている。
721
+
722
+ ### 4. スキーマ dump diff を apply ゲートに組み込んでいる
723
+
724
+ `migraguard dump` / `migraguard diff` を機能として持ち、`apply --verify` で以下を一連の手順として定義している。
725
+
726
+ 1. 現在 DB の dump と保存 `schema.sql` が一致するか検証
727
+ 2. 一致したら apply
728
+ 3. apply 後に新 dump を生成して `schema.sql` を更新
729
+
730
+ [Graphile Migrate](https://github.com/graphile/migrate) も「pg_dump を git 管理して差分を見る」ことを推奨しているが、migraguard はそれを verify ゲートとして apply に組み込み、手順抜け(運用のブレ)を減らす設計になっている。
731
+
732
+ ### 5. 失敗の取り扱いが現場運用(止める / 進める)を具体化している
733
+
734
+ `schema_migrations` に `failed` / `skipped` を持たせ、`resolve` で明示的にスキップする運用を定義している。
735
+
736
+ - 失敗したファイルが最新でなければ apply は即エラー停止(自動スキップしない)
737
+ - `resolve` は人間の判断を介在させる明示的操作
738
+ - 後続の forward migration で修正する運用を明文化
739
+
740
+ 既存ツールにも repair / ignore 的な逃げ道はあるが、migraguard は「どういう状況でそれを使うか」まで含めて設計されている。
741
+
742
+ ### 6. 依存 DAG への拡張が設計に組み込まれている
743
+
744
+ 線形モデルから依存 DAG(葉ノード = 編集可)への拡張が具体的に設計されている。
745
+
746
+ - 依存解析で独立した変更を並行リリース可能にする
747
+ - 失敗の影響を依存範囲に閉じる
748
+ - トポロジカルソート順に apply
749
+
750
+ [Sqitch](https://sqitch.org/docs/manual/sqitch/) は依存をマイグレーション間で宣言できるが、Merkle tree パターンで整合性を担保する。migraguard は「SQL ファイル(psql 実行)+ CI ゲート + dump diff」を軸にしたまま DAG へ拡張する方向性。
751
+
752
+ ### まとめ
753
+
754
+ migraguard は既存ツールが提供する「実行エンジン」や「履歴テーブル」よりも、以下を重視している。
755
+
756
+ - 運用事故の典型パターン(途中挿入、過去改変、先祖返り、段階的リリース中の詰まり)を CI ゲートと規約で潰す
757
+ - スキーマ dump を監査可能な成果物として常に diff 可能にし、drift を検知する
758
+ - 末端(線形なら末尾、DAG なら葉ノード)だけを可変にして hotfix を許す代わりに検出ロジックを強くする
759
+
760
+ これらを最小構成(psql + SQL + JSON + dump)で実現する。
761
+
762
+ ## apply の排他制御
763
+
764
+ 冪等な SQL であっても、同時実行は競合状態を引き起こし得る。apply は排他制御として PostgreSQL の advisory lock を使用する。
765
+
766
+ ```
767
+ apply の実行フロー(排他制御込み):
768
+ 1. DB 接続確立
769
+ 2. pg_advisory_lock(hashtext('migraguard-apply')) を取得
770
+ → 同時に別プロセスが apply を実行している場合はブロック(待機)
771
+ 3. schema_migrations を参照して未適用分を判定
772
+ 4. 各ファイルを psql で実行、結果を schema_migrations に記録
773
+ 5. 接続クローズ(advisory lock は自動解放)
774
+ ```
775
+
776
+ advisory lock はセッション単位で効くため、apply 全体を単一セッションで実行する必要がある。接続が切れた場合はロックが自動解放され、再実行が安全に行える。
777
+
778
+ CI パイプラインの並列実行(同一環境への同時 apply)や、手動 apply とパイプラインの競合を防止する。
779
+
780
+ ## DAG 移行時の互換方針
781
+
782
+ 線形モデルから依存ツリーモデルへ移行する際、既存環境の schema_migrations との整合性を維持する必要がある。
783
+
784
+ ### 移行手順
785
+
786
+ 1. **既存の schema_migrations はそのまま維持**: 線形モデルで記録されたレコードは、DAG モデルでも「依存関係が暗黙的に全直列」のファイルとして扱う
787
+ 2. **移行ポイントのマーカー**: DAG モデル導入時に metadata.json に `"model": "dag"` フラグを追加。このフラグ以前のファイルは線形順序、以降のファイルは DAG 解析対象とする
788
+ 3. **後方互換**: DAG モデル対応の migraguard は線形モデルの metadata.json も読める。逆(DAG → 線形へのダウングレード)は非サポート
789
+
790
+ ```
791
+ metadata.json の移行例:
792
+
793
+ {
794
+ "model": "dag",
795
+ "modelSince": "20260401_000000__first_dag_migration.sql",
796
+ "migrations": [
797
+ {"file": "20260301_...", "checksum": "aaa"}, ← 線形モデル時代(全直列扱い)
798
+ {"file": "20260302_...", "checksum": "bbb"}, ← 線形モデル時代
799
+ {"file": "20260401_...", "checksum": "ccc"} ← DAG モデル(依存解析対象)
800
+ ]
801
+ }
802
+ ```
803
+
804
+ ### check / apply の動作
805
+
806
+ - `modelSince` より前のファイル: 従来通りタイムスタンプ順の線形チェック
807
+ - `modelSince` 以降のファイル: DAG 解析による葉ノード判定、トポロジカルソート適用
808
+ - 両者の境界: `modelSince` のファイルは、それ以前の全ファイルに暗黙的に依存する(線形モデルの最終状態を引き継ぐ)
809
+
810
+ ## 技術スタック
811
+
812
+ | 項目 | 技術 |
813
+ |------|------|
814
+ | 言語 | TypeScript(Node.js) |
815
+ | DB 接続 | `psql` CLI(DDL ファイルを直接渡す) |
816
+ | スキーマ dump | `pg_dump --schema-only` |
817
+ | SQL lint | [Squawk](https://squawkhq.com/) |
818
+ | SQL パーサ | [libpg_query](https://www.npmjs.com/package/libpg_query) / [@pg-nano/pg-parser](https://www.npmjs.com/package/@pg-nano/pg-parser)(依存解析用) |
819
+ | パッケージ管理 | npm |
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import '../dist/cli.js';
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+
2
+ export { }
package/dist/cli.js ADDED
@@ -0,0 +1,57 @@
1
+ import { Command } from 'commander';
2
+
3
+ // src/cli/index.ts
4
+
5
+ // src/index.ts
6
+ var VERSION = "0.0.1";
7
+
8
+ // src/cli/index.ts
9
+ var program = new Command();
10
+ program.name("migraguard").description("PostgreSQL migration guard \u2014 idempotent SQL migrations with CI-enforced integrity checks").version(VERSION);
11
+ program.command("new <name>").description("Create a new migration SQL file with UTC timestamp").action((_name) => {
12
+ console.log("Not yet implemented");
13
+ process.exit(1);
14
+ });
15
+ program.command("apply").description("Apply pending migrations via psql").option("--verify", "Verify schema dump before and after apply").action(() => {
16
+ console.log("Not yet implemented");
17
+ process.exit(1);
18
+ });
19
+ program.command("check").description("Verify metadata integrity (no DB connection required)").action(() => {
20
+ console.log("Not yet implemented");
21
+ process.exit(1);
22
+ });
23
+ program.command("squash").description("Squash multiple new migration files into one").action(() => {
24
+ console.log("Not yet implemented");
25
+ process.exit(1);
26
+ });
27
+ program.command("lint").description("Run Squawk lint on migration files").action(() => {
28
+ console.log("Not yet implemented");
29
+ process.exit(1);
30
+ });
31
+ program.command("dump").description("Dump and normalize current DB schema").action(() => {
32
+ console.log("Not yet implemented");
33
+ process.exit(1);
34
+ });
35
+ program.command("diff").description("Show diff between current DB schema and saved schema.sql").action(() => {
36
+ console.log("Not yet implemented");
37
+ process.exit(1);
38
+ });
39
+ program.command("status").description("Show migration status (applied / pending / failed / skipped)").action(() => {
40
+ console.log("Not yet implemented");
41
+ process.exit(1);
42
+ });
43
+ program.command("resolve <file>").description("Mark a failed migration as skipped (requires human judgment)").action((_file) => {
44
+ console.log("Not yet implemented");
45
+ process.exit(1);
46
+ });
47
+ program.command("editable").description("List migration files that are currently editable (leaf nodes or latest file)").action(() => {
48
+ console.log("Not yet implemented");
49
+ process.exit(1);
50
+ });
51
+ program.command("deps").description("Analyze and display migration dependency graph").option("--dot", "Output in DOT format for Graphviz").action(() => {
52
+ console.log("Not yet implemented");
53
+ process.exit(1);
54
+ });
55
+ program.parse();
56
+ //# sourceMappingURL=cli.js.map
57
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/cli/index.ts"],"names":[],"mappings":"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","file":"cli.js","sourcesContent":["export const VERSION = '0.0.1';\n","import { Command } from 'commander';\nimport { VERSION } from '../index.js';\n\nconst program = new Command();\n\nprogram\n .name('migraguard')\n .description('PostgreSQL migration guard — idempotent SQL migrations with CI-enforced integrity checks')\n .version(VERSION);\n\nprogram\n .command('new <name>')\n .description('Create a new migration SQL file with UTC timestamp')\n .action((_name: string) => {\n console.log('Not yet implemented');\n process.exit(1);\n });\n\nprogram\n .command('apply')\n .description('Apply pending migrations via psql')\n .option('--verify', 'Verify schema dump before and after apply')\n .action(() => {\n console.log('Not yet implemented');\n process.exit(1);\n });\n\nprogram\n .command('check')\n .description('Verify metadata integrity (no DB connection required)')\n .action(() => {\n console.log('Not yet implemented');\n process.exit(1);\n });\n\nprogram\n .command('squash')\n .description('Squash multiple new migration files into one')\n .action(() => {\n console.log('Not yet implemented');\n process.exit(1);\n });\n\nprogram\n .command('lint')\n .description('Run Squawk lint on migration files')\n .action(() => {\n console.log('Not yet implemented');\n process.exit(1);\n });\n\nprogram\n .command('dump')\n .description('Dump and normalize current DB schema')\n .action(() => {\n console.log('Not yet implemented');\n process.exit(1);\n });\n\nprogram\n .command('diff')\n .description('Show diff between current DB schema and saved schema.sql')\n .action(() => {\n console.log('Not yet implemented');\n process.exit(1);\n });\n\nprogram\n .command('status')\n .description('Show migration status (applied / pending / failed / skipped)')\n .action(() => {\n console.log('Not yet implemented');\n process.exit(1);\n });\n\nprogram\n .command('resolve <file>')\n .description('Mark a failed migration as skipped (requires human judgment)')\n .action((_file: string) => {\n console.log('Not yet implemented');\n process.exit(1);\n });\n\nprogram\n .command('editable')\n .description('List migration files that are currently editable (leaf nodes or latest file)')\n .action(() => {\n console.log('Not yet implemented');\n process.exit(1);\n });\n\nprogram\n .command('deps')\n .description('Analyze and display migration dependency graph')\n .option('--dot', 'Output in DOT format for Graphviz')\n .action(() => {\n console.log('Not yet implemented');\n process.exit(1);\n });\n\nprogram.parse();\n"]}
@@ -0,0 +1,3 @@
1
+ declare const VERSION = "0.0.1";
2
+
3
+ export { VERSION };
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ // src/index.ts
2
+ var VERSION = "0.0.1";
3
+
4
+ export { VERSION };
5
+ //# sourceMappingURL=index.js.map
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"names":[],"mappings":";AAAO,IAAM,OAAA,GAAU","file":"index.js","sourcesContent":["export const VERSION = '0.0.1';\n"]}
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "migraguard",
3
+ "version": "0.0.1",
4
+ "description": "PostgreSQL migration guard — idempotent SQL migrations with CI-enforced integrity checks, schema drift detection, and dependency-aware apply",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/index.js",
12
+ "types": "./dist/index.d.ts"
13
+ }
14
+ },
15
+ "bin": {
16
+ "migraguard": "./bin/migraguard.js"
17
+ },
18
+ "engines": {
19
+ "node": ">=20.0.0"
20
+ },
21
+ "files": [
22
+ "dist",
23
+ "bin",
24
+ "COMMANDS.md"
25
+ ],
26
+ "scripts": {
27
+ "build": "tsup",
28
+ "dev": "tsup --watch",
29
+ "lint": "eslint",
30
+ "test": "vitest",
31
+ "test:run": "vitest run",
32
+ "typecheck": "tsc --noEmit",
33
+ "prepublishOnly": "npm run build"
34
+ },
35
+ "keywords": [
36
+ "postgresql",
37
+ "migration",
38
+ "database",
39
+ "schema",
40
+ "ddl",
41
+ "psql",
42
+ "ci",
43
+ "drift-detection",
44
+ "idempotent",
45
+ "dag"
46
+ ],
47
+ "author": "",
48
+ "license": "MIT",
49
+ "dependencies": {
50
+ "chalk": "^5.3.0",
51
+ "commander": "^12.1.0",
52
+ "glob": "^10.3.10"
53
+ },
54
+ "devDependencies": {
55
+ "@types/node": "^20.11.0",
56
+ "eslint": "^9.39.2",
57
+ "@eslint/js": "^9.39.2",
58
+ "@typescript-eslint/eslint-plugin": "^8.54.0",
59
+ "@typescript-eslint/parser": "^8.54.0",
60
+ "tsup": "^8.0.1",
61
+ "typescript": "^5.3.3",
62
+ "typescript-eslint": "^8.54.0",
63
+ "vitest": "^1.2.0"
64
+ }
65
+ }