npmモジュール`pre-commit`を利用してチーム全体で低品質なコードをコミットできないようにする

### 2019/03/04 追記

古い記事のため参照している人も少ないと思いますが、もしこの記事を参考に導入を考えているかたは、よりモダンかつ高機能な [husky](https://github.com/typicode/husky) を利用することをおすすめします。

### このエントリで解決できること

* テストやLintなどを通らない問題のあるコードがGit上でコミットされ、プッシュされることを防げるようになる
* 上記をチームメンバー全員にむけて共有する方法がわかる
* 実際にそのサンプルプロジェクトに触れて、一連のフローを理解できる

# はじめに
 
GitにはHookという機能があり、コミットやプッシュなどに合わせて、特定のシェルスクリプトを走らせることが可能となっています。

この機能を利用すると、作業の自動化などが出来るほか、最も効果的な用法として、「コミット前の機械的なコードチェック」が可能という点があります。

これは、Git Hookがもつ、「終了コードが1の場合は該当処理を取り消す」という機能を用いることによって、例えばテストが通らないコードや、そもそもテストコードを書いていないコードや、コーディング規約を守っておらず、Lintが通らないコードのコミットをさせないということができるということです。

チームで作業している時には、コードの品質やできるだけ一定以上で固定したいもの。
それらを解決できるのがGit Hookとなります。

しかしながらこのGit Hook、`.git`の管理下となるため、Git上にコミットできないため、他のチームメンバーと共有しづらいという問題を抱えています。

その問題を解決方法の一つとして、npmモジュール`pre-commit`が有効であったため、ご紹介します。

# 導入
### はじめに

まずはpre-commitの導入が必要です。
また、pre-commit時に走らせる何らかのテストも必要なので、今回は一番簡単に検証できるESLintを使ってみることにしました。

また、今回はpre-commit部以外はそこまで重要でないため、面倒な手順をスキップするためGitHubにてサンプルプロジェクトを用意しましたので、こちらをCloneして確認していくこととします。

[[potato4d/pre-commit-sample]](https://github.com/potato4d/pre-commit-sample)

### 環境

既にNode.js及びnpmが導入されている環境を対象としております。
また、今回こちらで実行している環境は以下となります。

* Node.js 6.5.0
* npm 3.10.3

### サンプルプロジェクトでの実行準備

まずはサンプルのプロジェクトを用意します。
`git clone`した後に、既に設定してあるnpmモジュールを一通り読み込んで、準備完了です。

```bash:shell
$ git clone https://github.com/potato4d/pre-commit-sample
$ cd pre-commit-sample
$ npm install
```

# 実際に問題のあるコードをコミットしてみる

準備ができたので、さっそく問題のあるコードをコミットしてみましょう。
今回のサンプルプロジェクトでは、ES2015以降を前提として、`var`の利用を禁止しています。

そのため、`var`を利用するとエラーが出るような仕組みになっているので、まずは既存の`const`を`var`に書き換えてみます。

```js:index.js
var Name = "Tom";

console.log(Name);
```

以上の状態でファイルを保存し、Gitにコミットしてみます。

```bash:shell
$ git add -A
$ git commit -m "エラーが出るかテスト"
```

結果は以下のようになり、コミットが行われていない場合は成功です。

```shell
/path/to/pre-commit-sample/index.js
  1:1  error  Unexpected var, use let or const instead  no-var

✖ 1 problem (1 error, 0 warnings)

pre-commit:
pre-commit: We've failed to pass the specified git pre-commit hooks as the `test`
pre-commit: hook returned an exit code (1). If you're feeling adventurous you can
pre-commit: skip the git pre-commit hooks by adding the following flags to your commit:
pre-commit:
pre-commit:   git commit -n (or --no-verify)
pre-commit:

pre-commit: This is ill-advised since the commit is broken.
pre-commit:
Potato4ds-Macbook:pre-commit-sample potato4d$
```

ちなみに実際の画面では色がついて表示されるはずです。

<img width="648" alt="スクリーンショット 2016-09-10 20.39.46.png" src="https://qiita-image-store.s3.amazonaws.com/0/75633/06078198-2609-ff3d-1227-c9136e59a0b1.png">

このように、設定さえ完了してしまえば、それだけでプロジェクトにとって望ましくないコードのコミットを制限することが出来ます。

# pre-commitの条件の設定方法

ここまでサンプルプロジェクトを利用してまずは実際の挙動を確認してもらいましたが、続いて設定の方法をご紹介します。

といっても設定方法は簡単で、`package.json`に追記するだけとなります。
`package.json`を開いた上で、末尾の以下の項目を確認してください。

```package.json
  "pre-commit": [
    "test"
  ]
}
```

この`pre-commit`上に定義されているスクリプト名が全て自動で実行される仕組みとなっているため、動かしたいスクリプト名を指定するだけの仕組みとなっています。

この時のスクリプト名はnpm scriptの名前となっており、実際に実行されるコマンドは`npm run $precommit-name`の形式となります。

サンプルでは、`eslint .`が動くように`scripts`側で定義していたため、そのとおりに動くこととなりました。

# まとめ

* pre-commitはGitがもつHookの仕組みであり、問題がある場合はコミットを中断することが出来る機能となる
* pre-commitモジュールを利用すると、チーム全体でコミット時の低品質コードの混入を防ぐ仕組みを共有できる
* pre-commitの定義に関しては、package.jsonで行い、任意のnpm scriptを実行できるようになる

# おわりに

pre-commitを行うモジュールやプラグインに関しては、みたところNodeに限らず、PHPやRubyでも提供されています。
個人的にはNode製のものが使い勝手がよく、利用していますが、言語を選ばない機能であるため、プロジェクトへの導入は難しくなさそうです。

pre-commitは最近、実際の現場でも導入し始めましたが、CI任せであった部分で、WIPのコードはたびたびコケていたのが解消されて、恩恵は十分にあるように感じました。

個人開発なら好き放題書いてしまってもなんとかなることも多いですが、チーム開発時には存分に効力を発揮してくれる存在となりそうです。