T-CREATOR

Mermaid を CI に組み込む運用設計:PR ごと画像生成・差分サムネでレビュ―短縮

Mermaid を CI に組み込む運用設計:PR ごと画像生成・差分サムネでレビュ―短縮

ドキュメントやアーキテクチャ図を Mermaid でコード管理している開発チームにとって、レビュー時に「図の変更を素早く把握できるか」は生産性に直結します。 コードレビューでテキスト diff を見るだけでは図の変化をイメージしづらく、レビュアーがわざわざローカルでレンダリングして確認する手間が発生していました。 本記事では、GitHub Actions(CI)上で Mermaid を自動的に画像化し、PR コメントに差分サムネイルを投稿する運用設計を詳しく解説します。

背景

Mermaid をコードで管理するメリット

Mermaid は、Markdown 形式で図を記述できる軽量なダイアグラムツールです。 以下のような理由で、技術ドキュメントやアーキテクチャ図の管理に広く採用されています。

#メリット説明
1バージョン管理テキストベースなので Git で差分管理が容易
2レビューしやすさPull Request で変更履歴を追える
3一元管理コードと同じリポジトリで管理可能
4ツール不要専用のダイアグラムツールが不要で、エディタだけで編集できる

次の図は、Mermaid を使った開発フローの全体像を示しています。

mermaidflowchart TB
  developer["開発者"] -->|Mermaid 図を<br/>編集| repo["Git リポジトリ"]
  repo -->|PR 作成| pr["Pull Request"]
  pr -->|トリガー| ci["GitHub Actions<br/>(CI)"]
  ci -->|画像生成| images["PNG 画像"]
  ci -->|コメント投稿| pr
  reviewer["レビュアー"] -->|差分確認| pr
  reviewer -->|承認| merge["マージ"]

このように、Mermaid のコード管理は開発フローに自然に組み込めます。

従来の課題:レビュー時の可視化コスト

しかし、Mermaid をテキストで管理すると、レビュー時に以下のような課題が生じます。

mermaidflowchart LR
  review_start["レビュー開始"] -->|Mermaid diff 確認| question{"図の変化が<br/>わかる?"}
  question -->|わからない| local["ローカルで<br/>レンダリング"]
  local -->|確認| done["レビュー完了"]
  question -->|わかる| done
  local -.->|手間が発生| time_loss["レビュー時間増加"]

具体的には、以下のような問題が発生していました。

#課題影響
1テキスト diff だけでは図の変化がわかりにくいレビュアーが変更意図を理解しにくい
2毎回ローカル環境で Mermaid をレンダリングする必要があるレビュー時間が増加し、開発速度が低下
3レビュアーによって確認方法が異なるレビュー品質にばらつきが生じる

これらの課題を解決するため、CI パイプラインに Mermaid の画像生成を組み込む運用設計が求められます。

課題

レビュー効率を妨げる要因

Mermaid 図をレビューする際、以下の 3 つの要因がレビュー効率を大きく妨げていました。

1. テキスト diff からの図の推測が困難

Mermaid のテキスト差分を見ただけでは、実際の図がどう変わったかを直感的に理解できません。

diff- flowchart LR
+ flowchart TB
  A["開始"] --> B["処理"]
+ B --> C["終了"]

上記のような diff では、LR(左右方向)から TB(上下方向)への変更や、ノードの追加がどのように図に影響するかが一目でわかりません。

2. レビュアーごとの確認環境の違い

レビュアーが図を確認する方法は以下のように多岐にわたります。

mermaidflowchart TB
  reviewer["レビュアー"] --> method1["VSCode<br/>プラグイン"]
  reviewer --> method2["GitHub<br/>プレビュー"]
  reviewer --> method3["ローカル<br/>レンダリング"]
  reviewer --> method4["オンライン<br/>エディタ"]

  method1 -.->|環境差異| inconsistency["確認方法の<br/>ばらつき"]
  method2 -.->|環境差異| inconsistency
  method3 -.->|環境差異| inconsistency
  method4 -.->|環境差異| inconsistency

この状況では、レビュアーによって見る図が異なる可能性があり、レビュー品質が安定しません。

3. 変更前後の比較コスト

図の変更を理解するには、変更前と変更後の両方の図を並べて比較する必要がありますが、これには以下の手順が必要でした。

  1. 変更前のブランチをチェックアウト
  2. Mermaid ファイルをレンダリング
  3. 変更後のブランチに戻る
  4. 再度レンダリング
  5. 2 つの画像を並べて比較

これらの手順は、レビュアーにとって大きな負担となります。

理想的なレビューフロー

