Let's write β

プログラミング中にできたことか、思ったこととか

GitHub ActionsでDBスキーマ情報をSchemaSpyでとりだしてSlackで社内共有した

弊社では、データの分析をメンバーがしやすくするにあたって、Re:dashを導入しています。

データへのアクセスのしやすさ、SQLの実行のしやすさはRe:dashで良いとして、 実際にどんなデータがどんなテーブルにはいっているのかを知らなければ分析方法をまちがってしまったり、 分析をするためにコミュニケーションが煩雑になりスケールしづらく、分析のハードルがあがってしまいます。

一方で、データベースのスキーマはプロダクトを日々開発をしていく中で追加になったり変更になったりする事がおおく、 変更にドキュメントを追従させていくコストをなるべく減らしていく事も必要です。

また、結果を社内のメンバーしかアクセスできないようにするために 社内サーバーを立てて、デプロイプロセスを設計して等のコストを書けるには、サービス初期という事もありまだ早いとかんがえました。

そこで、今回はRailsアプリケーションのDBスキーマ変更をGitHub Actionsで検知してSchemaSpyをつかって文書化し、 Slackのチャンネルに結果をアップロードするようにしてみました。

Schema Spy

データベースのドキュメント生成として、けっこう有名なツールになるかとおもいますが

github.com

DBに接続して、スキーマやテーブルの関連を調査してHTMLドキュメントを生成してくれるツールです。 JarやDockerコンテナとして配布されているので、CI上でも比較的動かしやすいツールとなっています。

dev.classmethod.jp

メタ情報

Schema SpyをそのままつかうとDBのテーブルのリスト等を集めてくれるのですが、 この段階では DBのテーブルやカラムに Comment が付いていなければ、ただテーブルの一覧やカラムの一覧、関連のER図を生成してくれるのみです。 それだけでも非常に有用なのですが、またenum系のカラムの数値が何を意味しているのか等の情報はわかりません。

また、このDBにコメントをつけていくという作業、 Railsのmigrationでも可能なのですがmigrationを大量に発行して全てのカラムにコメントをつけるよりも、 SchemaSpyにXMLで投入する事で追加して読みこんでもらえるため、そちらのプランでいこうとおもいました。

そんな時に、このデータをYAMLからXMLを生成してしまうという手法をみかけ、そちらを採用させていただきました!

gift-tech.co.jp

Github Actions

今回はRailsのアプリケーションでDBのスキーマを管理している db/schema.rb の変更を検知して変更がmasterブランチに発生したら、 SchemaSpyを実行して、結果をZipでまとめてSlackに投稿するようにしました。 以下Github Actionsのコードの抜粋です。

name: upload-schemaspy

on:
  push:
    branches: [master]
    paths:
      - "db/schema.rb"
      - "db/schemaspy_meta.yml"
      - "lib/tasks/schemaspy_meta_xml.rake"

jobs:
  schemaspy:
    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
        env:
          MYSQL_USER: root
          MYSQL_ROOT_PASSWORD: XXXXXXXXXXXXXX
        ports:
          - 3306
    container:
      image: ruby:2.6.6
      env:
        RAILS_ENV: test
        RAILS_MASTER_KEY: XXXXXXXXXXXXXXXXXXXXXXXXXXXX

    steps:
      - uses: actions/checkout@v2
      - name: Bundler Cache
        uses: actions/cache@v1
        id: cache-bundler
        with:
          path: vendor/bundle
          key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }}
          restore-keys: |
            ${{ runner.os }}-gems-

      - name: Setup Bundler
        run: |
          gem install bundler
          bundle config set path 'vendor/bundle'

      - name: Bundle install
        if: steps.cache-bundler.outputs.cache-hit != 'true'
        run: bundle install --jobs=4 --retry=3

      - name: Setup database
        run: |
          cp config/database.yml.ci config/database.yml
          bundle exec rails db:prepare

      - uses: actions/setup-java@v1
        with:
          java-version: '9.0.4' # The JDK version to make available on the path.
          java-package: jdk # (jre, jdk, or jdk+fx) - defaults to jdk
          architecture: x64

      - name: schemaSpy
        run: |
          wget https://github.com/schemaspy/schemaspy/releases/download/v6.1.0/schemaspy-6.1.0.jar -O schemaspy.jar
          wget https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-5.1.48.zip -O mysql-connector-java-5.1.48.zip
          unzip mysql-connector-java-5.1.48.zip
          bundle exec rails schemaspy_meta_xml:generate
          java -jar schemaspy.jar -configFile ./.github/schemaspy.properties -vizjs -meta ./db/schemaspy_meta.xml -norows
          mkdir -p artifact/schemaspy
          mv output/* artifact/schemaspy/.

      - uses: montudor/action-zip@v0.1.1
        with:
          args: zip -qq -r artifact/schema-information.zip artifact/schemaspy

      - name: Upload to slack
        uses: adrey/slack-file-upload-action@master
        with:
          token: ${{ secrets.SLACK_TOKEN }}
          path: artifact/schema-information.zip
          channel: XXXXX

SchemaSpyはJarでダウンロードして実行するようにしており、

Javaのセットアップ、Zipによる圧縮、Slackへの投稿に以下のActionを利用させていただいています。

github.com

github.com

github.com

実行結果

SlackのAppを作成して、TokenをGithub ActionsのScretsに設定して、データスキーマ共有のチャンネルにJoinさせ、実行してみると無事投稿されてきます。

f:id:Pocket7878_dev:20210118183622p:plain