GitHub ActionsでPHP_CodeSniffer+reviewdogを大規模プロジェクトに組み込む

# はじめに
PHPのプロジェクトのコードフォーマットが安定しない＆CIでやってみるということで、GitHub Actionsにphpcs（PHP_CodeSniffer）とreviewdogを組み合わせて設定します。
大規模な業務プロジェクトに対して設定してみたところ、ソースコードの多さによる影響があったので、対策をして組み込んでみたのでメモ代わりに。

# 環境
PHP 7.4
Composer 2.4
GitHub Actions

# プロジェクトにphpcsを入れる
次のコマンドでCI適用するプロジェクトにphpcsをインストール。

``` shell
composer --dev squizlabs/php_codesniffer
```

`composer.json`の`require-dev`に追加されます。
``` json
    "require-dev": {
        "squizlabs/php_codesniffer": "^3.7"
    }
```

# 最初のYAML

https://r-tech14.com/reviewdog/

https://aka-shin.com/phpstan-php-codesniffer-reviewdog-github-actions/

など参考に、以下のようなYAML定義を作成。
適用するプロジェクトのPHPが7.4なのでバージョンを合わせてインストールするようにしました。

``` reviewdog.yml
name: reviewdog
on:
  pull_request:

jobs:
  php-code-sniffer:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: reviewdog/action-setup@v1
        with:
          reviewdog_version: latest
      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '7.4.33'
      - name: composer install
        run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist
      - name: run codesniffer with reviewdog
        env:
          REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: ./Vendor/bin/phpcs --report=emacs --standard=PSR12 ./ | reviewdog -reporter=github-pr-review -efm='%f:%l:%c:%m' -fail-on-error=true
```
プルリクエストでGitHub Actionsを動作させてみると、10分かかっているという。
全体で約900のPHPファイル、ソースコード行数30万行のプロジェクトでは仕方ないかもしれませんが、毎回プルリクエストで10分かかるのはちょっとつらいので工夫したいと思います。
![スクリーンショット 2023-02-26 8.32.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3081806/fd6c0a8f-9dd1-78e1-7a11-0d1f82d18220.png)

# 変更後YAML
reviewdogがプルリクエストで変更のある箇所のみレポートしてくれるとはいえ、ソース規模が大きいと解析するphpcsで時間がかかるようです。

https://qiita.com/osakiy/items/0e18eee444446307b970

こちらを参考に、変更したファイルのみに限定してphpcsを実行するように変更してみました。

``` reviewdog.yml
name: reviewdog
on:
  pull_request:
    paths:
      - '**.php'

jobs:
  php-code-sniffer:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
        with:
          ref: ${{ github.event.pull_request.base.sha }}
      - uses: actions/checkout@v3
      - uses: reviewdog/action-setup@v1
        with:
          reviewdog_version: latest
      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '7.4.33'
      - name: composer install
        run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist
      - name: Get changed files
        id: get_changed_files
        run: |
          echo "changed_files=$(git diff --diff-filter=AM --name-only ${{ github.event.pull_request.base.sha }}.. | tr '\n' ' ' )" >> $GITHUB_OUTPUT
        shell: bash
      - name: Filter PHP files
        id: filter_php_files
        run: |
          echo "php_files=$(echo ${{ steps.get_changed_files.outputs.changed_files }} | tr ' ' '\n' | grep '\.php$' | paste -sd ' ' -)" >> $GITHUB_OUTPUT
        shell: bash
      - name: run codesniffer with reviewdog
        env:
          REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: ./Vendor/bin/phpcs --report=emacs --standard=PSR12 ${{ steps.filter_php_files.outputs.php_files }} | reviewdog -reporter=github-pr-review -efm='%f:%l:%c:%m' -fail-on-error=true
```
`git diff --diff-filter=AM --name-only ${{ github.event.pull_request.base.sha }}..`で、変更されたファイル名のみ列挙しています。
出力を次のステップで使用できるように`$GITHUB_OUTPUT`を使いましたが、複数行の結果を返すとうまくいかないので、
``` yml
echo "changed_files=$(git diff --diff-filter=AM --name-only ${{ github.event.pull_request.base.sha }}.. | tr '\n' ' ' )" >> $GITHUB_OUTPUT
```
の部分では、いったん複数行の結果をスペース区切りで1行にまとめて`changed_files`に変更ファイル名を列挙するようにしました。（複数行を`$GITHUB_OUTPUT`に入れる方法はありますが、手っ取り早く1行にして出力しました。）

次のステップの
``` yml
echo "php_files=$(echo ${{ steps.get_changed_files.outputs.changed_files }} | tr ' ' '\n' | grep '\.php$' | paste -sd ' ' -)" >> $GITHUB_OUTPUT
```
では改めてスペース区切りを改行に変更してphpファイルのみgrepし、再びスペース区切りで1行に連結しています。

プルリクエストでGitHub Actionsを動作させると、3秒で完了しています。
今回の例では、変更を加えた2ファイルのみをphpcsに渡して実行しています。
`reviewdog: input data has violations`というエラーで赤×がついている（何とかしたい）のですが、やりたかったことはできた感じです。
![スクリーンショット 2023-02-26 11.54.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3081806/a3ae02ab-2748-fec2-be96-b1bf5594802e.png)

# おわりに
`phpcs --report=emacs --standard=PSR12 ./ | reviewdog -reporter=github-pr-review -efm='%f:%l:%c:%m' -fail-on-error=true`で、phpcsから得られた指摘をreviewdogでプルリクエストに表示できましたが、大規模プロジェクトでは時間がかかりました。
しかし、変更したファイルに限定してphpcsを実行できれば、大規模プロジェクトでも実行時間は問題なさそうです。