オズビジョンのSREチームリーダの卜部です。
前回 ステージング環境のマイグレーションでDB定義が競合しにくい仕組みにしてみた - その1 の記事でマイグレーションが起こりにくい仕組みを作成した内容の投稿をしました。
しかしながら、上記の対応では migrate up と対になる migrate down をエンジニアが正しく実装してくれた前提での話となります。
弊社では CI に Scrutinizer CI を使い、コードに問題があるかを気づけるような仕組みになっています。今回、ここを使い、migrate up と対になる migrate down が実装されているか、確認が行える仕組みを検討します。
build:
root_path: ./
environment:
timezone: "Asia/Tokyo"
php: 7.2
mysql: 5.6
postgresql: false
redis: false
tests:
override:
- command: stty cols 80; ./vendor/bin/phpunit --coverage-clover=.coverage.xml
coverage:
file: ".coverage.xml"
format: "clover"
idle_timeout: 1200
ここにコマンドを一つ追加します
build:
root_path: ./
environment:
timezone: "Asia/Tokyo"
php: 7.2
mysql: 5.6
postgresql: false
redis: false
tests:
override:
- command: stty cols 80; ./vendor/bin/phpunit --coverage-clover=.coverage.xml
coverage:
file: ".coverage.xml"
format: "clover"
idle_timeout: 1200
- command: chmod 777 ./tests/migration-test.sh && ./tests/migration-test.sh
./tests/migration-test.sh というテストスクリプトを追記しました。
この中身は以下のようになっています
#!/bin/bash
SCRIPT_DIR=$(cd $(dirname $0); pwd)
# use https://github.com/lehmannro/assert.sh
. $SCRIPT_DIR/assert.sh
STATUS=0
NOMAL_OUTPUT=/dev/stdout
ERROR_OUTPUT=/dev/stderr
# migration を初期バージョンに戻してからテスト
while true;
do
CURRENT=$($SCRIPT_DIR/bin/console doctrine:migrations:status --em=migration|grep 'Current Version:'|cut -d: -f2- |xargs)
echo migration down. current: $CURRENT
if [ "$CURRENT" == "0" ]; then
echo OK. Already at first version.
break;
fi
$SCRIPT_DIR/bin/console doctrine:migrations:migrate prev --em=migration --no-interaction > /dev/null 2>&1
done
# バージョン毎の migrate up, down の差分比較
for target in $(ls $SCRIPT_DIR/app/DoctrineMigrations/*.php|sort -n)
do
version=$(basename $target|perl -pe 's/Version([0-9]{14})\.php/\1/ig')
echo $version migration test
mysqldump -uroot migration_ut -d --compact --ignore-table=ut_db.migration_versions | perl -pe 's/AUTO_INCREMENT=[0-9]+ //s' > /tmp/before.$version.txt 2> /dev/null
# migrate up 前の DB 定義を /tmp に保存
$SCRIPT_DIR/bin/console doctrine:migrations:migrate $version --em=migration --no-interaction > $NOMAL_OUTPUT 2> $ERROR_OUTPUT
RETURN=$?
[ "$RETURN" != "0" ] && STATUS=1
# migrate up 自体の正常実行チェック
assert "echo $RETURN" "0"
$SCRIPT_DIR/bin/console doctrine:migrations:migrate prev --em=migration --no-interaction > $NOMAL_OUTPUT 2> $ERROR_OUTPUT
RETURN=$?
[ "$RETURN" != "0" ] && STATUS=1
# migrate down 自体の正常実行チェック
assert "echo $RETURN" "0"
mysqldump -uroot migration_ut -d --compact --ignore-table=ut_db.migration_versions | perl -pe 's/AUTO_INCREMENT=[0-9]+ //s' > /tmp/after.$version.txt 2> /dev/null
# migrate down 後の DB 定義を /tmp に保存
diff /tmp/before.$version.txt /tmp/after.$version.txt > $NOMAL_OUTPUT 2> $ERROR_OUTPUT
# 差分がないことをチェック
RETURN=$?
[ "$RETURN" != "0" ] && STATUS=1
assert "echo $RETURN" "0"
$SCRIPT_DIR/bin/console doctrine:migrations:migrate $version --em=migration --no-interaction > /dev/null 2>&1
# 次のマイグレーションのテストを行うため、次のバージョンに migrate up
done
assert_end php-migration
exit $STATUS
簡単に説明すると以下のようなチェックが実施されます
- migration 用の DB の状態を初期のマイグレーションバージョンに戻す
- migration ファイルのバージョン一覧を古い順に取り出し、以下のチェックを行う
- a. DB のテーブル定義を保存する
- b. migrate up 自体が正常に行えることを確認 (assert1)
- c. migrate down 自体が正常に行えることを確認 (assert2)
- d. DB のテーブル定義を保存
- e. a. と d. のテーブル定義で差分がないかをチェック (assert3)
単純なUTではありますが、こうすることで、migrate up, down が正しく作られているかのチェックが行え、
その1の記事の ステージング環境でブランチを切り替えても マイグレーションを一度過去に戻して、必要なマイグレーションのみ実行されるようにするという仕組みの完成版になります。
なお、今回は DDL に限った課題解決となります。マイグレーションに DML が含まれる場合に対しては今回の仕組みでは対処できません。DML を含む場合は、別途テストをしっかり実施していただく必要があるかと思います。