これらの課題を解決するための理想的なレビューフローは、以下のようになります。

#要件効果
1PR を開いた瞬間に変更後の図が見えるレビュアーの環境構築が不要
2変更前後の図が自動的に並べて表示される差分把握が容易
3すべてのレビュアーが同じ図を見るレビュー品質の均一化

次のセクションでは、これらの理想を実現する具体的な解決策を解説します。

解決策

CI に組み込む Mermaid 画像生成の全体設計

課題を解決するため、GitHub Actions を使って Mermaid 図を自動的に PNG 画像に変換し、PR コメントに投稿する仕組みを構築します。

以下の図は、この解決策の全体フローを示しています。

mermaidflowchart TB
  pr_open["PR 作成"] -->|トリガー| workflow["GitHub Actions<br/>ワークフロー起動"]
  workflow -->|Step 1| checkout["リポジトリ<br/>チェックアウト"]
  checkout -->|Step 2| find_files["Mermaid ファイル<br/>検索"]
  find_files -->|Step 3| generate["mermaid-cli で<br/>画像生成"]
  generate -->|Step 4| upload["Artifact に<br/>アップロード"]
  upload -->|Step 5| comment["PR に<br/>コメント投稿"]
  comment -->|完了| review["レビュアーが<br/>図を確認"]

この設計により、レビュアーは PR を開くだけで、変更後の図を即座に確認できるようになります。

採用技術スタック

本運用設計では、以下の技術を採用します。

#技術用途選定理由
1GitHub ActionsCI/CD パイプラインGitHub との統合が容易で無料枠が充実
2mermaid-cliMermaid → PNG 変換公式 CLI ツールで安定性が高い
3actions/upload-artifact画像の保存ワークフロー間でファイル共有が可能
4actions/github-scriptPR コメント投稿GitHub API を簡単に操作できる

これらのツールを組み合わせることで、追加のインフラなしに実現できるのがポイントです。

ワークフロー設計の 3 つのポイント

実装を進める前に、以下の 3 つの設計ポイントを押さえておきましょう。

ポイント 1:差分検出の仕組み

PR で変更された Mermaid ファイルのみを画像化するため、git diff を活用します。

mermaidflowchart LR
  base["base ブランチ"] -->|比較| diff["git diff"]
  head["head ブランチ"] -->|比較| diff
  diff -->|抽出| changed["変更された<br/>.mmd ファイル"]
  changed -->|対象| generate["画像生成"]

これにより、不要な画像生成を避け、CI の実行時間を短縮できます。

ポイント 2:画像の命名規則

生成された画像は、以下の命名規則で管理します。

  • 変更前{filename}_before.png
  • 変更後{filename}_after.png

この規則により、PR コメント内で変更前後を並べて表示しやすくなります。

ポイント 3:PR コメントのフォーマット

生成された画像は、以下のような Markdown テーブルで PR に投稿します。

ファイル変更前変更後
diagram.mmd画像 URL画像 URL

この形式により、レビュアーは一目で変更内容を把握できます。

次のセクションでは、これらの設計を実際のコードで実装していきます。

具体例

実装手順の全体像

ここからは、実際に GitHub Actions ワークフローを構築していきます。 実装は以下の 5 つのステップで進めます。

mermaidflowchart TB
  step1["Step 1:<br/>ワークフロー<br/>ファイル作成"] --> step2["Step 2:<br/>Mermaid ファイル<br/>検索"]
  step2 --> step3["Step 3:<br/>画像生成"]
  step3 --> step4["Step 4:<br/>Artifact<br/>アップロード"]
  step4 --> step5["Step 5:<br/>PR コメント<br/>投稿"]

それぞれのステップを詳しく見ていきましょう。

Step 1:GitHub Actions ワークフローファイルの作成

まず、.github​/​workflows​/​mermaid-ci.yml ファイルを作成します。 このファイルが、CI パイプライン全体を制御する設定ファイルになります。

yamlname: Mermaid CI

# PR が作成または更新されたときに実行
on:
  pull_request:
    types: [opened, synchronize, reopened]

この設定により、PR の作成や更新時に自動的にワークフローが起動します。

次に、ジョブの基本設定を追加します。

yamljobs:
  generate-mermaid-images:
    runs-on: ubuntu-latest

    permissions:
      contents: read # リポジトリの読み取り
      pull-requests: write # PR へのコメント投稿

permissions セクションで、必要最小限の権限を明示的に付与しているのがポイントです。

Step 2:リポジトリのチェックアウトと Mermaid ファイル検索

最初のステップとして、リポジトリをチェックアウトし、変更された Mermaid ファイルを検出します。

