はじめに
こんにちは、金子です。
これまで、DB設計書はツールで自動作成していましたが、ツールを動かすのは人の作業でした。AIでの自動化に伴い、社内全体のフローを徐々に見直していて、今回はGitHub上で自動作成する仕組みを作りました。DDLをpushするだけで、DB定義書とER図がGitHub Actionsで生成されます。
仕組み
シンプルです。
- phpMyAdminなどから、構造のみのDDLをエクスポートしてリポジトリー内(e2infoでは
docs/db/schema/input/<DB名>.sql)に置く - featureブランチにpushすると、GitHub Actionsが起動する
- CI上で使い捨てのMySQLを起動してDDLを流し込み、tblsで定義書とER図を生成する
- 生成物を同じブランチに自動コミットする
PRを作っていれば、自動コミットもPRの差分に載ります。レビュアーはDBの変更を、定義書の差分としても確認できます。
ワークフローの本体は共通リポジトリに置き、各プロジェクトには呼び出し用のYAMLだけを置きます。YAMLを各リポジトリにコピーする方式も考えましたが、修正を一箇所で済ませるためにこの構成にしました。
各プロジェクトに置く .github/workflows/db-docs.yml
on:
push:
branches:
- "feature/**"
paths:
- "docs/db/schema/input/*.sql"
workflow_dispatch:
jobs:
db-docs:
permissions:
contents: write
uses: your-org/shared-workflows/.github/workflows/db-docs.yml@main
共通リポジトリに置く本体 .github/workflows/db-docs.yml
name: Generate DB Docs & ERD (Reusable Workflow)
on:
workflow_call:
inputs:
mysql-image:
description: "CI上で起動する使い捨てMySQLコンテナのイメージ"
type: string
default: "mysql:8.0"
sql-glob:
description: "取り込み対象DDLのglobパターン"
type: string
default: "docs/db/schema/input/*.sql"
tbls-config-path:
description: "tblsの設定ファイルパス(呼び出し側リポジトリ内)"
type: string
default: "docs/db/.tbls.yml"
secrets:
docs_commit_token:
description: "自動コミットのpushに使うトークン。未指定時はGITHUB_TOKEN"
required: false
jobs:
build-docs:
runs-on: ubuntu-latest
permissions:
contents: write
# 使い捨てMySQLコンテナ。本番/開発DBへは接続しない。
services:
mysql:
image: ${{ inputs.mysql-image }}
env:
MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
ports:
- 3306:3306
# optionsはdocker createに渡るためdockerフラグのみ可。
# --tmpfs: データディレクトリをRAM上に置き起動を短縮。
options: >-
--tmpfs /var/lib/mysql
--health-cmd="mysqladmin ping"
--health-interval=1s
--health-timeout=1s
--health-retries=15
steps:
- uses: actions/checkout@v7
with:
token: ${{ secrets.docs_commit_token || github.token }}
# サードパーティ製アクションはコミットハッシュで固定する。
- uses: k1low/setup-tbls@5e8e69748c0bb15fdb5e51a0ecdb84f1ecd0e3a1 # v1.4.1
# DDLごとに一時DBを作り、output/<DB名>/ へ定義書を出力する。
- name: Import DDLs and Generate Docs
id: generate
run: |
glob='${{ inputs.sql-glob }}'
outroot="$(dirname "$(dirname "$glob")")/output"
echo "output-root=$outroot" >> "$GITHUB_OUTPUT"
for file in ${{ inputs.sql-glob }}; do
[ -e "$file" ] || continue
dbname=$(basename "$file" .sql)
docpath="$outroot/$dbname"
mysql -h 127.0.0.1 -u root -e "CREATE DATABASE \`$dbname\`;"
mysql -h 127.0.0.1 -u root "$dbname" < "$file"
# --config: TBLS_DSN設定時はtbls設定が自動読み込みされないため明示する。
TBLS_DSN="mysql://root:@127.0.0.1:3306/$dbname" \
TBLS_DOC_PATH="$docpath" \
tbls doc --config "${{ inputs.tbls-config-path }}" --rm-dist
done
- name: Auto commit generated docs
uses: stefanzweifel/git-auto-commit-action@4a55954c782fc1ea30b9056cd3e7a2b40ca8887d # v7.2.0
with:
commit_message: "docs: auto-generate DB docs and ERD"
# 生成物だけをコミット対象にする。
file_pattern: "${{ steps.generate.outputs.output-root }}/*"
DBが2つ以上ある案件でも、input/ にファイルを足すだけで、出力はDBごとのフォルダに分かれます。

ハマったところ
mysqldのオプションが渡せない
CIのMySQLを速くしようとして services の options に --innodb-... を書いたら、unknown flagで失敗しました。ここは docker create に渡るオプションなので、dockerのフラグしか書けません。代わりに --tmpfs /var/lib/mysql でデータをメモリに置いて、起動を速くしています。
日本語にならない
.tbls.yml の dict で見出しを日本語化しているのに、なぜか英語のまま出力されました。TBLS_DSN を環境変数で渡すと設定ファイルが自動で読み込まれないようで、--config を明示したら直りました。
TBLS_DSN="mysql://root:@127.0.0.1:3306/$dbname" \
TBLS_DOC_PATH="$docpath" \
tbls doc --config docs/db/.tbls.yml --rm-dist
最後のDBしか残らない
DBが複数あるとき、出力先を共有していると --rm-dist で前のDBの出力が消えます。DBごとにサブフォルダを分けて解決しました。
AIに任せること、任せないこと
導入作業はAIエージェント(Claude Codeなど)にお願いすることが多いので、READMEにAI向けの指示文を用意しました。
最初は共通リポジトリのURLだけを渡していたのですが、呼び出し側のYAMLが作られず、ワークフローが動かないということがありました。本体は workflow_call なので、単体では起動しません。そこで「呼び出し用のファイルをそのままコピーして作ること」「書き換えや要約をしないこと」を指示文に明記しています。
一方で、入力のDDLだけはAIに作らせないことにしました。本番と検証でDB定義や構造が違う案件がたまにあるため、現時点では人間が判定しています(現時点では)
まとめ
- tbls+GitHub Actionsで、DB設計書とER図の自動出力を全社共通の仕組みにしました
- AIには導入作業をお願いしつつ、設計書の正しさを決める入力DDLは人が作ることにしました





