🎢
GitHub Actionsでrails db:migrate/rollbackを実行しdb/schema.rbの差分をチェックする
これはなに?
PRを出したときにrails db:migrate
とrails db:rollback
を実行しその実行がエラーにならないことと、それぞれの実行後にdb/schema.rb
に差分がでないことを確認するGitHub Actionsのworkflowです。
コード
本題に関係ない処理は省いています。
.github/workflows/migrate_test.yml
name: Migrate Test
on:
pull_request:
branches:
- develop
paths:
- 'db/migrate/*.rb'
- 'db/schema.rb'
- '.github/workflows/migrate_test.yml'
jobs:
migrate-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: 3.2.2
bundler-cache: true
- name : Exec Migrate
run: bundle exec rails db:migrate
# migrate実行後にdb/schema.rbに差分がでていないかチェック
- name: Check schema diff after migrate
run: |
DIFF="$(git diff HEAD db/schema.rb)"
if [[ $DIFF != '' ]]; then
echo 'db:migrateを実行するとdb/schema.rbに差分が発生します。差分が出ないように修正してください'
git diff HEAD
exit 1
fi
# 追加されたdb/migrate配下のファイルの数を取得
- name: Count new migration files
id: count-migrations
run: |
git fetch origin ${{ github.base_ref }}
count=$(git diff --name-only --diff-filter=A origin/${{ github.base_ref }} HEAD | grep db/migrate/ | wc -l)
echo "Number of new migrations: $count"
echo "count=$count" >> $GITHUB_OUTPUT
# 追加されたmigrateファイルの数をSTEPに指定してrollback
- name: Rollback database
if: steps.count-migrations.outputs.count > 0
working-directory: ./lc-local-dev
run: |
bundle exec rails db:rollback STEP=${{ steps.count-migrations.outputs.count }}
# rollback後にPRのターゲットブランチと比較して差分がでていないかチェック
- name: Check schema diff after rollback
if: steps.count-migrations.outputs.count > 0
run: |
git fetch origin ${{ github.base_ref }}
ROLLBACK_DIFF="$(git diff origin/${{ github.base_ref }} db/schema.rb)"
if [[ $ROLLBACK_DIFF != '' ]]; then
echo 'db:rollbackを実行するとdb/schema.rbに差分が発生します。差分が出ないように修正してください'
echo $ROLLBACK_DIFF
exit 1
fi
モチベーション
副業で複数のRailsアプリケーションが1つのDBを利用する構成のシステムに関わる機会がありました。
共通で参照しているテーブルもあり、当然ですがDBになにかあった場合は複数のアプリケーションに影響します。
このような状況下でdb:migrate
はそこそこ頻度が多いにもかかわらず割とリスクが伴う操作となっています。
特にいざ切り戻しが必要となった場合にdb:rollback
が失敗したり、テーブル定義がdb:migrate
をする前と異なってしまうというのは二次障害につながるため避けたい事態でした。
このような背景からmigrationファイル追加をするPRを出したときにdb:migrate
とdb:rollback
ができることをGitHub Actionsでチェックするようにしました。
補足
- 複数人が同時にmigration追加のPRを作成し、それらのうちタイムスタンプが新しい日付のものが先にマージされた場合、古い方のタイムスタンプのPRはrollbackの対象ファイルが追加したファイルにならないため必ず
db/schema.rb
に差分が発生し失敗します。- ただしこの場合
db/schema.rb
にコンフリクトが発生していると思います。 - また、railsのmigrateがタイムスタンプ順に実行される仕様から、PRを作成したときと前提とするDBの状態が変わっている可能性があるため、タイムスタンプを新しくつけ直すのが無難だと考えています。
- ただしこの場合
-
rubocop-rails
のRails/ReversibleMigration
でmigrationファイルのコードがrollbackでmigrate実行前の状態に戻せるものになっているかチェックすることができます。ある程度はこれで検出できると思いますが、実際にrollbackを実行してその結果を確認するほうが確実だと考えています。
参考
Discussion