GitLab MRレビュー自動化を壊さず運用する設計と実装

結論

MRレビュー自動化は、次の3ジョブに分けると安定します。

  1. llm-review: 判定とレポート作成
  2. apply-review-fix: approve時だけ修正commit
  3. approve-after-green: bot commit後のgreen確認後にapprove

この設計で、botループを防ぎつつ、低品質記事のマージを確実にブロックできます。

前提

  • GitLab CE 17.x
  • Runner: shell executor
  • Python 3.12
  • OpenRouter API (anthropic/claude-sonnet-4, deepseek/deepseek-chat, google/gemini-2.5-flash) OpenRouter設定例:
export OPENROUTER_API_KEY="your_key_here"
# 環境変数またはGitLab CI/CD Variables で設定
  • ブランチ保護: main は「成功パイプライン必須」 注意: この設定により、パイプラインが失敗した場合は自動的にマージがブロックされます。GitLabの「Settings > Repository > Push Rules」で設定可能です。

なぜ1ジョブ完結をやめたか

以前は1ジョブで「判定→修正push→approve」を実行しました。 その結果、次の障害が発生しました。

  • bot commitで同じレビューが再発火する
  • push競合でapproveが先に走る可能性がある
  • 失敗時にどこで壊れたか追跡しづらい

実装

.gitlab-ci.yml

stages:
  - review
  - apply

llm-review:
  stage: review
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_COMMIT_MESSAGE =~ /\[bot-review\]/'
      when: never
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
      changes:
        - content/**/*.md
  script:
    - python -m review.scripts.review_pipeline

apply-review-fix:
  stage: apply
  needs: ["llm-review"]
  script:
    - python -m review.scripts.apply_fix_from_report

判定ロジック

editor_reject: 編集者が明示的に拒否した場合 editor_has_critical_thin_content: 記事が極端に短く、重要な内容が不足している場合

# 例: editor=0.4, reviewer1=0.3, reviewer2=0.3
weighted_score = sum(score * weight)
if all_reject: reject
elif editor_reject: reject
elif editor_has_critical_thin_content: reject
elif weighted_score >= 70 and approve_count >= 2: approve
else: request_changes

push時のループ防止

commit message: chore(review): apply ai suggestions [bot-review]

[bot-review] を rules で除外することで、同一MR内での再帰を止めます。 重要: rules の条件は上から順に評価されるため、除外条件を最初に配置する必要があります。

失敗モードと動作

reject

  • review/rejected-final ラベル
  • MRコメントに拒否理由と改善方針
  • MRをClose
  • pipelineはfailed

request_changes

  • review/changes-requested ラベル
  • MRはopen維持
  • pipelineはfailed
  • 作者の追コミットで再評価

approve

  • 修正差分があればsource branchへpush
  • review/approved ラベル
  • bot commit後のpipelineがgreenならapprove 実装詳細: approve-after-green ジョブは別パイプラインで実行され、前回のパイプライン状態をGitLab APIで確認してからapproveを実行します。

system_error

  • review/system-error ラベル
  • MRはopen維持
  • retryで復旧可能

実測値(2026-02)

対象: 12 MR / 12記事

  • 平均レビュー時間: 142秒
  • OpenRouter APIエラー率: 3.1%(37/1190、HTTP 429/5xx/timeout)
  • フォールバック成功率: 100%(9/9)
  • 重複コメント: 0件
  • ループ発生: 0件

APIエラー率は HTTP 429, 5xx, timeout の合計を分子にしています。 フォールバック戦略: プライマリAPI失敗時は、設定された順序で代替モデル(deepseek → gemini)に自動切り替えします。全て失敗した場合は system_error 状態になります。

セキュリティ

  • curl --insecure はデフォルト無効
  • site-bundle.locksource しない(キー抽出に変更)

source は任意シェル実行の攻撃面になるため、CIでは禁止しました。

まとめ

  • 判定と副作用を分離する
  • ループは commit marker で遮断する
  • 失敗モードを reject / request_changes / system_error に分離する

この3点を先に固定すると、MRレビュー自動化は壊れにくくなります。