Posted at

チームで共有しておきたい!オススメ githooks まとめ

More than 3 years have passed since last update.


はじめに

githooksをうまく使うことで、lintによる規約チェックやコミットメッセージのフォーマット確認など、チームメンバー全員のコード品質を担保したり、作業の漏れを防ぐことが出来ます。

というわけで、今回はチームメンバー全体で共有しておきたい、オススメのgithooksをまとめてみたいと思います。


注意点

基本的にbash前提で書いているので、 #!/bin/bash をgithooksの各ファイルの1行目に書いておいてください。

また、githooksは実行権限がないと動きません。

なので、ファイルを作った後は必ず権限を付与してあげてください。

vi .git/hooks/pre-commit

chmod +x .git/hooks/pre-commit

vi .git/hooks/commit-msg
chmod +x .git/hooks/commit-msg

それと、hookしたい処理はメソッド化しておき、メソッド呼び出しを下にまとめておいたほうがあとあと楽です。

(hookしたい処理のON/OFFやチェック順の変更が簡単にできるので)


メッセージフォーマット

コミットメッセージに [add] とか [fix] とか付けよう、というルールがあったとします。

githooksを使えば、そのルールをコミット時にチェックすることが出来ます。


.git/hooks/commit-msg

MSG=$(cat "$1")

check_message_format() {
local REGEX='^\[add|fix|update|remove\].+'

if [[ ! $MSG =~ $REGEX ]]; then
printf "\e[31m[Error]: コミットメッセージのフォーマットが正しくありません\e[0m\n"
exit 1
fi
}

check_message_format


ルールを変更したい場合は REGEX の正規表現を変えてあげるだけでOKです。


コーディング規約

コミットのタイミングでコーディング規約に違反していないかどうかチェックすることも出来ます。

今回はサンプルとしてRubyの規約チェックツール rubocop を使ってみます。


.git/hooks/pre-commit

check_rubocop() {

rubocop -v >/dev/null 2>&1 || { printf >&2 "\e[31m[Error]: rubocopが見つかりません\e[0m\n"; exit 1; }

local FILES="$(git diff --cached --name-only --diff-filter=AMCR | grep "\.rb$" | tr '\n' ' ')"
if [ -n "$FILES" ]; then
rubocop ${FILES}
if [ $? -ne 0 ]; then
printf "\e[31m[Error]: Rubyのコーディング規約に準拠していません\e[0m\n"
exit 1
fi
fi

}

check_rubocop


Pythonだったら pep8 を、JavaScriptだったら ESLint を使えば同等のことができると思います。


コミット時刻

テストでローカルPCの時刻を変更していて、うっかりコミットしてしまうとおかしな時刻になってしまいます。

これは、それを防ぐhookスクリプト例です。


.git/hooks/pre-commit

check_ntpd() {

if ntpdate -q ntp.nict.jp | grep step ; then
printf "\e[31m[Error]: 時刻がずれています\e[0m\n"
exit 1
fi
}

check_ntpd



コンフリクトの対応漏れ

コンフリクトが残った状態でコミットしてしまうことも良くあります。

下記スクリプトを仕込んでおくことで、コンフリクトが残っていた場合、該当箇所の前後3行を出してくれます。


.git/hooks/pre-commit

check_conflict() {

local FILES="$(git diff --cached --name-only --diff-filter=AMCR | tr '\n' ' ')"
local FILE
for FILE in $FILES; do
conflict=`grep -3 -E '(<<<<<<<|>>>>>>>)' $FILE | grep -v '^$'`
if [ -n "${conflict}" ]; then
printf "\e[31m[Error]: ${FILE} コンフリクトの対応が残っています\e[0m\n"
printf "${conflict}\n"
exit 1
fi
done

}

check_conflict



本番ビルド

つい本番用のビルドを忘れてしまうことがあります。

jenkinsなどのリリース手順に含めてしまうのが一番いいのですが、設定が面倒だったりするのであれば、代替としてhookスクリプトを使用するのでもいけます。

下記はRailsのcompile例です。webpackなら webpack -p とかでしょうか。


.git/hooks/pre-commit

compile_production() {

local FILES="$(git diff --cached --name-only --diff-filter=AMCR | grep "^app/assets")"
if [ -n "$FILES" ]; then
printf "Precompiling assets...\n"
bundle exec rake assets:precompile:all RAILS_ENV=production
git add public/assets/*
fi
}

compile_production


ちなみに、これまでのhookスクリプトでステージング上の差分ファイルを何回も取得してしまっているので、下記のようにグローバルで取得して使いまわすと良いかもしれませんね。

FILES="$(git diff --cached --name-only --diff-filter=AMCR)"


AWSアクセスキー

これはちょっと番外編になります。

AWSのアクセスキーを誤ってコミットしてしまわないようにする方法なのですが、githooksに書くやり方ではありません。

というのも、公式が出しているgit-secretsというのがあるので、それを使ったほうが良いからです。

最高に分かりやすくまとめてくれている方がいるので、こちらをご参考ください。


hookさせない

--no-verify オプションを付けると、pre-commit/commit-msgのhookスクリプトをスキップすることができます。

緊急時でどうしてもという場合のみ許可しましょう。

git commit --no-verify


まとめ

Git 2.9.0から core.hooksPath を使ってgithooksのパスを指定できるようになったということもあり、チーム間でもgithooksが共有していく流れがになるのではないかと感じています。(使い方として合ってますかね…?)

上記以外でもチームのルールとして強制できるものがあれば、どんどんhookスクリプト化しましょう。