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


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

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

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

また、githooksは実行権限がないと動きません。
なので、ファイルを作った後は必ず権限を付与してあげてください。

```bash
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を使えば、そのルールをコミット時にチェックすることが出来ます。

```bash:.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` を使ってみます。

```bash:.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スクリプト例です。

```bash:.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行を出してくれます。

```bash:.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` とかでしょうか。

```bash:.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スクリプトでステージング上の差分ファイルを何回も取得してしまっているので、下記のようにグローバルで取得して使いまわすと良いかもしれませんね。

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


## AWSアクセスキー
これはちょっと番外編になります。
AWSのアクセスキーを誤ってコミットしてしまわないようにする方法なのですが、githooksに書くやり方ではありません。
というのも、公式が出している[git-secrets](https://github.com/awslabs/git-secrets)というのがあるので、それを使ったほうが良いからです。

最高に分かりやすくまとめてくれている方がいるので、[こちら](https://saku.io/preventing-aws-credential-leaks/)をご参考ください。

## hookさせない
`--no-verify` オプションを付けると、pre-commit/commit-msgのhookスクリプトをスキップすることができます。
緊急時でどうしてもという場合のみ許可しましょう。

```bash
git commit --no-verify
```

## まとめ
Git 2.9.0から `core.hooksPath` を使ってgithooksのパスを指定できるようになったということもあり、チーム間でもgithooksが共有していく流れがになるのではないかと感じています。（使い方として合ってますかね…？）
上記以外でもチームのルールとして強制できるものがあれば、どんどんhookスクリプト化しましょう。