🥑

golang-migrate の マイグレーションファイル名をチェックする CLI を作った

に公開

TL;DR

golang-migratemigrate create コマンドで作成した マイグレーションファイル名(<VERSION>_<NAME>.(up|down).<ext> の形式)が期待通りになっているかをチェックする CLI miglint を作りました。

https://github.com/tetzng/miglint

「マイグレーションファイルの命名規則が崩れてないか」「version が被ってないか」「up/down が片方だけになってないか」みたいなのをツールで検知できます。

なぜ作ったか

チームで golang-migrate などのマイグレーションツールを使っているときに、マイグレーションのバージョンが期待通りの順番になっているかというのは地味に気を使うポイントでした。
レビュー時やマージ前に、以下のようなことを毎回手動でチェックするのは面倒ですし、見落とす可能性もあります。

  • up/down の NAME 部分が一致しているか
  • (連番運用している場合)欠番がないか
  • 最新を取り込むときに version が被らないか (別ブランチで同じ version のマイグレーションファイルが作られていて、その変更が先にマージされたケースなどに起こりがち)
  • up と down の両方のファイルが存在しているか

こういうのが、機械的にチェックできたら嬉しいなと思って、ファイル名だけを見る軽量な lint ツールを作りました

golang-migrate の migration の命名規則

golang-migrate には migrate create コマンドがあり、これでマイグレーションファイルを作ると以下のような命名規則でファイルが生成されます。

  • <VERSION>_<NAME>.up.<ext>
  • <VERSION>_<NAME>.down.<ext>

例えば migrate create -ext sql -seq create_users_table で作るとこうなります。

path/to/000001_create_users_table.up.sql
path/to/000001_create_users_table.down.sql

(-seq を指定しない場合だと 20060102150405_<NAME>... のような形式)

何をチェックするか

miglint は 指定されたディレクトリを見て、ファイルの命名と整合性について以下のような観点でチェックします。

  • VERSION_NAME.(up|down).EXT 形式になっているか
  • 同じ version の up/down が複数ないか
  • down の作り忘れがないか(任意)
  • up/down の NAME や拡張子が一致しているか(任意)
  • 000001 みたいに version の桁数を固定したい(任意)
  • 000001, 000002, 000004 ... みたいな欠番を許さない(任意)

任意のオプションが多いのは、 migrate create の挙動自体が柔軟なためです。プロジェクトごとに運用ルールが異なる可能性があるため、必要なチェックだけを有効化できるようにしています。

使い方

インストール

go install github.com/tetzng/miglint/cmd/miglint@latest

コマンド実行例

miglint -path ./db/migrations

migrateコマンドと同様に、-path オプションでマイグレーションファイルが置かれているディレクトリを指定します。
このオプションは必須です。

オプションの説明

-require-down

up だけ作って down がないファイルを検出するようにします

-strict-name-match

up/down の NAME と拡張子が一致しているかをチェックします
例えば、こういう不一致を検出できます。

  • 000001_create_users.up.sql
  • 000001_create_user.down.sql ← users が user になってる

-digits

golang-migrate では 1_, 2_... や 0001_, 000001_ のように version の桁数を揃えずに連番を付けることもできますが、桁数を揃えたい場合があります。

miglint -path ./db/migrations -digits 6

このようにすると 001_create.up.sql みたいなファイルがあった場合に検出できます。

-no-gaps

versionの欠番を検出します。

  • 000001_create.up.sql
  • 000003_add_column.up.sql ← 000002 がない

こういうのを検出します。
seq 運用の場合のみ使いたいオプションですね。

-ext / -enforce-ext

拡張子を .sql だけに限定したい場合。

miglint -path ./db/migrations -ext sql

さらに、マイグレーションファイルっぽい命名で拡張子が違うやつを検出したいなら -enforce-ext を追加します。

例えば 000001_a.down.txt みたいなのを検出します。

sql.gz みたいな複合拡張子にも対応しています。
-ext sql.gz なら .up.sql.gzsql.gz 部分で一致
-ext gz なら 最後の gz 部分で一致

-strict-pattern

migrations ディレクトリにマイグレーションファイルっぽくないファイルが混入している場合に検出します。

  • 123notes.sql(数字から始まるけど形式が違う)
  • 000001_add_user.up(.up で止まっていて拡張子がない)
  • 000001_a.up.(末尾ドット)

個人的なおすすめ設定

筆者は連番で運用することが多いので、以下のような設定で使うつもりです。

miglint -path ./db/migrations \
  -ext sql -enforce-ext \
  -require-down \
  -strict-name-match \
  -digits 6 \
  -no-gaps \
  -strict-pattern

注意点

  • 指定したディレクトリ直下のファイルだけをチェックします(サブディレクトリは探索しません)
  • マイグレーションファイルの形式に合わないファイル名は、デフォルトだと無視(-strict-pattern でエラー化)
  • シンボリックリンクなどは無視されます

CI に組み込む

プロジェクトの運用方針にもよりますが、これを CI に入れると migration 周りのヒューマンエラーがかなり減らせると思います。

GitHub Actions の例を示します。

name: migration lint

on:
  pull_request:

jobs:
  miglint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6

      - uses: actions/setup-go@v6
        with:
          go-version-file: 'go.mod'

      - name: Install miglint
        run: go install github.com/tetzng/miglint/cmd/miglint@latest

      - name: Lint migrations
        run: |
          miglint -path ./db/migrations \
            -ext sql -enforce-ext \
            -require-down \
            -strict-name-match \
            -digits 6 \
            -no-gaps \
            -strict-pattern

おわりに

miglint は、筆者の携わっているプロジェクトのために作ったものですが、同じような悩みを持っている人の役に立てば嬉しいです。
もし「こういうチェックもしたい」とか「こういう命名規則も許容したい」みたいなのがあれば、気軽に Issue や PR を投げてください。

GitHubで編集を提案

Discussion