Blog

MRごとに PHPStan + AIコードレビューを走らせ、reviewdogでコメントするCI構成

岡本 渉 技術ブログ

寒い日が続きますね、私が住んでいる新潟県では久しぶりの大雪で路面がでこぼこしています。
ようやく除雪も追いついてきたようで、一安心ですが、まだまだ油断できません。
さて、弊社では他社で運営・保守していたPHPで作成されたサイトの保守を引き継いで行うにあたり、レビュアーの負荷が増えてしまうという問題が発生していました。
対策としてPHPStanによる静的エラーチェックと、AI(Gemini)によるコードレビューをGitLabのパイプラインで自動化してみましたので、軽く手法を紹介したいと思います。
お手柔らかにお願いいたします。

目的と背景


株式会社コルシスのバックエンドチームでは、セルフマネージド版のGitLabを運用しています。
その運用で発生するレビュー負荷を減らすため、PHPStanによる静的エラーチェックAI(Gemini)によるコードレビューを自動化しました。

コードレビューの品質を安定させるために、
  • 静的解析(PHPStan)で確実に拾える問題は機械に任せる
  • 仕様の穴や設計の癖など、人が気づきにくい点はAIレビューで補助する
  • どちらもMR上にコメントとして残し、レビュアーの視認性を高める

という構成を採用しました。本記事ではこの設定を汎用化し、GitLab CIでの構成例としてまとめます。

全体像(処理フロー)

MR作成
├─ PHPStanジョブ: 解析 → reviewdog投稿
└─ AIレビューjob: 差分抽出 → AIレビュー → reviewdog投稿
mermaid-diagram-2026-01-28T04-11-43.png

ポイント

  • どちらもMRパイプラインでのみ実行
  • 失敗してもパイプライン全体は落とさない
  • コメントは reviewdog に集約

構成要素

1) PHPStan(静的解析)

  • phpstan--error-format=raw で出力
  • reviewdog -f=phpstan でMRにコメント投稿
  • メモリや解析範囲は必要に応じて調整

2) AIコードレビュー

  • MR差分を取得し、AIにレビュー依頼
  • 返却された指摘をreviewdogのrdjsonl形式へ変換
  • reviewdogでMRに投稿

3) reviewdog

  • CI内でインストールし、標準入力で受け渡し
  • GitLabでは -reporter=gitlab-mr-discussion を利用

GitLab CIのサンプル

stages:
  - test
  - review

phpstan:
  stage: test
  image: php:8.4-alpine
  rules:
    - if: $CI_MERGE_REQUEST_ID
  allow_failure: true
  before_script:
    - apk add --no-cache curl git php84 php84-cli php84-phar php84-openssl php84-curl php84-zip php84-mbstring php84-ctype
    - php -r "copy('https://getcomposer.org/installer','composer-setup.php');"
    - php composer-setup.php --install-dir=/usr/local/bin --filename=composer
    - rm -f composer-setup.php
    - curl -sfL https://raw.githubusercontent.com/reviewdog/reviewdog/master/install.sh | sh -s -- -b /usr/local/bin
  script:
    - cd app
    - composer install --no-interaction --no-progress
    - ./vendor/bin/phpstan analyse --error-format=raw --no-progress --memory-limit=512M > phpstan-report.txt || PHPSTAN_EXIT=$?
    - reviewdog -f=phpstan -name="phpstan" -reporter=gitlab-mr-discussion -fail-on-error=false < phpstan-report.txt
    - exit ${PHPSTAN_EXIT:-0}

ai_code_review:
  stage: review
  image: python:3.11-alpine
  rules:
    - if: $CI_MERGE_REQUEST_ID
  allow_failure: true
  before_script:
    - apk add --no-cache git curl
    - pip install --no-cache-dir requests
    - curl -sfL https://raw.githubusercontent.com/reviewdog/reviewdog/master/install.sh | sh -s -- -b /usr/local/bin
    - git fetch origin ${CI_MERGE_REQUEST_TARGET_BRANCH_NAME:-main} || true
  script:
    - python3 bin/ai-code-review.py --model ${AI_REVIEW_MODEL:-} --max-files ${AI_REVIEW_MAX_FILES:-10} > ai-review-report.txt || AI_REVIEW_EXIT=$?
    - if [ -s ai-review-report.txt ]; then reviewdog -f=rdjsonl -name="ai-code-review" -reporter=gitlab-mr-discussion -fail-on-error=false < ai-review-report.txt; fi
    - exit ${AI_REVIEW_EXIT:-0}

AIレビュー用スクリプトの要点

  1. MRの差分ファイル一覧を取得
  2. 対象ファイルのdiffを取得
  3. Gemini APIに差分を送信
  4. 返却文をreviewdogのrdjsonlに変換
  5. 標準出力へ流す

設計のコツ

  • レビュー対象ファイル数やdiffサイズを制限(コスト・速度のため)
  • 「No issues found」のときは空出力
  • コメントの行番号はdiffのhunk先頭など、最低限でOK

変数・シークレットの例

  • GEMINI_API_KEY
  • AI_REVIEW_MODEL(任意)
  • AI_REVIEW_MAX_FILES(任意)

reviewdog用のトークンが必要な環境では、REVIEWDOG_GITLAB_API_TOKEN等をCI変数として設定します。

運用のポイント

  • 最初はallow_failureで導入し、ノイズ量を調整
  • PHPStanの levelexcludePaths を段階的に厳格化
  • AI(Gemini)レビューはレビューの補助として位置付ける
  • 1ジョブあたりの時間とコストを計測する
  • 機密情報は送らない運用を徹底する(後述)

よくある落とし穴

  • diffが巨大でAIが落ちる → diffサイズ制限ファイル数制限
  • reviewdogの投稿失敗 → APIトークンの権限不足
  • PHPStanの解析が遅い → --memory-limitpaths を見直し

セキュリティと情報取り扱い(重要)

AIレビュー導入にあたっては、コードが学習に使われないこと、およびシークレット(DBパスワード等)が外部に漏れないことを前提条件としています。
そのため、以下の運用と文章を必ず明記します。

前提を明文化する文面(例)

送信される差分には機密情報を含めない運用を徹底し、レビュー対象のコードが学習用途に利用されないことを前提としています。
特にAWS Access Token/API Key/DBパスワード等のシークレットはCI環境変数で管理し、AIレビューへの送信対象から除外します。

実務上の対策(例)

  • 差分送信前に秘密情報の混入を検知(シークレット検知ツール/禁止パターン)
  • .envconfig/* など機密が含まれうるパスを除外
  • AI APIのデータ保持ポリシーを事前確認し、社内規定に合うものだけ利用

まとめ

PHPStanとAIレビューをMR単位で自動化し、reviewdogでコメント投稿まで繋げると、レビュー体験が大きく改善します。
この構成はGitLab以外でも移植可能なので、必要に応じてreporterや差分取得方法を置き換えて運用してください。

現場からは、以上です。
一覧にもどる