【Laravel Pint & Git Hooks】pre-commitを使ってレビューをもっと楽にしよう

# 無駄なコードレビューで疲弊していませんか？

Linterを使わないと、実装とは無関係の差分が発生しがちです。
レビューの質にも関わってくるので、この辺はコードをコミットする前に自動で直しましょう。

- インデントの階層違い
- `if()` or `if ()`
- `!$bool` or `! $bool` 
- その他諸々。。。

## 目標

[Laravel Pint](https://github.com/laravel/pint)と[Git Hooks(pre-commit)](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks#:~:text=The%20pre%2Dcommit%20hook%20is%20run%20first)を使って、コミット前にLintを走らせる。
**実装とは関係ない差分**をコードレビュー前に**排除**して、**レビューをもっと楽にする。**

## 前提条件

- Laravelプロジェクト作成済
- プロジェクトがGit管理されている
- PHPがローカル環境またはDockerコンテナ内にインストール済
- Composerがローカル環境またはDockerコンテナ内にインストール済

## 手順

### 1. [Laravel Pint](https://github.com/laravel/pint)インストール

[公式ドキュメント](https://laravel.com/docs/9.x/pint)曰く、最近のLaravelだと標準でインストールされているそうです。
Composer.jsonの中を確認して、`laravel/pint`の表記がある場合は、飛ばしてください。

```bash
composer require laravel/pint --dev
```

### 2. pre-commit用シェルスクリプト用意

`.git/hooks`ディレクトリの中を見ると、以下のようにGit Hooksに関するサンプルスクリプトが用意されています。

![スクリーンショット 2022-10-25 13.23.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838340/a5519800-c768-20a1-5632-1c6069cacc63.png)

今回使用したいのは、`pre-commit.sample`です。
しかし、`.git`ディレクトリはGit管理下に含めることができません。
つまり、このままではチームメンバーに`pre-commit`のスクリプトを使用してもらうことができません。

そこで、Git管理できるように別ディレクトリを作成してその中に`pre-commit`のスクリプトを作成します。

```bash
# プロジェクトのトップディレクトリにいる前提
#1. ディレクトリ作成(ディレクトリ名は何でもOK）
mkdir .githooks

#2. pre-commitシェルスクリプトをコピー
cp .git/hooks/pre-commit.sample .githooks/pre-commit

#3. 実行権限を付与
chmod +x .gihooks/pre-commit

#4. pre-commitの中身を変更
vim .githooks/pre-commit
```

中身は以下のように書き換えてください。

```bash
#!/bin/sh
fileList=$(git diff --diff-filter=d --cached --name-only)

phpFiles=$(echo "$fileList" | grep -E '\.(php)$')

if [ ${#phpFiles} -gt 0 ]; then
  # Lintスタート
  echo 'Start Linting with laravel/pint.'
  docker exec app ./vendor/bin/pint ${phpFiles[*]} "$@"
  # エラーがあれば、コミットさせない
  if [ $? -gt 0 ]; then
    exit 1
  fi
  # エラーがなければ、Linterが変更した差分を取り込んでコミット
  git add ${phpFiles[@]}
fi
```

以下の部分に関しては、Laravel/Pintが走れば何でも大丈夫です。
私の場合は、Docker内にPHPがインストールされているので、dockerコマンドにて実行しています。

```bash
docker exec app ./vendor/bin/pint ${phpFiles[*]} "$@"
```

### 3. .githooksディレクトリをHooksのコアディレクトリに指定

手順２の段階では、コミットしてもまだpre-commitスクリプトは走りません。
なぜなら、デフォルトでは`.git/hooks`ディレクトリ内の`pre-commit`などを実行するからです。

なので、今回新規作成した`.githooks`ディレクトリの中を見るように設定を変更しましょう。

```
git config --local core.hooksPath .githooks
```

↑このコマンドを打つだけです。
`.githooks`の部分は、作成したディレクトリ名に合わせてください。

### 4. pre-commit実行確認

PHPファイルを適当に編集して、コミットしてみましょう。

```php
//正常
    public function handle(Admin $admin): ?bool
    {
        return DB::transaction(fn () => $admin->delete());
    }

    //↓↓↓　fn（）部分を編集

//エラー
    public function handle(Admin $admin): ?bool
    {
        return DB::transaction(fn() => $admin->delete());
    }
```

```bash
# 1. 編集したファイルをステージングに上げる
git add {編集したPHPファイル名}

# 2. コミットする
git commit -m "commit with pre-commit"

# 3. Laravel/Pintが走ることを確認
Start Linting with laravel/pint.

  ✓

  ──────────────────────────────────────────────────────────────────── Laravel  
    FIXED   ...................................... 1 file, 1 style issue fixed  
  ✓ app/Actions/DeleteAdmin.php   function_declaration  

[feature/pint 86bda9c4] pre-commit

# 4. コミット履歴確認
git log
```

コミット履歴に、１つ新規コミットが追加されています。
加えて、編集したファイルを確認してみると正しい記述に自動で修正されています。

手順としては、以上となります。

## よくある質問

### 1. Git Hooksのコアディレクトリ設定は毎回手動でコマンド入力が必要？

最初の`composer install`時に自動でこのコマンドを走らせるようにしましょう。
`Composer.json`内の`scripts`の中にコマンドを追加します。

```json
{
  "scripts": {
     "post-install-cmd": [
        "git config --local core.hooksPath .githooks"
     ],
  }
}
```

こうすることで、新しいメンバーがジョインした時にも手動入力なしで即座にpre-commit適用が可能です。

### 2. PHPファイル以外の差分しかない場合はどうなる？

`Laravel/Pint`が走るのは、PHPファイルの差分がある時だけです。
`.json`や`.html`にのみ差分がある場合は、Lint無しでコミットされます。

### 3. Lint実行時にエラーが起こった場合はどうなる？

Lint実行時に、エラーが起こった場合はコミットされません。
エラー内容を確認して、エラー解消してから再度コミットしてください。

## 参考文献
https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks
https://efcl.info/2021/08/21/git-precommit-hook/
https://markus.oberlehner.net/blog/lint-only-files-with-changes-on-pre-commit/