CIサービスはもういらない！？github actionsでphp-cs-fixerを使ってみる

本記事は、[サムザップ Advent Calendar 2020 #2](https://qiita.com/advent-calendar/2020/sumzap2) の12/3の記事です。

# はじめに
タイトル煽ってすみません（笑）

github actionsを使って、php-cs-fixerでコードのオートフォーマットを試してみた記事です。

みなさんは、チーム開発でファイル毎にインデントや改行等コードフォーマットの違いで可読性の下がったコードに悩まされたり、お互いに異なるコードフォーマットで修正し合うループに陥ったりしたことはないでしょうか。

その解決のため、コードフォーマットの統一するべく、IDEや開発ツールの統一を図ろうとし、

「自分〇〇派なので！」

「開発効率が落ちる！」

と反対されたり、逆に自身が使い慣れたツールを手放さなければならなかったりして困ったことはないでしょうか。

はたまたこれらの解決に、gitのフック機能（クライアントサイド）を使おうとして、各自の環境に依存したり、個々の問い合わせに対応が必要になって手間が大きくなる予感をエスパー並みにヒシヒシと感じて、手つかずだったりしていないでしょうか。

- windowsやmac、IDE等開発ツールを縛られずに
- 簡単に
- 統一されたコードフォーマット

を手に入れたい！！

ということで、**github actions & php-cs-fixer** を試していきたいと思います！！

## github actionsとは

- GitHub Actionsを使用すると、ワールドクラスのCI / CDですべてのソフトウェアワークフローを簡単に自動化できます。 GitHubから直接コードをビルド、テスト、デプロイでき、コードレビュー、ブランチ管理、問題のトリアージを希望どおりに機能させます。
- 引用元: [*https://github.co.jp/features/actions*](https://github.co.jp/features/actions)

とういことで、動的にサーバー立ててごにょごにょ出来ちゃう（自分で作ったdockerコンテナも動かせちゃう）サービスと解釈しました。

そして何と太っ腹なことに！？パブリックリポジトリならば、無料で利用できちゃいます。

[https://github.co.jp/features/actions#pricing](https://github.co.jp/features/actions#pricing)

## php-cs-fixerとは

今回の主目的であるオートフォーマットのツールです。

[https://github.com/FriendsOfPHP/PHP-CS-Fixer](https://github.com/FriendsOfPHP/PHP-CS-Fixer)

現在はPHPのコーディング規約はPSR-2が非推奨となり、PSR-12が推奨となったようですが、php-cs-fixerはまだPSR-12に対応していないようなので、対応されるまではPSR-2設定で良さそうかなと。

[https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-12-extended-coding-style-guide.md](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-12-extended-coding-style-guide.md)

## github actionsの設定

では、早速github actionsの設定を見ていきたいと思います。

github actionsは、ワークフローを定義することで、githubのイベントにフックして起動できます。

ワークフローの定義は、リポジトリ内に`.github/workflows/`のディレクトリを作成し、yaml形式で定義していきます。

複数配置することもでき、それぞれ実行されるようです。

以下今回のワークフローの定義です。

```yaml
name: PhpCsFixer

on: push

jobs:
  php_cs_fixer:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: confirm version
        run: |
          php -v
      - name: install php-cs-fixer
        run: |
          composer require --dev friendsofphp/php-cs-fixer -n
          ./vendor/bin/php-cs-fixer --version
      - name: dry-run php-cs-fixer
        run: |
          ./vendor/bin/php-cs-fixer fix --dry-run --diff --diff-format=udiff --using-cache=no . || true
      - name: execute php-cs-fixer
        run: |
          ./vendor/bin/php-cs-fixer fix --using-cache=no
          git status | grep modified || echo "GIT_STATUS_RESULT=$(echo $?)" >> $GITHUB_ENV
      - name: commit & push
        if: ${{ env.GIT_STATUS_RESULT == 0 }}
        run: |
          git config --global user.name "Github Actions"
          git config --global user.email "github_actions@example.com"
          git commit -a -m "auto code format by Github Actions"
          git push
```

### 以下順に定義の説明をします。

ワークフローの名前です。

```yaml
name: PhpCsFixer
```

必須：ワークフローをトリガーするGitHubイベントの名前。

githubの全てのイベントで起動可能と記載があり、pushやpull_requestのみならず、issueやリリース等様々なイベントを設定できるようです。

pushやpull_requestの場合は、ブランチの指定もできる模様。

[https://docs.github.com/ja/free-pro-team@latest/actions/reference/workflow-syntax-for-github-actions](https://docs.github.com/ja/free-pro-team@latest/actions/reference/workflow-syntax-for-github-actions)

今回はpushをトリガーに設定します。

```yaml
on: push
```

job定義です。

php_cs_fixerがjob_idです。

```yaml
jobs:
  php_cs_fixer:
```

必須：ジョブを実行するマシンの種類。

```yaml
runs-on: ubuntu-latest
```

stepの定義です。

github actionsでは環境変数が使えますが、step間では引き継がれません。

引き継ぎたい場合は、後述のGITHUB_ENVを使う必要があります。

```yaml
steps
```

usesは、ジョブでステップの一部として実行されるアクションを選択します。

ここではチェックアウトのアクションを選択し、自身のブランチをチェックアウトしています。

```yaml
- uses: actions/checkout@v2
```

nameは、GitHubで表示されるステップの名前。

runは、オペレーティングシステムのシェルを使用してコマンドラインプログラムを実行します。

ありがたいことに、ubuntu-latest環境には、デフォルトでPHPがインストールされています。

バージョンを指定したい場合は、非公式ですが、[shivammathur/setup-php](https://github.com/shivammathur/setup-php) を使うと良いようです。

```yaml
- name: confirm version
  run: |
    php -v
```

以下同様に実行したい処理を記述していきます。

composerでphp-cs-fixerをインストールし、バージョンを確認します。

```yaml
- name: install php-cs-fixer
  run: |
    composer require --dev friendsofphp/php-cs-fixer -n
    ./vendor/bin/php-cs-fixer --version
```

php-cs-fixerで変更される内容を表示するため、dry-runします。

このときdry-runの結果、修正対象のファイルがある場合は、exit code:8となるため、最後に、`|| true`とすることで、exit codeを0にします。

※github actionsでは、デフォルトではexit code:0以外を検出すると実行がストップするため

```yaml
- name: dry-run php-cs-fixer
  run: |
    ./vendor/bin/php-cs-fixer fix --dry-run --diff --diff-format=udiff --using-cache=no . || true
```

php-cs-fixerを実行します。

最後に結果をGITHUB_ENVを通して、次のstepに引き継ぎます。

```yaml
- name: execute php-cs-fixer
  run: |
    ./vendor/bin/php-cs-fixer fix --using-cache=no
    git status | grep modified || echo "GIT_STATUS_RESULT=$(echo $?)" >> $GITHUB_ENV
```

最後に、pushします。

先程のGITHUB_ENVで引き継いだ環境変数を`if`で判定して、変更があった場合のみpushします。

```yaml
- name: commit & push
  if: ${{ env.GIT_STATUS_RESULT == 0 }}
  run: |
    git config --global user.name "Github Actions"
    git config --global user.email "github_actions@example.com"
    git commit -a -m "auto code format by Github Actions"
    git push
```

ちなみに今回特に何の設定もせずcheckout & pushできてしまいました。

トリガーとなるgit操作をしたユーザーの権限と同じなんでしょうか！？

（どなたか教えてもらえたら嬉しいですっ

## 実行してみる

実際にpushしてみると、該当リポジトリのActionsタブに以下のような結果が表示されています。
![_2020-11-28_22.22.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209922/a807bf67-763d-9188-1035-11f6d64ac73d.png)


更に実行結果をクリックしていくと、以下のような画面で、job（左メニュー）とstep（中央の一覧）毎に結果が確認できます。
![_2020-11-28_22.25.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209922/669a1d38-05e9-7755-8078-560bd5a606a8.png)


また以下の画面の通り、php-cs-fixerで修正した内容がpushされていることが分かります。
![_2020-11-28_22.31.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209922/2afdb105-38ec-50e3-1157-eb2f9eb2c6e7.png)

## まとめ
主目的のphp-cs-fixerについて、あまり触れられていませんが、力尽きたのでここまでで。。
余力があれば、ルールの設定方法や、個々のルールについてあれこれ試して記載したかったところです。

とはいえ、github actionsが様々な用途に使えそうなことは理解していただけたのではないでしょうか。

弊社では、unittestの実行や自作のチェックツールの実行等に利用しています。
PRをトリガーとし、マージ前にテストや各種チェックが失敗していないかをチェックして、その後にマージするといったフローを実現しています。