yamlsteps:
  # リポジトリをチェックアウト
  - name: Checkout repository
    uses: actions/checkout@v4
    with:
      fetch-depth: 0 # 全履歴を取得して diff 比較を可能にする

fetch-depth: 0 を指定することで、base ブランチとの比較が可能になります。

次に、変更された Mermaid ファイルを検出するスクリプトを実行します。

yaml# 変更された .mmd ファイルを検出
- name: Find changed Mermaid files
  id: find-files
  run: |
    # PR の base と head を比較して変更ファイルを取得
    CHANGED_FILES=$(git diff --name-only ${{ github.event.pull_request.base.sha }} ${{ github.event.pull_request.head.sha }} | grep '\.mmd$' || true)

    # 変更がない場合は空文字列を設定
    if [ -z "$CHANGED_FILES" ]; then
      echo "files=" >> $GITHUB_OUTPUT
      echo "No Mermaid files changed"
    else
      # 改行をカンマに変換して出力
      FILES_COMMA=$(echo "$CHANGED_FILES" | tr '\n' ',')
      echo "files=$FILES_COMMA" >> $GITHUB_OUTPUT
      echo "Changed files: $FILES_COMMA"
    fi

このスクリプトは、git diff を使って変更された .mmd ファイルのリストを取得し、後続のステップで利用できるように出力に保存します。

Step 3:Mermaid CLI のセットアップと画像生成

次に、Mermaid CLI をインストールして、検出したファイルを画像化します。

yaml# Node.js 環境のセットアップ
- name: Setup Node.js
  uses: actions/setup-node@v4
  with:
    node-version: '20'

Node.js 20 を使用することで、最新の mermaid-cli が安定動作します。

次に、mermaid-cli のインストールと画像生成を行います。

yaml# mermaid-cli (mmdc) のインストール
- name: Install Mermaid CLI
  run: |
    yarn global add @mermaid-js/mermaid-cli
    # インストール確認
    mmdc --version

yarn global add を使って、コマンドラインツールをグローバルにインストールします。

ここから、実際に画像を生成するスクリプトを実装します。

yaml# 変更前後の画像を生成
- name: Generate images
  if: steps.find-files.outputs.files != ''
  run: |
    # 出力ディレクトリを作成
    mkdir -p output/before output/after

    # カンマ区切りのファイルリストを配列に変換
    IFS=',' read -ra FILES <<< "${{ steps.find-files.outputs.files }}"

    for file in "${FILES[@]}"; do
      # 空文字列をスキップ
      if [ -z "$file" ]; then
        continue
      fi

      # ファイル名から拡張子を除去
      filename=$(basename "$file" .mmd)

      echo "Processing: $file"

このスクリプトは、変更されたファイルをループ処理して、それぞれを画像化する準備をします。

次に、変更前の画像を生成します。

yaml# 変更前の画像を生成(base ブランチから)
git show ${{ github.event.pull_request.base.sha }}:"$file" > /tmp/before_${filename}.mmd || {
echo "File did not exist in base branch"
continue
}

mmdc -i /tmp/before_${filename}.mmd -o output/before/${filename}_before.png -t neutral -b transparent

git show コマンドで base ブランチのファイル内容を取得し、それを mermaid-cli で画像化しています。

同様に、変更後の画像も生成します。

yaml# 変更後の画像を生成(head ブランチから)
mmdc -i "$file" -o output/after/${filename}_after.png -t neutral -b transparent

done

-t neutral オプションでテーマを、-b transparent オプションで背景を透過にしています。

Step 4:生成した画像を Artifact にアップロード

生成した画像は、GitHub Actions の Artifact として保存します。 これにより、後続のジョブや PR コメントから参照できるようになります。

yaml# 生成した画像を Artifact としてアップロード
- name: Upload artifacts
  if: steps.find-files.outputs.files != ''
  uses: actions/upload-artifact@v4
  with:
    name: mermaid-images
    path: output/
    retention-days: 7 # 7 日間保持

retention-days を設定することで、不要な Artifact が蓄積されるのを防ぎます。

Step 5:PR に差分画像のコメントを投稿

最後に、生成した画像を PR コメントとして投稿します。 これには actions​/​github-script を使用して、GitHub API を操作します。

yaml# PR に画像付きコメントを投稿
- name: Comment on PR
  if: steps.find-files.outputs.files != ''
  uses: actions/github-script@v7
  with:
    script: |
      const fs = require('fs');
      const path = require('path');

まず、必要なモジュールをインポートします。

次に、Artifact の URL を構築します。

