Skip to content

chore: 上流タスクとPRの構造化テンプレートを導入 #4

chore: 上流タスクとPRの構造化テンプレートを導入

chore: 上流タスクとPRの構造化テンプレートを導入 #4

Workflow file for this run

name: Mermaid図チェック(reviewdog)
on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
paths:
- "docs/**/*.md"
- ".github/**/*.md"
permissions:
contents: read
concurrency:
group: mermaid-lint-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
mermaid:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: "24"
- name: Log Mermaid CLIインストール開始
run: echo "Mermaid CLIをインストールします" >&2
- name: Install Mermaid CLI
run: |
set -e
npm install -g @mermaid-js/mermaid-cli@11.12.0
- name: Log Mermaid CLIインストール完了
run: echo "Mermaid CLIのインストールが完了しました" >&2
- name: Log mmdcコマンド確認開始
run: echo "mmdcコマンドの存在を確認します" >&2
- name: Check mmdc availability
run: |
if ! command -v mmdc >/dev/null 2>&1; then
echo "mmdcコマンドが見つからないためジョブを失敗扱いにします" >&2
exit 1
fi
- name: Log goコマンド確認開始
run: echo "goコマンドの存在を確認します" >&2
- name: Check Go availability
run: |
if ! command -v go >/dev/null 2>&1; then
echo "goコマンドが見つからないためジョブを失敗扱いにします" >&2
exit 1
fi
- name: Install reviewdog
run: |
go install github.com/reviewdog/reviewdog/cmd/reviewdog@v0.21.0
echo "$HOME/go/bin" >> "$GITHUB_PATH"
- name: Log Mermaidブロック抽出開始
run: echo "Mermaidブロック抽出を開始します" >&2
- name: Extract Mermaid blocks
run: |
mkdir -p tmp
node <<'EOF'
const fs = require('fs')
const path = require('path')
// Mermaid抽出ファイルと元Markdownの対応を「抽出ファイル:元ファイル:開始行」形式で記録
// 例: tmp/diagram-1.mmd:docs/example.md:42
const mapPath = path.join('tmp', 'map.txt')
fs.writeFileSync(mapPath, '')
// Mermaidブロックの件数
let index = 0
// 指定ディレクトリ以下のMarkdownを探索
function walk(dir) {
if (!fs.existsSync(dir)) return
fs.readdirSync(dir).forEach((entry) => {
const full = path.join(dir, entry)
const stat = fs.statSync(full)
if (stat.isDirectory()) {
walk(full)
return
}
if (full.endsWith('.md')) {
processFile(full)
}
})
}
function processFile(file) {
const lines = fs.readFileSync(file, 'utf8').split('\n')
let inside = false
let buffer = []
let startLine = 0
lines.forEach((line, idx) => {
const trimmed = line.trim()
// Mermaidブロックの開始
if (!inside && trimmed.startsWith('```mermaid')) {
inside = true
buffer = []
startLine = idx + 1
return
}
// Mermaidブロックの終了
if (inside && trimmed.startsWith('```')) {
inside = false
const name = `tmp/diagram-${++index}.mmd`
fs.writeFileSync(name, buffer.join('\n'))
fs.appendFileSync(mapPath, `${name}:${file}:${startLine}\n`)
return
}
if (inside) buffer.push(line)
})
// ファイル末尾まで到達しても終了フェンスが無い場合(閉じ忘れ)もブロックを検証対象に含める
if (inside) {
const name = `tmp/diagram-${++index}.mmd`
fs.writeFileSync(name, buffer.join('\n'))
fs.appendFileSync(mapPath, `${name}:${file}:${startLine}\n`)
}
}
walk('docs')
walk('.github')
console.error(`抽出したMermaid図: ${index}件`)
EOF
- name: Log Mermaidブロック抽出完了
run: echo "Mermaidブロック抽出が完了しました" >&2
- name: Log Mermaid図検証開始
run: echo "Mermaid図の検証を開始します" >&2
- name: Validate Mermaid diagrams
run: |
: > tmp/result.txt
MAX_ERROR_LENGTH=500
# Mermaid CLIの実行環境起因エラー(Puppeteer/ブラウザ起動失敗等)を判定するための正規表現パターン
# 各要素は正規表現として評価するため、必要に応じてメタ文字を使用/エスケープする
# 出力メッセージの大文字小文字揺れを吸収するため、grepは -i を使用する
MMD_CLI_EXEC_ERROR_PATTERNS=(
# Puppeteer関連のエラー文言(例: "Error: Puppeteer failed to connect")
'error.*puppeteer'
# 実行ファイル未検出(例: "Executable doesn't exist at /path/to/chrome")
"executable doesn't exist"
# browser executable.*exist は実行ファイル探索時の存在エラー検知を意図(例: "browser executable doesn't exist")
'browser executable.*exist'
# 起動失敗の共通文言(例: "Failed to launch the browser process!")
'failed to launch'
# browsertype.launch のドットはリテラルとして扱う(例: "browserType.launch: Executable doesn't exist ...")
'browsertype\.launch'
)
# 配列要素を | で連結して正規表現文字列を生成(例: error.*puppeteer|...)
MMD_CLI_EXEC_ERROR_REGEX=$(IFS='|'; printf '%s' "${MMD_CLI_EXEC_ERROR_PATTERNS[*]}")
cat <<'JSON' > tmp/puppeteer-config.json
{
"args": ["--no-sandbox", "--disable-setuid-sandbox"]
}
JSON
if [ -s tmp/map.txt ]; then
mmdc_exec_error=0
mmdc_exec_summary=""
while IFS=":" read -r mmd src line; do
if [ -z "${mmd}" ]; then
continue
fi
if ! output=$(mmdc -p tmp/puppeteer-config.json -i "${mmd}" -o "${mmd}.svg" 2>&1); then
summary=$(printf "%s" "${output}" | tr '\n' ' ' | cut -c1-${MAX_ERROR_LENGTH})
if printf "%s" "${output}" | grep -qiE "${MMD_CLI_EXEC_ERROR_REGEX}"; then
mmdc_exec_error=1
mmdc_exec_summary="${summary}"
echo "mmdcの実行に失敗しました: ${summary}" >&2
# 実行環境エラーは全図に影響するため、以降の検証を中断してジョブを失敗扱いにする
break
fi
echo "${src}:${line}: Mermaid図の構文検証に失敗しました: ${summary}" >> tmp/result.txt
fi
done < tmp/map.txt
if [ "${mmdc_exec_error}" -ne 0 ]; then
echo "Mermaid CLIの実行エラーのためジョブを失敗扱いにします: ${mmdc_exec_summary}" >&2
exit 1
fi
else
echo "Mermaidブロックが見つからなかったため検証をスキップします" >&2
fi
- name: Log Mermaid図検証完了
run: echo "Mermaid図の検証が完了しました" >&2
- name: Log reviewdog通知開始
run: echo "reviewdogで結果を通知します" >&2
- name: Report via reviewdog
env:
REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
reviewdog < tmp/result.txt \
-efm="%f:%l: %m" \
-name="Mermaid図チェック" \
-reporter=github-pr-review \
-level=error
- name: Log reviewdog通知完了
run: echo "reviewdogの通知処理が完了しました" >&2