Cakephp Git WEB開発 環境構築

【Cakephp4/Git】pre-commitを導入して、コミット時にphpcsチェックを自動化する方法

※本サイトはPR表記を含みます。

PHP CodeSniffer(phpcs) は、コードを一定の規約に沿って管理してくれるものでWEB開発においてとても重要な役割を担っています。

例えば一つのサービスを複数人で開発するとしても、同じコードの書き方を良い意味で強要されるので、PSRなどの規約に沿った品質の高いコードを担保できます。

 

■ pre-commit が必要な背景

コードを変更し、コミット前に手動で composer cs-check とすれば、同じ効果が得られるので別に自動チェックせずとも問題はないのですが、繰り返す作業は煩雑になりがちですし、あまり頻繁に出入りしないメンバーがコード修正などを行った場合とか、新規メンバーが参入した場合など、cs-check 自体をしないままコミットをしてしまう、という問題点が出てきます。

そこで、Gitのフック機能を利用して、コミット時に cs-check を自動実行するように設定しておけば、サービスの背景を知らない人が、コードの変更を行ったとしても、コード規約に沿っていなければ、コミット自体を弾くことができます。

結果的にチェック漏れは発生しないですし、保守性が高まります。

以下、その方法です。

■ pre-commit導入前提条件

  • composer などでphpcsはすでに導入されていること
  • 規約条件は phpcs.xml で設定されていること
  • composer cs-check で実行できる
  • Cakephp4での導入なので、laravelなどは別途パスなどを変更する必要ありかも

■ .git-hooks.sampleディレクトリ作成

.git-hooks.sample/pre-commit のサンプルディレクトリを作成しました。別に、直接 .git/hooks ディレクトリにぶち込んでも良いかとは思います。

このあたりは管理に対する自身のポリシーで良さそうです。

■ pre-commit内容

#!/bin/bash
# PHP CodeSniffer pre-commit hook for git
# https://github.com/ekino/git-hooks/blob/master/php/phpcs/pre-commit

# バックサイトでもフロントでも名前をご自由に
BACK_SITE="{YOUR URL}"
TMP_STAGING="${BACK_SITE}/.pre-commit"

# stolen from template file
if git rev-parse --verify HEAD > /dev/null
then
against=HEAD
else
# Initial commit: diff against an empty tree object
against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
fi

# this is the magic:
# retrieve all files in staging area that are added, modified or renamed
# but no deletions etc
FILES=$(git diff-index --name-only --cached --diff-filter=ACMR $against -- )

if [ "$FILES" == "" ]; then
exit 0
fi

# create temporary copy of staging area
if [ -e $TMP_STAGING ]; then
rm -rf $TMP_STAGING
fi
mkdir $TMP_STAGING

# match files against whitelist
FILES_TO_CHECK=""
PHPCS_FILE_PATTERN="\.php$"
for FILE in $FILES
do
echo "$FILE" | egrep "$PHPCS_FILE_PATTERN" | egrep -q "$BACK_SITE"
RETVAL=$?
if [ "$RETVAL" -eq "0" ]
then
FILES_TO_CHECK="$FILES_TO_CHECK ${FILE#*/}"
fi
done

if [ "$FILES_TO_CHECK" == "" ]; then
exit 0
fi

# Copy contents of staged version of files to temporary staging area
# because we only want the staged version that will be commited and not
# the version in the working directory
STAGED_FILES=""
for FILE in $FILES_TO_CHECK
do
ID=$(git diff-index --cached $against $BACK_SITE/$FILE | cut -d " " -f4)

# create staged version of file in temporary staging area with the same
# path as the original file so that the phpcs ignore filters can be applied
mkdir -p "$TMP_STAGING/$(dirname $FILE)"
git cat-file blob $ID > "$TMP_STAGING/$FILE"
STAGED_FILES="$STAGED_FILES $FILE"
done

OUTPUT=$(cd $BACK_SITE && vendor/bin/phpcs --standard=phpcs.xml --extensions=php $STAGED_FILES)
RETVAL=$?

# delete temporary copy of staging area
rm -rf $TMP_STAGING

if [ $RETVAL -ne 0 ]; then
echo -e "$OUTPUT"
echo -e "\e[31m[pre-commit] phpcs error\e[m"
fi

exit $RETVAL

ファイル名や拡張子の対応は別途環境により調整する必要があります。

against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 という部分は empty tree を示す特別なハッシュ値らしく、空の状態から最初のコミットを含む全ての差分を取得したい場合は決まり文句的にこれを使えばよい、ということらしいです。

参考URL: https://stackoverflow.com/questions/40883798/how-to-get-git-diff-of-the-first-commit

■ 作成したpre-commitを.git/hooks以下にぶち込む

作成した pre-commit ファイルを .git/hooks 以下にぶち込みます。この時、ファイルは実行可能であることを確認してください。

権限変更が必要な場合、 chmod 755 pre-commit で大丈夫かと。

■ 実際にコミットをして弾かれるか確認

コミットしようとして、弾かれたところ。「Gitログを開く」で確認します。

「出力」タグで以下のように cs-check エラーが表示されていた

 command
----------------------------------------------------------------------
FOUND 2 ERRORS AFFECTING 1 LINE
----------------------------------------------------------------------
46 | ERROR | [x] Expected 1 space(s) after IF keyword; 0 found
46 | ERROR | [x] Expected 1 space(s) after closing parenthesis;
| | found 0
----------------------------------------------------------------------
PHPCBF CAN FIX THE 2 MARKED SNIFF VIOLATIONS AUTOMATICALLY
----------------------------------------------------------------------

これで、指摘箇所を修正して、コミットが成功すれば、Gitのフック機能を利用して、コミット時に自動 cs-check が走る形になります。

 

-Cakephp, Git, WEB開発, 環境構築
-,