BASEアドベントカレンダー2021 10日目の記事です。
BASE BANKでエンジニアをしている @budougumi0617 です。
マイグレーションファイルが含まれたPull Request(PR)が作られたとき、自動更新したER図をPRに追加するGitHub Actionsを作りました。
本記事では紹介するGitHub Actionsを利用すると次のようなメリットが得られます。
- マイグレーションファイルをPRに出すだけでPRに更新されたER図が追加される
- 開発者は面倒なER図の更新作業から開放される
- レビューアはマイグレーションファイルを含んだPRをER図を見ながらレビューできるようになる
- プロジェクト関係者は常にメインブランチのマイグレーションファイルの状態と一致したER図を確認できる
TL;DR
- ER図はあれば便利だけれど運用しているとメンテが大変
k1LoW/tbls
をGitHub Actionsで動かすとマイグレーションファイルのPull Request(PR)に自動更新したER図を追加できる- RDBMSのスキーマをマイグレーションツールで管理しているのが前提条件
- マイグレーションファイルの追加PRでマイグレーション後のER図を確認しながらレビューできる
- マイグレーションファイルを書くだけでER図をメンテする必要がなくなる
サンプルリポジトリはこちらです。
GitHub ActionsのYAMLを見たい方はこちらを参照してください。 https://github.com/budougumi0617/sample_tbls_actions/blob/v0.0.1/.github/workflows/tbls.yml
k1LoW/tbls
で自動生成しているER図はこのディレクトリにあります。
https://github.com/budougumi0617/sample_tbls_actions/tree/v0.0.1/schema/dbdoc
マイグレーションファイルを含んだPRに対してGitHub ActionsでER図を更新しているPRのサンプルはこちらです。
https://github.com/budougumi0617/sample_tbls_actions/pull/2
ER図の必要性
Webアプリケーションを開発・運用しているならば、大抵の場合RDBMSを利用していると思います。
私のチームではRDBMSのテーブル定義をrubenv/sql-migrate
によるマイグレーションによって管理しています。
マイグレーションによるテーブル管理を行なっているとき問題になるのがER図の管理です。
マイグレーションファイルとER図を両方利用しているとどうやってER図を最新の状態と一致させるのかが運用上の課題になります。新規開発サービスだったり機能追加が活発なサービスの背後にあるRDBMSは毎週のようにマイグレーションが実施されます。このようなRDBMSの状態を手動でER図に反映し続けるのはかなりの労力が必要です。
また、マイグレーションファイルのレビューをするときはDDLとして正しいかだけではなく、DBの設計として妥当であるかもレビューする必要があります。そのためには他のカラム、他のテーブルを含めてマイグレーション後の姿を俯瞰的に確認する必要があります。
ER図を運用する時の課題
前述したとおりER図はあったほうが良いですがER図を作り維持し続けるには様々な課題があります。
- 本番環境のテーブルの状態とER図がかならずマッチしている保証がない
- マイグレーションファイルが真か?ER図が真か?
- ER図が古いだけなのか?それともマイグレーションが意図通り行われなかったのか?
- マイグレーションファイルを書くのが先か、ER図を書くのが先か
- 俯瞰的にレビューするならばER図がほしい
- オレは
ADD COLUMN
したいだけなんだ…- マイグレーションをする側からすると少しDDLを書くだけで済むはずなのにマイグレーション結果に影響しないER図の修正まで気を使うのは大変…
単純なカラム追加だけならばよいですが、外部キー制約付きのテーブル追加のようなDDLを書く場合ER図の更新は非常に煩わしいものでした。
k1LoW/tblsという救世主
メンテフリーなER図を実現するための救世主が github.com/k1LoW/tbls
です。
tbls
のいいところそれだけで2記事くらい書けるので省略しますが、シンプルに設定した接続先のDBのスキーマ情報を(デフォルトでは)MarkdownとSVGファイルに出力してくれます。
CI-Friendly
とREADMEにかかれている通りシングルバイナリでsvg
ファイルまで生成してくれる点もとてもありがたいです。これ系のツールでありがちな「まずはGraphviz
をインストールします」ということもないのでCI上でも簡単に実行できます。
しかし、tbls
は「データベースに接続してER図を自動生成するツール」です。
つまりマイグレーションを当てた状態のデータベースを用意しないとtbls
を使うことができません。
マイグレーションの妥当性を確認する際もER図を見たいのに、マイグレーションしないとER図が生成できません。
そこでマイグレーションファイルを含んだPRが作られるたびにActions上でRDBMSを起動するようにActionsを書きました。
マイグレーションを含んだPRでER図を自動更新するGitHub Actions
今回作成したGitHub Actionsの全容は以下のとおりです。 https://github.com/budougumi0617/sample_tbls_actions/blob/v0.0.1/.github/workflows/tbls.yml
name: update er graph on: pull_request: paths: - schema/sample/** - schema/tbls.yml jobs: tbls: name: generate-and-push runs-on: ubuntu-latest services: mysql: image: mysql:5.7 options: --health-cmd "mysqladmin ping -h localhost" --health-interval 20s --health-timeout 10s --health-retries 10 ports: - 3306:3306 env: MYSQL_ALLOW_EMPTY_PASSWORD: yes MYSQL_DATABASE: sample MYSQL_USER: sample MYSQL_PASSWORD: sample steps: - name: Checkout uses: actions/checkout@v2 with: ref: ${{ github.event.pull_request.head.ref }} token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} - name: Setup go uses: actions/setup-go@v2 with: go-version: '^1.17.1' - name: Execute migration run: | go get -u github.com/rubenv/sql-migrate/sql-migrate make up ENV=ci working-directory: ./schema - name: Execute tbls run: | curl -sL https://git.io/use-tbls > use-tbls.tmp && . ./use-tbls.tmp && rm ./use-tbls.tmp tbls doc -f working-directory: ./schema # tbls実行後、差分有りもしくは新規ファイルの数をカウントする - name: Count uncommit files id: check_diff run: | git status --porcelain | wc -l file_count=$(git status --porcelain | wc -l) echo "::set-output name=file_count::$file_count" working-directory: ./schema - name: Commit ER graph # 更新したER図をPRにコミットする if: ${{ steps.check_diff.outputs.file_count != '0' }} run: | git config user.name github-actions git config user.email github-actions@github.com git add . git commit -m "generate er graphs from actions" git push working-directory: ./schema # PRへ自動コミットしたらPRにコメントしておく - name: Report commit on pull request if: ${{ steps.check_diff.outputs.file_count != '0' }} uses: actions/github-script@v4 with: script: | github.issues.createComment({ issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, body: 'Actions committed new ER files🤖' })
Actionsの主な流れは次のとおりです。
- PRにマイグレーションファイルの変更が含まれていた場合実行する
- 事前にMySQLを起動しておく
- マイグレーションファイルを実行する
tbls
コマンドを実行してER図を生成する- もし既存のER図と差分があったら、PRに対してその差分をコミットする
- PRにマイグレーションファイルを更新した旨をコメントする
ひとつひとつ該当するYAMLの抜粋と一緒に解説していきます。
PRにマイグレーションファイルの変更が含まれていた場合実行する
GitHub Actionsは特定のディレクトリに更新があったときのみ起動する設定が可能です。
私たちのリポジトリはアプリケーションコードと一緒にマイグレーションファイルがコミットされていますが、この設定によりアプリケーションコードを変更するだけのPRで意味もなくCIが実行されることを防ぎます。
on: pull_request: paths: - schema/sample/** - schema/tbls.yml
事前にMySQLを起動しておく
GitHub Actionsはワークフロー中にサービスコンテナという名前でDBなどのDockerコンテナを起動しておく事ができます。 docs.github.com
今回は次のような宣言でMySQLを起動しておきます。
mysql: image: mysql:5.7 options: --health-cmd "mysqladmin ping -h localhost" --health-interval 20s --health-timeout 10s --health-retries 10 ports: - 3306:3306 env: MYSQL_ALLOW_EMPTY_PASSWORD: yes MYSQL_DATABASE: sample MYSQL_USER: sample MYSQL_PASSWORD: sample
コードをクローンしてGitHubの設定をする
コードをクローンしてGitの設定も行ないます。ここで設定したGitの設定は後半の更新したER図のコミット・pushにも利用されます。
- name: Checkout uses: actions/checkout@v2 with: ref: ${{ github.event.pull_request.head.ref }} token: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
マイグレーションファイルとアプリケーションコードを同じリポジトリに入れている場合、Actionsで利用するトークンはデフォルトのトークンではなく、払い出したトークンを利用したほうがよいです。 これはデフォルトのトークンでPRに対してコミットをpushすると、そのコミットに対しては(無限ループ事故の防止のため)Actionsが起動しないためです。
本記事で紹介しているActionsは起動しなくても問題ありません。しかし、アプリケーションコードとマイグレーションファイルを一緒にPRで変更していた場合ActionsからのpushでActionsが起動しないことになります。
ただ起動しないだけならばいいのですが、 CIのチェックステータスも消えるため、他のActionsでfailしていた状態も消えます。
PRのBranch Protection RuleでCIのステータスを利用しているならば必ずトークンを払い出して再度Actionsが実行されるようにしておきます。
マイグレーションファイルを実行する
先ほど示したMySQLに対してマイグレーションを実行しておきます。
これでER図を生成したいDBがGitHub Actions上に構築されます。
- name: Checkout uses: actions/checkout@v2 with: ref: ${{ github.event.pull_request.head.ref }} token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} - name: Setup go uses: actions/setup-go@v2 with: go-version: '^1.17.1' - name: Execute migration run: | go get -u github.com/rubenv/sql-migrate/sql-migrate make up ENV=ci working-directory: ./schema
make up ENV=ci
コマンドは次のMakefileの設定と、dbconfig.yml
で成り立っています。
https://github.com/budougumi0617/sample_tbls_actions/blob/v0.0.1/schema/Makefile
ENV := "local" up: ## Apply migration files sql-migrate up -env=$(ENV) -config= dbconfig.yml
https://github.com/budougumi0617/sample_tbls_actions/blob/v0.0.1/schema/dbconfig.yml
ci: dialect: mysql datasource: root@tcp(127.0.0.1:3306)/sample?parseTime=true dir: sample
tbls
コマンドを実行してER図を生成する
tbls
をインストールして実行します。CIフレンドリー過ぎてやることはこれだけです。あっけないですね…
- name: Execute tbls run: | curl -sL https://git.io/use-tbls > use-tbls.tmp && . ./use-tbls.tmp && rm ./use-tbls.tmp tbls doc -f working-directory: ./schema
tblsの設定自体も接続情報とsql-migrate
が利用しているマイグレーション管理用のテーブルを除外するようにしているくらいです。
https://github.com/budougumi0617/sample_tbls_actions/blob/v0.0.1/schema/tbls.yml
dsn: mysql://root@127.0.0.1:3306/sample?parseTime=true er: comment: true distance: 9 exclude: - gorp_migrations
もし既存のER図と差分があったら、PRに対してその差分をコミットする
先ほど実行したtbls
によってER図のファイルに何らかの変化があった場合、gitのステータスに差分が出ます。
一つでも差分を見つけたらコミットを作り、PRのブランチに対してpushします。
このようにしておくことで空コミットが作られたり、Actionsが無限にループ実行することありません。
- name: Count uncommit files id: check_diff run: | git status --porcelain | wc -l file_count=$(git status --porcelain | wc -l) echo "::set-output name=file_count::$file_count" working-directory: ./schema - name: Commit ER graph # 更新したER図をPRにコミットする if: ${{ steps.check_diff.outputs.file_count != '0' }} run: | git config user.name github-actions git config user.email github-actions@github.com git add . git commit -m "generate er graphs from actions" git push working-directory: ./schema
PRにマイグレーションファイルを更新した旨をコメントする
最後にPRにER図を更新した旨を書くコメントを残して終わります。
- name: Report commit on pull request if: ${{ steps.check_diff.outputs.file_count != '0' }} uses: actions/github-script@v4 with: script: | github.issues.createComment({ issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, body: 'Actions committed new ER files🤖' })
あとはPERSONAL_ACCESS_TOKEN
と言う名前でGitHubパーソナルアクセストークンを保存しておけば次のPRよりER図が自動生成されるようになります。
自動生成したER図が追加されたサンプルPR
上記のGitHub Actionsを使って更新されたPRが次になります。
このPRではマイグレーションファイルが一つ追加されています。
PRに対してGitHub Actionsが実行された結果、更新されたER図が追加されています。
開発者は何もせずマイグレーション後のER図をPRに追加できました。
レビューアは更新されたER図を確認することで、表で示されたテーブル構成や図示されたリレーションを見ながらレビューすることができます。
https://github.com/budougumi0617/sample_tbls_actions/blob/migrate-card/schema/dbdoc/cards.md
このPRが妥当ならばマイグレーションファイルと更新されたER図がmain
ブランチにマージされます。
終わりに
以上のGitHub Actionsにより、我々の開発では以下の開発者体験が実現できました。
- GitHub上でER図をいつでもテーブル定義を確認できる
- ER図とマイグレーション結果が一致していることを保証する
- 開発者はER図を書く必要がない
- マイグレーション後のER図を確認しながらマイグレーションファイルのPRをレビューできる
明日は @pigooosuke さんです!