TL;DR
リポジトリ内に.git/hooks/pre-commit
を作成し、内容を以下の通りとします。
コミットする直前に.git/hooks/pre-commit
スクリプトが実行され、Lintと静的解析を行います。
前提
PHP
、composer
、node.js
、npm
とeslint
、phpcs
、larastan
はインストール済みであるものとします。
#!/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
が動かない可能性があります。
ファイルリストが空のとき
JavaScript
とPHP
のそれぞれでステージングしているファイルのリストを取得します。
JavaScript
のファイルリストが空ならばeslint
は実行しません。
PHP
のファイルリストが空ならばphpcs
とphpstan
は実行しません。
$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からダウンロードできます。