MR品質ゲート設計
Issue #7: MRレビューワークフロー・報告資料テンプレート設計
1. 概要
MRの品質を自動的に検証し、基準を満たさないMRのマージを防止する仕組みを設計する。
品質ゲートの目的:
- レビュワーの負担軽減(基本的な品質は自動で担保)
- MRテンプレートの記入漏れ防止
- コード品質の一定水準維持
- マージ条件の自動制御
2. GitLab CI パイプライン構成
2.1 パイプライン全体図
┌──────────────────────────────────────────────────────────────────┐
│ MR パイプライン │
├──────────┬──────────┬──────────┬──────────┬──────────────────────┤
│ Stage 1 │ Stage 2 │ Stage 3 │ Stage 4 │ Stage 5 │
│ validate │ lint │ test │ build │ report │
├──────────┼──────────┼──────────┼──────────┼──────────────────────┤
│ template │ eslint │ unit │ build │ coverage-report │
│ -check │ prettier │ integr. │ check │ quality-summary │
│ │ stylelint│ e2e(opt) │ │ │
│ │ secrets │ │ │ │
└──────────┴──────────┴──────────┴──────────┴──────────────────────┘2.2 .gitlab-ci.yml 構成
yaml
stages:
- validate
- lint
- test
- build
- report
# ========================================
# Stage 1: validate - テンプレート準拠チェック
# ========================================
template-check:
stage: validate
image: alpine:latest
script:
- apk add --no-cache bash jq curl
- chmod +x scripts/check-mr-template.sh
- bash scripts/check-mr-template.sh
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
allow_failure: false
# ========================================
# Stage 2: lint - コード品質チェック
# ========================================
eslint:
stage: lint
image: node:20-alpine
script:
- npm ci
- npx eslint . --format gitlab
artifacts:
reports:
codequality: gl-codequality.json
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
allow_failure: false
prettier:
stage: lint
image: node:20-alpine
script:
- npm ci
- npx prettier --check .
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
allow_failure: false
secret-detection:
stage: lint
image: alpine:latest
script:
- apk add --no-cache git
- git log --all --diff-filter=A --name-only --pretty=format: | sort -u > new_files.txt
- chmod +x scripts/check-secrets.sh
- bash scripts/check-secrets.sh
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
allow_failure: false
# ========================================
# Stage 3: test - テスト実行
# ========================================
unit-test:
stage: test
image: node:20-alpine
script:
- npm ci
- npm run test -- --coverage
artifacts:
reports:
junit: junit.xml
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml
coverage: '/All files\s*\|\s*([\d.]+)/'
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
allow_failure: false
# ========================================
# Stage 4: build - ビルド確認
# ========================================
build-check:
stage: build
image: node:20-alpine
script:
- npm ci
- npm run build
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
allow_failure: false
# ========================================
# Stage 5: report - レポート生成
# ========================================
quality-summary:
stage: report
image: alpine:latest
script:
- echo "All quality gates passed"
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
when: on_success3. テンプレート必須項目の自動検証スクリプト
3.1 スクリプト設計: scripts/check-mr-template.sh
bash
#!/bin/bash
set -euo pipefail
# ============================================
# MRテンプレート必須項目チェックスクリプト
# ============================================
# GitLab CI から呼び出され、MR説明欄の
# テンプレート記入状況を検証する。
# ============================================
GITLAB_API_URL="${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/merge_requests/${CI_MERGE_REQUEST_IID}"
MR_DESCRIPTION=$(curl -s --header "PRIVATE-TOKEN: ${GITLAB_API_TOKEN}" "${GITLAB_API_URL}" | jq -r '.description // ""')
ERRORS=()
WARNINGS=()
# --------------------------------------------
# 必須項目チェック関数
# --------------------------------------------
check_section() {
local section_name="$1"
local pattern="$2"
local is_required="${3:-true}"
if ! echo "${MR_DESCRIPTION}" | grep -qP "${pattern}"; then
if [ "${is_required}" = "true" ]; then
ERRORS+=("ERROR: 必須項目「${section_name}」が未記入です")
else
WARNINGS+=("WARNING: 推奨項目「${section_name}」が未記入です")
fi
fi
}
# 対応Issueセクションにプレースホルダーでなく実際のIssue番号が入っているか
check_issue_link() {
if ! echo "${MR_DESCRIPTION}" | grep -qP 'Closes #\d+'; then
ERRORS+=("ERROR: 「対応Issue」にIssue番号が記載されていません(Closes #<番号> の形式で記載してください)")
fi
}
# --------------------------------------------
# 各必須項目のチェック実行
# --------------------------------------------
echo "=============================="
echo "MRテンプレート準拠チェック開始"
echo "=============================="
# 1. 対応Issue
check_issue_link
# 2. 変更の目的(プレースホルダーのコメントだけでないこと)
check_section "変更の目的" '## 実装概要[\s\S]*### 変更の目的\s*\n(?!<!--).+'
# 3. 変更内容(箇条書きが最低1項目あること)
check_section "変更内容" '### 変更内容[\s\S]*?- [^\s]'
# 4. 動作確認結果(正常系に最低1行のデータがあること)
check_section "動作確認結果(正常系)" '### 正常系の確認[\s\S]*?\| .+\| .+\| .+\| .+\| (OK|NG)'
# 5. テスト結果(チェックボックスが最低1つチェックされていること)
check_section "テスト結果" '\[x\].*テスト'
# 6. セルフチェックリスト(コーディング規約セクションのチェック)
check_section "セルフチェックリスト" '### コーディング規約[\s\S]*?\[x\]'
# 7. AI利用について(使用有無の明記)
check_section "AI利用について" '## AI利用について[\s\S]*?\[x\]'
# --------------------------------------------
# 結果出力
# --------------------------------------------
echo ""
echo "--- チェック結果 ---"
if [ ${#WARNINGS[@]} -gt 0 ]; then
for warning in "${WARNINGS[@]}"; do
echo "${warning}"
done
fi
if [ ${#ERRORS[@]} -gt 0 ]; then
echo ""
for error in "${ERRORS[@]}"; do
echo "${error}"
done
echo ""
echo "=============================="
echo "チェック結果: NG (${#ERRORS[@]} 件のエラー)"
echo "=============================="
echo ""
echo "MRテンプレートの必須項目を記入してください。"
echo "テンプレート: docs/workflows/mr-template.md を参照"
exit 1
else
echo ""
echo "=============================="
echo "チェック結果: OK"
echo "=============================="
exit 0
fi3.2 秘密情報検出スクリプト: scripts/check-secrets.sh
bash
#!/bin/bash
set -euo pipefail
# ============================================
# 秘密情報漏洩チェックスクリプト
# ============================================
ERRORS=()
# 秘密情報パターン
PATTERNS=(
'PRIVATE.?KEY'
'SECRET.?KEY'
'API.?KEY\s*=\s*["\x27][a-zA-Z0-9]'
'TOKEN\s*=\s*["\x27][a-zA-Z0-9]'
'PASSWORD\s*=\s*["\x27][a-zA-Z0-9]'
'aws_access_key_id\s*='
'aws_secret_access_key\s*='
)
# .env ファイルがコミットされていないか
if git diff --name-only origin/${CI_MERGE_REQUEST_TARGET_BRANCH_NAME}...HEAD | grep -qE '\.env($|\.)'; then
ERRORS+=("ERROR: .env ファイルがコミットに含まれています")
fi
# 差分ファイルに対して秘密情報パターンを検索
CHANGED_FILES=$(git diff --name-only origin/${CI_MERGE_REQUEST_TARGET_BRANCH_NAME}...HEAD || true)
for file in ${CHANGED_FILES}; do
if [ -f "${file}" ]; then
for pattern in "${PATTERNS[@]}"; do
if grep -qPi "${pattern}" "${file}" 2>/dev/null; then
ERRORS+=("WARNING: ${file} に秘密情報の可能性があるパターンが検出されました (${pattern})")
fi
done
fi
done
if [ ${#ERRORS[@]} -gt 0 ]; then
echo "=============================="
echo "秘密情報チェック結果: NG"
echo "=============================="
for error in "${ERRORS[@]}"; do
echo "${error}"
done
exit 1
else
echo "=============================="
echo "秘密情報チェック結果: OK"
echo "=============================="
exit 0
fi4. マージ条件
4.1 必須条件(全て満たすこと)
| # | 条件 | 制御方法 |
|---|---|---|
| 1 | CI パイプライン全ステージ通過 | GitLab: Pipelines must succeed |
| 2 | レビュワー1名以上の Approve | GitLab: Required approvals: 1 |
| 3 | 未解決の Discussion なし | GitLab: All discussions must be resolved |
| 4 | コンフリクトなし | GitLab: 自動チェック |
| 5 | MR作成者による自己承認なし | GitLab: Prevent approval by author |
4.2 GitLab プロジェクト設定
Settings > Merge requests > Merge request approvals:
- Required approvals: 1
- Eligible approvers: プロジェクトメンバー(Developer以上)
- [x] Prevent approval by author
- [x] Remove all approvals when commits are added
Settings > Merge requests > Merge checks:
- [x] Pipelines must succeed
- [x] All discussions must be resolved
- [ ] Skipped pipelines are considered successful (OFF)
Settings > Repository > Protected branches (main/develop):
- Allowed to merge: Maintainers
- Allowed to push: No one (MR経由のみ)5. 品質ゲート運用ルール
5.1 ゲート通過基準
| ゲート | 基準 | 失敗時の対応 |
|---|---|---|
| テンプレートチェック | 必須7項目が全て記入済み | 作成者がテンプレートを修正して再プッシュ |
| lint | エラー 0件 | 作成者がコードを修正して再プッシュ |
| テスト | 全テストパス | 作成者がテスト失敗を修正して再プッシュ |
| ビルド | ビルド成功 | 作成者がビルドエラーを修正して再プッシュ |
| 秘密情報検出 | 検出 0件 | 作成者が秘密情報を除去して再プッシュ |
5.2 例外対応
CI パイプラインが技術的な理由(インフラ障害等)で失敗する場合:
- Maintainer が原因を確認
- コード起因でない場合はパイプラインを再実行
- 再実行でも解消しない場合は PM 判断でマージ可否を決定
6. CI/CD 変数設定
パイプラインで使用する変数を GitLab CI/CD Variables に設定する。
| 変数名 | 用途 | マスク | 保護 |
|---|---|---|---|
GITLAB_API_TOKEN | MR説明欄取得用APIトークン | Yes | Yes |
設定場所: Settings > CI/CD > Variables
7. ディレクトリ構成
品質ゲート関連ファイルの配置:
project-root/
├── .gitlab-ci.yml # パイプライン定義
├── .gitlab/
│ └── merge_request_templates/
│ └── default.md # MRテンプレート
├── scripts/
│ ├── check-mr-template.sh # テンプレート検証スクリプト
│ └── check-secrets.sh # 秘密情報検出スクリプト
└── docs/
└── workflows/
├── mr-template.md # テンプレート設計書(本ドキュメント群)
├── review-workflow.md # レビューワークフロー設計
└── quality-gate.md # 品質ゲート設計(本ドキュメント)