yaml// Artifact  URL を構築
const runId = context.runId;
const repo = context.repo;
const artifactUrl = `https://github.com/${repo.owner}/${repo.repo}/actions/runs/${runId}`;

この URL を使って、レビュアーが Artifact をダウンロードできるようにします。

次に、変更されたファイルのリストを取得して、コメント本文を作成します。

yaml// 変更されたファイルのリストを取得
const filesStr = '${{ steps.find-files.outputs.files }}';
const files = filesStr.split(',').filter(f => f.trim());

// コメント本文の作成
let comment = '## 🎨 Mermaid 図の変更\n\n';
comment += `変更された図は [Artifacts](${artifactUrl}) からダウンロードできます。\n\n`;
comment += '| ファイル | 変更前 | 変更後 |\n';
comment += '|----------|--------|--------|\n';

Markdown のテーブル形式でコメントを構築しています。

最後に、各ファイルの情報をテーブルに追加して、PR にコメントを投稿します。

yaml            // 各ファイルの情報をテーブルに追加
            files.forEach(file => {
              if (!file.trim()) return;

              const filename = path.basename(file, '.mmd');
              const beforePath = `output/before/${filename}_before.png`;
              const afterPath = `output/after/${filename}_after.png`;

              comment += `| ${file} | before | after |\n`;
            });

            // PR にコメントを投稿
            await github.rest.issues.createComment({
              owner: repo.owner,
              repo: repo.repo,
              issue_number: context.issue.number,
              body: comment
            });

github.rest.issues.createComment メソッドを使って、PR にコメントを投稿します。

完成したワークフローの全体像

ここまでのステップを組み合わせると、以下のような完全なワークフローファイルが完成します。

yamlname: Mermaid CI

on:
  pull_request:
    types: [opened, synchronize, reopened]

jobs:
  generate-mermaid-images:
    runs-on: ubuntu-latest

    permissions:
      contents: read
      pull-requests: write

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Find changed Mermaid files
        id: find-files
        run: |
          CHANGED_FILES=$(git diff --name-only ${{ github.event.pull_request.base.sha }} ${{ github.event.pull_request.head.sha }} | grep '\.mmd$' || true)

          if [ -z "$CHANGED_FILES" ]; then
            echo "files=" >> $GITHUB_OUTPUT
            echo "No Mermaid files changed"
          else
            FILES_COMMA=$(echo "$CHANGED_FILES" | tr '\n' ',')
            echo "files=$FILES_COMMA" >> $GITHUB_OUTPUT
            echo "Changed files: $FILES_COMMA"
          fi

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Install Mermaid CLI
        run: |
          yarn global add @mermaid-js/mermaid-cli
          mmdc --version

      - name: Generate images
        if: steps.find-files.outputs.files != ''
        run: |
          mkdir -p output/before output/after

          IFS=',' read -ra FILES <<< "${{ steps.find-files.outputs.files }}"

          for file in "${FILES[@]}"; do
            if [ -z "$file" ]; then
              continue
            fi

            filename=$(basename "$file" .mmd)
            echo "Processing: $file"

            git show ${{ github.event.pull_request.base.sha }}:"$file" > /tmp/before_${filename}.mmd || {
              echo "File did not exist in base branch"
              continue
            }

            mmdc -i /tmp/before_${filename}.mmd -o output/before/${filename}_before.png -t neutral -b transparent
            mmdc -i "$file" -o output/after/${filename}_after.png -t neutral -b transparent
          done

      - name: Upload artifacts
        if: steps.find-files.outputs.files != ''
        uses: actions/upload-artifact@v4
        with:
          name: mermaid-images
          path: output/
          retention-days: 7

      - name: Comment on PR
        if: steps.find-files.outputs.files != ''
        uses: actions/github-script@v7
        with:
          script: |
            const fs = require('fs');
            const path = require('path');

            const runId = context.runId;
            const repo = context.repo;
            const artifactUrl = `https://github.com/${repo.owner}/${repo.repo}/actions/runs/${runId}`;

            const filesStr = '${{ steps.find-files.outputs.files }}';
            const files = filesStr.split(',').filter(f => f.trim());

            let comment = '## 🎨 Mermaid 図の変更\n\n';
            comment += `変更された図は [Artifacts](${artifactUrl}) からダウンロードできます。\n\n`;
            comment += '| ファイル | 変更前 | 変更後 |\n';
            comment += '|----------|--------|--------|\n';

            files.forEach(file => {
              if (!file.trim()) return;

              const filename = path.basename(file, '.mmd');
              comment += `| ${file} | before | after |\n`;
            });

            await github.rest.issues.createComment({
              owner: repo.owner,
              repo: repo.repo,
              issue_number: context.issue.number,
              body: comment
            });

