前提
pre-commitフックで実現する内容
-
php -l
コマンドによるシンタックスチェック - php-cs-fixerによるコード整形
- phpmdによるコーディグルールのチェック
- phpstanによるシンタックスチェック
参考サイト↓
[PHP] Git Commit 時にコードの整形、静的解析を実行
環境
Docker使用
Laravelプロジェクト
ディレクトリ構成
backendにLaravelが格納されています。
その一個上の階層がプロジェクトのルートディレクトリとなっていて、そこに.git
があるイメージですね。
Dockerを使用していると、こういう感じになっているかと思います。
Laravelの1つ上の階層がルートディレクトリというのがpre-commitを書く上でも重要になってきます。
$ tree -L 1 -a
.
├── .DS_Store
├── .git // .git/hooks/pre-commitにスクリプトを書く
├── .gitignore
├── .php-cs-fixer.cache
├── .vscode
├── README.md
├── backend // ここがLaravelプロジェクトが入っているところ
├── docker-compose.yml
├── infra
└── test.txt
.gitのディレクトリ構成
.git
├── COMMIT_EDITMSG
├── HEAD
├── config
├── description
├── hooks
│ ├── applypatch-msg.sample
│ ├── commit-msg.sample
│ ├── fsmonitor-watchman.sample
│ ├── post-update.sample
│ ├── pre-applypatch.sample
│ ├── pre-commit
│ ├── pre-commit.sample
│ ├── pre-merge-commit.sample
│ ├── pre-push.sample
│ ├── pre-rebase.sample
│ ├── pre-receive.sample
│ ├── prepare-commit-msg.sample
│ ├── push-to-checkout.sample
│ └── update.sample
(以下略)
hooksの中にはpre-commit以外のsampleも色々入っていますね。
最初はpre-commitファイルはありません。作成する必要があります
pre-commitを書くまでの手順
インストールするライブラリ
- phpstan
- phpmd
- php-cs-fixer
インストール方法
// PHPのコンテナ内のLaravelのプロジェクトのルートディレクトリで以下のコマンドを実行
composer require --dev friendsofphp/php-cs-fixer
composer require --dev phpmd/phpmd
composer require --dev phpstan/phpstan
ちなみに--dev
オプションで開発環境向けのインストールをします。
本番環境でcomposer install
を実行する際に--no-dev
オプションをつけて実行することで、composer.jsonのrequire dev
オプションに記載されているライブラリ自体はvendorへインストールされません。
composer install --no-dev
pre-commitの作成
Git管理のルートディレクトリから以下のコマンドを実行し、pre-commitを作成していく。
vi .git/hooks/pre-commit
そして以下の内容をpre-commitに記述して:wq
で保存する。
#!/bin/bash
echo -e $'\e[1;45m ***** pre-commit hookを実行します ***** \e[m'
# プロジェクトディレクトリ
project_dir=backend
if git rev-parse --verify HEAD >/dev/null 2>&1
then
against=HEAD
else
# Initial commit: diff against an empty tree object
against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
fi
# Redirect output to stderr.
exec 1>&2
SYNTAX_CHECK=0
## 静的解析、及びフォーマッターの実行
# コミットされるファイルのうち、.phpで終わるもの
for FILE in `git diff-index --name-status $against -- | grep -E '^[AUM].*\.php$'| cut -c3-`; do
# シンタックスのチェック
echo -e $'\e[36mphp -l シンタックスチェック \e[m'
if php -l $FILE; then
# PSR準拠でコード書き換え
echo -e $'\e[36m\nphp-cs-fixerを実行します \e[m'
$project_dir/vendor/bin/php-cs-fixer fix $FILE
git add $FILE
# PHPMDで未使用変数などのチェック
echo -e $'\e[36m\nphpmdを実行します \e[m'
if ! $project_dir/vendor/bin/phpmd $FILE text backend/ruleset.xml; then
SYNTAX_CHECK=1
echo -e $'\e[31;43m phpmdのチェックエラーを直してください \e[m'
fi
# phpstanでコード静的解析
echo -e $'\e[36m\nphpstanを実行します \e[m'
if ! $project_dir/vendor/bin/phpstan analyse $FILE; then
SYNTAX_CHECK=1
echo -e $'\e[31;43mphpstanのチェックエラーを直してください \e[m'
fi
else
SYNTAX_CHECK=1
fi
done
## 最後の確認
if [ $SYNTAX_CHECK -eq 0 ]; then
exit 0
else
echo -e $'\e[31;43m 修正を行った上で再度コミットしてください \e[m'
exit 1
fi
phpmdチェックで引っ掛かるように、以下のコードを
- backend/app/Http/Controllers/Controller.php
- backend/app/Models/User.php
に追記しています。
// メソッド名がキャメルケースになっていないコード
public function test_test()
{
}
差分比較についての補足
for FILE in `git diff-index --name-status $against -- | grep -E '^[AUM].*\.php$'| cut -c3-`; do
[AUM]
という部分について、そういう正規表現があるのかな?って思ったんですけど、そういうわけではなくて、
git diff-index --name-status HEAD
コマンドをを実行した時に表示される、ファイル名のリストの先頭に
$ git diff-index --name-status HEAD
M backend/app/Http/Controllers/Controller.php
M backend/app/Models/User.php
先頭にA or U or Mが表示されます。それぞれの意味は
- A 追加
- M 更新
- U 競合
ということらしい。
参考にしたサイト
http://lost-waldo.blogspot.com/
注意点
いろいろ参考になる記事が世の中にありますが、ポイントとしては
Git管理のルートディレクトリがどこか
を意識しないと、pre-commit内で実行するコマンドのパスが変わってくるので重要なんですね。
今回はDockerを使用していて、Laravelプロジェクトの1つ上の階層がルートになり、そのディレクトリに.git
ディレクトリが存在しています。
php-cs-fixerなどのライブラリを使用しているので、
backend/
vendor/bin/phpmdと書く必要があるということです。
(すぐ気づかないといけませんが。。。)
表示例
なんかいまいち見やすくならなかったかなぁ・・・
最後に
php-cs-fixerのコマンド実行のパス指定の注意点があったので、他につまづいている方がいましたら参考になればと思います。
また、他にもpre-commitフックに追加した方がいいチェックや
間違いなどがありましたらご指摘いただければと思います。