Claude CodeでFlywayのマイグレーションを書く — infoとvalidateで確認しcleanは拒否
Claude CodeにFlywayのSQLマイグレーションを書かせるとき、infoとvalidateで確認する順序、適用済みSQLを編集させない規約、cleanとrepairをpermissionsで止める設定を示します。
Claude CodeでFlywayのマイグレーションを書く — infoとvalidateで確認しcleanは拒否
Claude CodeにFlywayのマイグレーションを書かせるときに決めておくことは3つです。確認に使うコマンドの順序、適用済みのSQLファイルを書き換えさせない規約、そしてcleanを実行できないようにする設定です。
Flywayのcleanは、設定したスキーマのオブジェクトをすべて削除します。Flywayのドキュメント自身が「本番DBに対して使わないこと」と注意書きを置いているコマンドです。Claude Codeが開発DBで試行錯誤する流れのなかに、このコマンドが紛れ込む余地を残さないのが、この記事の中心です。
Claude Codeに使わせるFlywayコマンドを仕分ける
Flywayのコマンドは、DBの状態を読むだけのもの、履歴に書き込むもの、中身を消すものに分かれます。Claude Codeの権限もこの3段階に合わせると、設定が単純になります。
| コマンド | 何をするか | Claude Codeでの扱い |
|---|---|---|
info | 何をするか全マイグレーションの詳細と状態を表示する | Claude Codeでの扱い許可 |
validate | 何をするか適用済みと手元のファイルを突き合わせる | Claude Codeでの扱い許可 |
migrate | 何をするかスキーマを最新バージョンまで進める | Claude Codeでの扱い開発DBに限り許可 |
repair | 何をするかスキーマ履歴テーブルを直す | Claude Codeでの扱い確認を挟む(ask) |
clean | 何をするか設定したスキーマのオブジェクトをすべて削除する | Claude Codeでの扱い拒否(deny) |
repairをaskに置く理由は次の節で説明します。cleanの説明は、ドキュメントでは1行です。「設定したスキーマの全オブジェクトを削除する。Flywayが自動作成したスキーマなら、スキーマごと削除する」。事故防止の既定設定はあるものの、「本当にやるつもりだったのかを対話で聞くことはできない」とも書かれています。確認の最後の砦は、Flywayではなく呼び出す側にあります。
適用済みのSQLを編集させない
Flywayのvalidateは、適用済みのマイグレーションと手元で見つかるマイグレーションを比べます。失敗する条件は3つです。
- 名前・種類・チェックサムに違いがある
- 適用済みなのに、手元で見つからなくなったバージョンがある
- 手元にあるのに、まだ適用されていないバージョンがある
チェックサムは、SQLマイグレーションの場合CRC32で、実行時に保存されます。validateは、手元のファイルのチェックサムがDBに記録された値と同じかを確かめます。
つまり、適用済みのV1__first.sqlをClaude Codeが「ついでに」直すと、validateが落ちます。Claude Codeは不具合を見つけると、既存のSQLを直したくなります。ここを防ぐ規約をCLAUDE.mdに置きます。
## マイグレーション(Flyway)
- 適用済みのバージョンのSQLは編集しない。直したいときは新しいバージョンのSQLを足す
- 新しいSQLを書いたら、次の順で実行して出力を読み、結果を報告する
1. flyway info(新しいファイルが Pending で並んでいるか)
2. flyway migrate(開発DBだけ)
3. flyway validate
4. flyway info(新しいファイルが適用済みになっているか)
- flyway clean と flyway repair は実行しない。必要だと思ったら理由を書いて止まる
- 接続先は環境の設定で渡される。本番の設定や接続情報は読まない最後の一文が効くのは、Claude Codeが「接続できない」と困ったとき、設定ファイルを探して本番の値に行き当たるのを避けるためです。
repairは「検証を通すための道具」になりうる
repairは、履歴テーブルのチェックサムと説明と種類を、手元のファイルに揃え直します。失敗したマイグレーションを履歴から除く、見当たらなくなったマイグレーションを削除済みとして記録する、という動作もします。
先ほどのvalidateが落ちた場面で、repairを打てばvalidateは通ります。履歴の側を書き換えて、編集を後追いで承認することになるためです。検証の意味が消えるので、Claude Codeが勝手に実行しないようaskに置き、人が理由を見てから判断します。
infoとvalidateを往復する検証ループ
順序はinfo、migrate、validate、infoです。validateをmigrateの前に置くと、新しく書いたファイルが「手元にあるが未適用」にあたるため、検証が落ちます。validateは適用後に走らせるものと決めておきます。
flyway info
flyway migrate
flyway validate
flyway info出力が長くなる場合は、infoの絞り込み用パラメータが使えます。-infoOfStateは、指定した状態のマイグレーションだけを表示します。-infoSinceVersion=1.1のように、特定のバージョン以降へ絞ることもできます。Claude Codeが読むログを短くしたいときに向きます。-migrationIdsを付けると他の出力を抑え、バージョン番号だけをカンマ区切りで返します。どのバージョンが対象かだけを知りたい場面で、読む量をさらに減らせます。
flyway info -infoOfState=pendingドキュメントの出力例では、infoのJSONに各マイグレーションのstate(例: Pending)とfilepath(例: V1__first.sql、繰り返し適用型のR__repeatable.sql)が含まれます。validateのJSONはvalidationSuccessfulで成否を返し、失敗時の手がかりとしてinvalidMigrationsとerrorDetails、検査した件数を示すvalidateCountも含みます。Claude Codeに結果を読ませるなら、これらの項目で判断するよう指示に書いておくと、判定が文面の印象に依存しません。
cleanをpermissionsでdenyする
ここからが記事の本題です。.claude/settings.jsonに次を置きます。
{
"permissions": {
"deny": [
"Bash(flyway clean*)",
"Bash(flyway * clean*)",
"Bash(mvn *flyway:clean*)",
"Bash(gradle *flywayClean*)",
"Bash(./gradlew *flywayClean*)"
],
"ask": [
"Bash(flyway repair*)",
"Bash(mvn *flyway:repair*)",
"Bash(gradle *flywayRepair*)",
"Bash(./gradlew *flywayRepair*)"
]
}
}cleanの呼び出し方は、ドキュメントに3通り書かれています。コマンドラインのflyway clean、Mavenのmvn flyway:clean、Gradleのgradle flywayCleanです。flyway -url=... cleanのようにオプションを前に置く形もあるため、flyway * clean*も加えています。ラッパー(./gradlew)の分も別に書きます。
評価はdeny、ask、allowの順に行われ、denyに一致した呼び出しは、より狭いallowがあっても止まります。たとえばBash(flyway *)をallowにしたあとでも、上のcleanのdenyは勝ちます。この性質があるので、infoとvalidateとmigrateの許可は広く書けます。
{
"permissions": {
"allow": [
"Bash(flyway info*)",
"Bash(flyway validate*)",
"Bash(flyway migrate*)"
]
}
}cleanが消す範囲と、出力の読み方
cleanが対象にするのは、設定したスキーマの中のオブジェクトです。Flywayがスキーマを自動で作っていた場合は、スキーマそのものも削除されます。対象のスキーマが開発DBの共有スキーマだと、同じDBを使う別の作業も巻き込みます。
ドキュメントに載っている制限事項は1つだけです。SQL Serverでは、ユーザーは削除されません。
実行結果のJSONには、schemasCleaned(中身を消したスキーマ)、schemasDropped(スキーマごと削除したもの)、warningsが含まれ、operationはcleanです。拒否をすり抜けて実行されたときは、この出力で何が消えたかを確かめます。
Mavenのmvn flyway:cleanは、既定のフェーズがpre-integration-testだとドキュメントにあります。pom.xmlでcleanを実行段階に結び付けてある場合、Bashルールでは見えない経路で動く可能性があります。Claude Codeにpom.xmlを編集させる変更は、レビューでflywayの設定部分を見る価値があります。
denyが止められない形
Bashルールがマッチするのは、Claudeが書いたコマンドの文字列です。権限ドキュメントは、Bash(rm *)がrm -rf build/を止める一方、/bin/rm -rf build/やbash -c 'rm -rf build/'は止めないと例示しています。同じことがflyway cleanにも言えます。次のような形は上のルールをすり抜けます。
/opt/flyway/flyway cleanのような絶対パスでの起動sh -c 'flyway clean'のようなシェル経由docker runでFlywayのコンテナを起動する形、npx経由の起動
したがってdenyは、Claudeがいつも書く形を止める保険として置きます。確実さが必要なら、コマンド全文を検査するPreToolUseフックを足します。フックはpermissionDecisionにdenyを返せます。
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-flyway-clean.sh",
"args": []
}
]
}
]
}
}#!/bin/bash
# .claude/hooks/block-flyway-clean.sh
COMMAND=$(jq -r '.tool_input.command')
if echo "$COMMAND" | grep -Eiq 'flyway.*clean|flywayClean'; then
jq -n '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: "flyway clean is blocked. Add a new migration instead."
}
}'
else
exit 0
fi文字列の部分一致なので、絶対パスやシェル経由でもコマンド全文にflywayとcleanが含まれていれば止まります。flywayとcleanが離れて出てくる無関係なコマンドも巻き込むため、誤検知は許容する設計です。フックを書くときは、permissionDecisionReasonに「代わりに何をするか」まで入れると、Claude Codeが次の手を選びやすくなります。フックの全体像はClaude Code Hooks完全ガイドにあります。
接続先のDBを役割で分ける
cleanを止めても、本番の接続情報が手元にあれば、migrateが本番へ流れます。Flywayのコマンドは、接続先となる環境が設定または引数で渡されて初めて動きます。ドキュメントにも、すべてのパラメータは省略可能だが「対象環境は設定するか渡す必要がある」とあります。
ここで言えることは単純です。Claude Codeが動く端末には、開発DBの環境だけを渡します。本番へのmigrateは、レビュー後にCIのデプロイジョブが実行します。役割ごとの認証情報の分け方は、Alembicを扱ったClaude CodeでAlembicのマイグレーションを書くの「本番DBの権限を分ける」と同じ考え方です。読み取り系だけを許可する線引きは、Claude CodeでOpenTofuを使うでも同じ形で出てきます。
よくあるつまずき
新しいSQLを書いた直後にvalidateが落ちる
未適用のバージョンがあるときのvalidateは失敗します。仕様どおりの動作なので、migrateの後に実行する順序へ直します。CLAUDE.mdの手順に番号を振っておくと、Claude Codeが順序を入れ替えません。
適用済みのSQLを直したら、validateのチェックサム不一致になった
直した内容を新しいバージョンのSQLとして書き直します。repairを打つ判断は、askの確認画面で理由を読んでから行います。
Claude Codeが「DBをリセットしたい」と言い出す
開発中にスキーマが壊れた場面で、Claude Codeはcleanを候補に挙げることがあります。denyで止まったあとは、フックのpermissionDecisionReasonに書いた代替手段(新しいマイグレーションを足す)に誘導します。開発DBを作り直したい場合は、DBそのものを人が作り直し、Claude Codeにはmigrateだけをやり直させます。
まとめ
- 確認の順序は
info、migrate、validate、info。validateは適用後に走らせる - 適用済みのSQLは編集させず、足すのは新しいバージョンだけにする
cleanはdeny、repairはaskに置く。呼び出し方はCLI、Maven、Gradleの3通りを書く- denyは文字列の照合で、境界にはならない。全文を見るPreToolUseフックを併用する
- 本番の接続情報はClaude Codeの環境に置かず、本番への
migrateはCIに渡す
サンドボックスでDB接続が止まる場合の切り分けはClaude Codeのsandboxから繋がらないにあります。Packerのように別のコマンドをhookで守る例はClaude CodeでPackerのAMI定義を書くが参考になります。