search
LoginSignup
0

posted at

updated at

【Laravel向け】git hookで自動的にLintと静的解析を行う

TL;DR

リポジトリ内に.git/hooks/pre-commitを作成し、内容を以下の通りとします。
コミットする直前に.git/hooks/pre-commitスクリプトが実行され、Lintと静的解析を行います。

前提

PHPcomposernode.jsnpmeslintphpcslarastanはインストール済みであるものとします。

.git/hooks/pre-commit
#!/bin/bash
<< COMMENT
MIT License

Copyright (c) 2022 hidao80
COMMENT

php_target_files=`git diff --cached --name-only | grep .php`
js_target_files=`git diff --cached --name-only | grep ".js\|.vue\|.jsx\|.tsx\|.ts\|.json\|.html\|.twig"`
phpcs=`composer global config bin-dir --absolute 2> /dev/null`/phpcs
phpcs_ruleset=./phpcs.xml
phpstan=`composer global config bin-dir --absolute 2> /dev/null`/phpstan
viewer=code

# メインで他のシェルを使っているときのためにこのスクリプトで使う環境変数をセットする
export NODE_PATH=`npm root -g`

if [ -e ./storage/logs ]; then
    # Laravelプロジェクトのリポジトリの場合
    phpstan_extension="-c `composer global config home --absolute 2> /dev/null`/vendor/nunomaduro/larastan/extension.neon"
    output=./storage/logs/git_hook.log
else
    # Laravelプロジェクトではないリポジトリの場合
    phpstan_extension=""
    output=./git_hook.log
fi

# pre-commitのログを削除する
echo "" > $output

# ステージングしたファイルにJavaScriptやjsonがなければスキップ
if [ -z "$js_target_files" ]; then
    echo "[eslint] js/json files not found." >> $output 2>&1
else
    echo "[eslint] running..." >> $output 2>&1
    eslint $js_target_files >> $output 2>&1
fi

# ステージングしたファイルにPHPがなければスキップ
if [ -z "$php_target_files" ]; then
    echo "[larastan] php files not found." >> $output 2>&1
else
    echo "[phpcs] running..." >> $output 2>&1
    $phpcs --standard=$phpcs_ruleset $php_target_files >> $output 2>&1

    echo "[larastan] running..." >> $output 2>&1
    php -d memory_limit=-1 $phpstan analyse -l 5 $phpstan_extension $php_target_files >> $output 2>&1
fi

# VS Codeで今実行したpre-commitのログファイルを開く
$viewer $output

解説

git diff --cached --name-only

リポジトリでステージングしている(git add .など)ファイル名のリストを取得するコマンドです。
この後のLinterや静的解析に変更したファイルだけを渡して処理時間を抑える工夫です。

composer global config bin-dir --absolute 2> /dev/null

composerのグローバルでインストールしたときの保存先を取得します。
OSの違いやインストールオプションによって異なるので、コマンドで取得するようにしています。
シバンでbashを指定しているので、Windows環境でもディレクトリ区切り文字を\にする必要はありません。

export NODE_PATH=`npm root -g`

npmのグローバルでインストールしたときのnode_modules/の保存先を取得します。
OSの違いやインストールオプションによって異なるので、コマンドで取得するようにしています。
環境変数NODE_PATHがないとeslintに必要なパッケージeslint-plugin-*が読み込めず、eslintが動かない可能性があります。

ファイルリストが空のとき

JavaScriptPHPのそれぞれでステージングしているファイルのリストを取得します。
JavaScriptのファイルリストが空ならばeslintは実行しません。
PHPのファイルリストが空ならばphpcsphpstanは実行しません。

$viewer $output

今回pre-commitスクリプトが実行したときに出力したログファイルをVS Codeで開きます。
VS Codeが入っていない、またはログの確認に使用したいプレビューアーがある場合は変数viewerの値をそのプログラム名と置き換えてください。

ライセンス

pre-commitスクリプトはMITライセンスで配布しています。

ログファイルの例


[eslint] js/json files not found.
[phpcs] running...
. 1 / 1 (100%)


Time: 31ms; Memory: 6MB

[larastan] running...
 0/1 [░░░░░░░░░░░░░░░░░░░░░░░░░░░░]   0%[1G[2K 1/1 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%

 ------ ---------------------------------------------------------------------- 
  Line   aaa.php                                                               
 ------ ---------------------------------------------------------------------- 
  10     PHPDoc tag @param for parameter $id with type string is incompatible  
         with native type int.                                                 
  12     Function fucn() with return type void returns mixed but should not    
         return anything.                                                      
  12     Undefined variable: $id2                                              
  14     Unreachable statement - code above always terminates.                 
 ------ ---------------------------------------------------------------------- 


 [ERROR] Found 4 errors                                                         


応用

.git/hooks/commit-msgスクリプトでコミットメッセージをチェックし、わざと異常終了するようなコミットメッセージでコミットを試みることで、コミット前にソースの自動解析だけをすることができます。

また、.git/hooks/pre-commitでエラーや警告が出た場合、スクリプトを異常終了させる(exit 1など)ことで、Lintと静的解析による不具合チェックを通らないとコミットできないようにもできます。(ただし、緊急時に困ることがありそう)

ダウンロード

本スクリプトはgistからダウンロードできます。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
0