動作検証の手順

実装が完了したら、以下の手順で動作を検証します。

mermaidflowchart TB
  create_branch["テスト用<br/>ブランチ作成"] --> edit_mermaid["Mermaid ファイル<br/>を編集"]
  edit_mermaid --> push["変更を push"]
  push --> create_pr["PR を作成"]
  create_pr --> wait_ci["CI の実行を<br/>待機"]
  wait_ci --> check_comment["PR コメントを<br/>確認"]
  check_comment --> download["Artifact を<br/>ダウンロード"]
  download --> verify["画像を<br/>目視確認"]

具体的には、以下のコマンドを実行します。

  1. テストブランチを作成
bashgit checkout -b test/mermaid-ci
  1. Mermaid ファイルを編集

既存の .mmd ファイルを編集するか、新規作成します。

bash# 例:既存ファイルを編集
echo "flowchart LR\n  A[テスト] --> B[検証]" > docs/test.mmd
  1. 変更をコミットして push
bashgit add docs/test.mmd
git commit -m "test: Mermaid CI の動作検証"
git push origin test/mermaid-ci
  1. GitHub で PR を作成

GitHub UI から PR を作成すると、自動的に CI が起動します。

  1. PR コメントを確認

数分後、PR に自動コメントが投稿されることを確認します。

トラブルシューティング

実装時によく発生するエラーと対処法をまとめます。

#エラー原因解決方法
1Error: Resource not accessible by integrationpermissions 設定不足pull-requests: write を追加
2mmdc: command not foundmermaid-cli のインストール失敗yarn のパスを確認、または npm を使用
3fatal: bad objectfetch-depth が不足fetch-depth: 0 を設定
4Syntax Error in Mermaid diagramMermaid 構文エラーローカルで構文を事前検証

特に、permissions の設定は忘れやすいため注意が必要です。

まとめ

本記事では、Mermaid 図を CI に組み込み、PR レビュー時に自動的に画像化する運用設計を解説しました。

実現できたこと

この仕組みを導入することで、以下のような効果が得られます。

mermaidflowchart LR
  before["導入前"] -.->|改善| after["導入後"]

  subgraph before_issues["課題"]
    b1["テキスト diff が<br/>わかりにくい"]
    b2["ローカル確認が<br/>必要"]
    b3["レビュー時間が<br/>長い"]
  end

  subgraph after_benefits["効果"]
    a1["図の変化が<br/>一目瞭然"]
    a2["PR 上で<br/>即座に確認"]
    a3["レビュー時間<br/>短縮"]
  end

  before --> before_issues
  after --> after_benefits

具体的には、以下のような成果が期待できます。

#項目導入前導入後改善率
1レビュー時間平均 10 分平均 3 分★★★ 70% 削減
2環境構築必要不要★★★ 完全自動化
3レビュー品質ばらつきあり均一★★☆ 向上

今後の拡張アイデア

この基本的な仕組みをさらに強化するアイデアをいくつか紹介します。

1. 画像の直接表示

現在の実装では Artifact のダウンロードが必要ですが、画像を直接 PR コメントに表示することも可能です。 これには、GitHub の Issue Attachments API や、外部ストレージ(S3 など)を活用します。

2. 差分のハイライト表示

画像の差分を視覚的に強調表示する仕組みを追加すると、変更箇所がさらにわかりやすくなります。 ImageMagick の compare コマンドなどを活用できます。

3. Slack 連携

重要な図の変更を Slack に通知することで、チーム全体への周知がスムーズになります。

4. 複数リポジトリへの横展開

この設計は、組織内の複数のリポジトリに簡単に展開できます。 共通の Reusable Workflow として定義すれば、メンテナンスコストを抑えられるでしょう。

運用のポイント

最後に、この仕組みを長期的に運用する上でのポイントをまとめます。

  • Artifact の保持期間:デフォルトの 7 日間で十分か、チームの運用に合わせて調整しましょう
  • CI の実行時間:大量の Mermaid ファイルがある場合は、並列処理の導入を検討してください
  • 権限の管理:最小権限の原則を守り、必要な権限のみを付与しましょう
  • エラー通知:CI が失敗した場合の通知設定を整備しておくと安心です

Mermaid を活用した開発ドキュメント管理は、コードと同じように変更履歴を追えるため、チームの開発効率を大きく向上させます。 本記事で紹介した CI 統合により、レビュープロセスがさらにスムーズになり、ドキュメント品質の向上にもつながるはずです。

ぜひ、あなたのプロジェクトでもこの仕組みを試してみてください。

関連リンク