🔰 はじめに
Rails + PostgreSQL のデータベーススキーマを可視化する SchemaSpy を使って、自動でスキーマのドキュメントを生成し、 Cloudflare Pages にデプロイする方法を紹介します。
💡 この方法のメリット
- GitHub Actions を活用して完全自動化
- SchemaSpy を使って ER 図を生成
- Cloudflare Pages にホスティング
- Cloudflare Functions で Basic 認証を追加し、公開範囲を制限
- GitHub Pages の制約を回避(Enterprise でなくても非公開運用が可能!)
🌍 使用するサービスの説明
GitHub
GitHub はソースコードを管理するプラットフォームです。今回のワークフローでは、GitHub Actions を使ってデータベースのスキーマを取得し、Cloudflare Pages に自動デプロイします。
GitHub Actions
GitHub Actions は CI/CD(継続的インテグレーション / 継続的デリバリー) を実現するための仕組みです。
リポジトリの変更をトリガーにして、スクリプトを自動実行できます。
GitHub Pages
GitHub Pages は GitHub が提供する静的サイトのホスティングサービスです。
ただし、無料プランでは「公開」しかできない(組織以下のリポジトリで、組織がエンタープライズプラン出ないと一般公開しかできないらしい)ため、非公開での利用は不可。
そのため、今回は GitHub Pages ではなく Cloudflare Pages にデプロイします。
Cloudflare Pages + Functions
Cloudflare Pages は、GitHub リポジトリと連携してデプロイできるホスティングサービスです。
また、Cloudflare Functions を活用すると function機能を利用すれば Basic 認証をつけることが可能 なので、一応セキュアにスキーマ情報を管理できます。
📌 GitHub Actions の設定
以下の GitHub Actions ワークフローを .github/workflows/schemaspy.yml
に作成します。
name: Generate SchemaSpy Docs
on:
push:
branches:
- main # main ブランチに push された時
paths:
- 'db/schema.rb' # db/schema.rb が変更された時のみ実行
workflow_dispatch: # 手動実行も可能
jobs:
generate-docs:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:latest
env:
POSTGRES_DB: my_database
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U postgres"
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Wait for PostgreSQL to be ready
run: sleep 10s
- name: Setup Ruby and Bundler
uses: ruby/setup-ruby@v1
with:
ruby-version: .ruby-version # .ruby-version がない場合には、3.4.1 とか記載してください
bundler-cache: true
- name: Install dependencies
run: bundle install
- name: Set up database schema
env:
RAILS_ENV: test
DATABASE_URL: postgres://postgres:postgres@localhost:5432/my_database
run: |
bundle exec rails db:create
bundle exec rails db:migrate
- name: Install Graphviz (for ER diagrams)
run: sudo apt-get update && sudo apt-get install -y graphviz
- name: Install SchemaSpy and JDBC Driver
run: |
wget https://github.com/schemaspy/schemaspy/releases/latest/download/schemaspy-6.2.4.jar -O schemaspy.jar
wget https://jdbc.postgresql.org/download/postgresql-42.6.0.jar -O postgresql-jdbc.jar
- name: Generate SchemaSpy documentation
run: |
mkdir -p schemaspy-output
java -jar schemaspy.jar \
-t pgsql \
-host localhost \
-port 5432 \
-db my_database \
-u postgres \
-p postgres \
-s public \
-o schemaspy-output \
-dp postgresql-jdbc.jar
- name: Copy _middleware.js to Cloudflare Functions
run: |
mkdir -p schemaspy-output/functions
cp cloudflare/_middleware.js schemaspy-output/functions/_middleware.js
- name: Deploy to GitHub Pages
uses: JamesIves/github-pages-deploy-action@v4
with:
branch: gh-pages
folder: schemaspy-output
clean: true
🔐 Cloudflare Functions で Basic 認証を追加
Cloudflare Pages で、Basic 認証を追加するための functions/_middleware.js
を作成します。
- name: Copy _middleware.js to Cloudflare Functions
run: |
mkdir -p schemaspy-output/functions
cp cloudflare/_middleware.js schemaspy-output/functions/_middleware.js
この部分で、SchemaSpyが作成した schemaspy-output ディレクトリ以下にfunctions/_middleware.js
をコピーしてます。
中身は↓
/**
* Shows how to restrict access using the HTTP Basic schema.
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication
* @see https://tools.ietf.org/html/rfc7617
*
* A user-id containing a colon (":") character is invalid, as the
* first colon in a user-pass string separates user and password.
*/
const BASIC_USER = 'admin'; // そのまま使わないで!
const BASIC_PASS = 'aBcDeFgHiJ'; // そのまま使わないで!
async function errorHandling(context) {
try {
return await context.next()
} catch (err) {
return new Response(`${err.message}\n${err.stack}`, { status: 500 })
}
}
async function handleRequest({ next, request }) {
// The "Authorization" header is sent when authenticated.
if (request.headers.has("Authorization")) {
const Authorization = request.headers.get('Authorization')
// Throws exception when authorization fails.
const [scheme, encoded] = Authorization.split(' ')
// The Authorization header must start with Basic, followed by a space.
if (!encoded || scheme !== 'Basic') {
return new Response(`The Authorization header must start with Basic`, {
status: 400,
})
}
// Decodes the base64 value and performs unicode normalization.
// @see https://datatracker.ietf.org/doc/html/rfc7613#section-3.3.2 (and #section-4.2.2)
// @see https://dev.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/normalize
const buffer = Uint8Array.from(atob(encoded), (character) =>
character.charCodeAt(0)
)
const decoded = new TextDecoder().decode(buffer).normalize()
// The username & password are split by the first colon.
//=> example: "username:password"
const index = decoded.indexOf(':')
// The user & password are split by the first colon and MUST NOT contain control characters.
// @see https://tools.ietf.org/html/rfc5234#appendix-B.1 (=> "CTL = %x00-1F / %x7F")
if (index === -1 || /[\0-\x1F\x7F]/.test(decoded)) {
return new Response('Invalid authorization value.', { status: 400 })
}
const user = decoded.substring(0, index);
const pass = decoded.substring(index + 1);
if (BASIC_USER !== user) {
return new Response('Invalid credentials.', { status: 401 })
}
if (BASIC_PASS !== pass) {
return new Response('Invalid credentials.', { status: 401 })
}
// Only returns this response when no exception is thrown.
return await next()
}
// Not authenticated.
return new Response('You need to login.', {
status: 401,
headers: {
// Prompts the user for credentials.
'WWW-Authenticate': 'Basic realm="my scope", charset="UTF-8"',
},
})
}
export const onRequest = [errorHandling, handleRequest]
参考:
https://www.to-r.net/media/cloudflare-pages-basic/
🚀 GitHub Actions → Cloudflare Pages のデプロイの流れ
- mainブランチで、db/schema.rb が変更されたら GitHub Actions が実行される。
- SchemaSpy でデータベーススキーマの HTML を生成。
- schemaspy-output/functions/_middleware.js をコピー(Basic 認証を追加)。
- GitHub Pages に push → Cloudflare Pages がビルドを検知し、自動でデプロイ。
Cloudflare Pageの設定方法
こちらのページを参考にさせていただきました。
https://qiita.com/RyoWakabayashi/items/2d40b6e8ae7ddee092db
ちなみ 対象ブランチに関しては、 action で、指定している、 gh-pages
を指定します。
- name: Deploy to GitHub Pages
uses: JamesIves/github-pages-deploy-action@v4
with:
branch: gh-pages
folder: schemaspy-output
clean: true
ちなみに、仮に今後Githubエンタープライズプランになれた時に、そのまま使えるので、 uses: JamesIves/github-pages-deploy-action@v4
を使わせていただいております。Github Page の設定で、対象ブランチに gh-pages
を設定しないでください。(一般公開されちゃいます)