はじめに
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を使えば、そのルールをコミット時にチェックすることが出来ます。
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
を使ってみます。
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スクリプト例です。
check_ntpd() {
if ntpdate -q ntp.nict.jp | grep step ; then
printf "\e[31m[Error]: 時刻がずれています\e[0m\n"
exit 1
fi
}
check_ntpd
コンフリクトの対応漏れ
コンフリクトが残った状態でコミットしてしまうことも良くあります。
下記スクリプトを仕込んでおくことで、コンフリクトが残っていた場合、該当箇所の前後3行を出してくれます。
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
とかでしょうか。
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スクリプト化しましょう。