GitHub ActionsでPHP-CS-Fixerを実行してコミットする

ついうっかりPHP-CS-Fixerで自動整形し忘れてpushしても、GitHub Actionsで自動整形したら幸せになれると思ったので試してみました。


## 前提

Laravel Sailで作成したプロジェクトでPHP-CS-Fixerを利用できるようした[GitHubリポジトリ](https://github.com/kai-kou/laravel-sail-sample-projects)にワークフローを追加する手順となります。

## 手順

### ローカルでワークフロー実行ができるツールのインストール

ローカルでワークフロー実行ができるツールがありました🚀🚀🚀

nektos/act: Run your GitHub Actions locally 🚀
https://github.com/nektos/act

インストール方法も簡単なので導入しやすいです。

```console:Macの場合
> brew install act
```

セットアップスクリプトも提供されているので、Mac以外でもインストールは簡単です。

```console
> curl https://raw.githubusercontent.com/nektos/act/master/install.sh | sudo bash
```


### ワークフローを設定する

ツールがインストールできたらワークフローを作成します。

```console
> cd <プロジェクトのディレクトリ>
> mkdir -p .github/workflows
> touch .github/workflows/php-cs-fixer.yml
```

定義したワークフローは以下となります。

```yaml:php-cs-fixer.yml
name: Laravel

on: push

jobs:
  php-cs-fixer:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2
    - name: Cache vendor
      id: cache
      uses: actions/cache@v1
      with:
        path: ./vendor
        key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
        restore-keys: |
          ${{ runner.os }}-composer-
    - name: Composer install
      if: steps.cache.outputs.cache-hit != 'true'
      run: composer install -n --prefer-dist
    - 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
    - name: Commit and push
      uses: stefanzweifel/git-auto-commit-action@v4.2.0
      with:
        commit_message: pxp-cs-fixer by Github Actions
    - name: Run if changes have been detected
      if: steps.auto-commit-action.outputs.changes_detected == 'true'
      run: echo "Changes!"
    - name: Run if no changes have been detected
      if: steps.auto-commit-action.outputs.changes_detected == 'false'
      run: echo "No Changes!"
```

#### パッケージのインストール

GitHub上ワークフロー実行する際にはキャッシュが効くので、下記を参考に`vendor`ディレクトリが存在するか否かで`composer install`が実行されるようにしました。

cache/examples.md at main · actions/cache 　
https://github.com/actions/cache/blob/main/examples.md#php---composer

```yaml
    - name: cache vendor
      id: cache
      uses: actions/cache@v1
      with:
        path: ./vendor
        key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
        restore-keys: |
          ${{ runner.os }}-composer-
    - name: composer install
      if: steps.cache.outputs.cache-hit != 'true'
      run: composer install -n --prefer-dist
```

#### PHP-CS-Fixerの実行

変更箇所がわかるように`--dry-run --diff --diff-format=udiff`オプションを付けています。
また、最後に`|| true`をつけることでワークフローが終了しないようにしています。

こちらの記事を参考にさせてもらいました。(感謝

CIサービスはもういらない！？github actionsでphp-cs-fixerを使ってみる - Qiita
https://qiita.com/kojima_akira/items/1eeafcb4fd3bf910182e

```yaml
    - 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
```

#### リポジトリへコミット

変更されたファイルを自動コミットしてくれる素敵なアクションがあったので利用しました。

stefanzweifel/git-auto-commit-action: Automatically Commit changed Files back to Github with Github Actions
https://github.com/stefanzweifel/git-auto-commit-action


```yaml
    - name: Commit and push
      uses: stefanzweifel/git-auto-commit-action@v4.2.0
      with:
        commit_message: pxp-cs-fixer by Github Actions
    - name: Run if changes have been detected
      if: steps.auto-commit-action.outputs.changes_detected == 'true'
      run: echo "Changes!"
    - name: Run if no changes have been detected
      if: steps.auto-commit-action.outputs.changes_detected == 'false'
      run: echo "No Changes!"
```


## ローカルで動作確認

```console
# ワークフローの一覧
> act -l

ID            Stage  Name
php-cs-fixer  0      php-cs-fixer


# ワークフローの実行
> act -P ubuntu-latest=nektos/act-environments-ubuntu:18.04
```

`-P`オプションで実行する環境のDockerイメージを指定します。actツール作成している方がイメージをいくつかしてくれていますが、Composerが利用できるのが指定したイメージとなります。

nektos/act: Run your GitHub Actions locally 🚀
https://github.com/nektos/act#runners

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48549/cc3184ee-6614-4b11-3e43-ad65793c4185.png)

初回はイメージの取得で時間がかなりかかりますが、2回目からはサクサク動作します。
`git: 'secrets' is not a git command. See 'git --help'.`エラーが発生してGitHubリポジトリへのpushに失敗しましたが、調べてもnektos/actで解決する方法をみつけられなかったのでひとまず良しとします。
（おわかりになられる方がいたらぜひコメントお願いします。）

## GitHubで動作確認

作成したワークフローをGitHubリポジトリにPushするついでにワークフローの動作確認をします。

```console
> git brunch feature/use-php-cs-fixer
> git checkout feature/use-php-cs-fixer
> git add .github/
> git commit -m 'Add: GitHub ActionsでPHP-CS-Fixerの自動整形を実行するようにしました。'
> git push
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48549/6874ceb4-c7b8-8a18-58e7-9fc8252dce7d.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48549/e44a9922-d8f0-d55b-6760-a29ef0a621ff.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48549/feba1c5c-0639-ec49-a296-5da8b408d161.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48549/045613d2-9fa7-d65f-fc6e-e8e8dd8e0515.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48549/454c8c4f-54c3-06dd-fff1-38a7952ea46e.png)

無事にPHP-CS-Fixerが実行できて、リポジトリへのpushもできました。
やったぜ


## 参考

nektos/act: Run your GitHub Actions locally 🚀
https://github.com/nektos/act

cache/examples.md at main · actions/cache 　
https://github.com/actions/cache/blob/main/examples.md#php---composer

CIサービスはもういらない！？github actionsでphp-cs-fixerを使ってみる - Qiita
https://qiita.com/kojima_akira/items/1eeafcb4fd3bf910182e

stefanzweifel/git-auto-commit-action: Automatically Commit changed Files back to Github with Github Actions
https://github.com/stefanzweifel/git-auto-commit-action

【PHP】CS-Fixerの整形をGithub Actionsで自動化するぞ。 - ポンコツエンジニアのごじゃっぺ開発日記。
https://www.pnkts.net/2020/07/29/php-cs-fixer-by-github-actions

Github Actionで自動コミット設定をする
https://sunday-morning.app/posts/2020-5-19-github-action-auto-commit