Git hooksで(huskyと一緒に🐶)お手軽にコードの治安を守る

はじめに

こんにちは!menuフロントエンドエンジニアの林です。

本記事では、Git hooksで特定の処理を走らせる方法を、menuフロントエンド開発チームでも用いているhuskyというnpmパッケージと併せてご紹介します。

Git hooksによりコミットする前のファイルに対してのLintやコミットメッセージの規約チェックなどを行うことができますが、huskyを用いることでそれをより簡単に導入・管理することが出来ます!

Git hooks/huskyとは

Git hooksとはコミット・プッシュなどgitを実行中の特定のタイミングで決められた処理を行うためのプログラムです。 デフォルトでは、git initを行ったときに.git/hooksが作成されます。中身は以下のような感じです。

$ ls .git/hooks/
applypatch-msg.sample           commit-msg.sample               fsmonitor-watchman.sample       post-update.sample              
pre-applypatch.sample           pre-commit.sample               pre-merge-commit.sample         pre-push.sample                 
pre-rebase.sample               pre-receive.sample              prepare-commit-msg.sample       push-to-checkout.sample         update.sample

これらはサンプルファイルですが、.sampleを外せばファイル名通りのタイミングで処理が走ります。 pre-commitならコミットの前、pre-pushならプッシュの前、と言った要領です。これらのファイルに実行したい処理を記述します。

また、デフォルトのディレクト.git/hooksは基本的にGitで管理できません。 ここで設定した内容をGitで管理したい場合にはconfigのcore.hooksPathでこのhooksを別のディレクトリで管理する必要があります。

huskyは、このようなhooksの設定や管理をより簡単に行うためのツールです。

セットアップがコマンドひとつで終わる他、エラーが見やすくなる・いくつかのhusky独自の機能を使うことが出来ます。

git-scm.com

typicode.github.io

なお、ここから先のはhuskyのv6の使用を前提としています。huskyの使い方、導入方法について検索するとv4の情報が出てくることもありますのでご注意ください。

husky v4 と v6の違いについては開発者の方のブログに詳しく説明があります。

簡単に言うとhusky v4はcore.hooksPathがGitに導入される前のもので、husky v6はcore.hooksPathを導入することでv4以前でのパフォーマンスなどの課題を解決したものになります。

(ちなみにv5はほぼv6と同じですが、MITライセンスでないので商用での利用には注意が必要です。)

Git hooksをhuskyを使って簡単に導入する


バージョン情報

  • husky: 6.0.0
  • yarn: 1.22.4

yarnは勿論npmでも全く問題ありませんが、yarn v2になるとセットアップが少し変わってくるのでご注意ください。


では、Git hooksやhuskyについて概要をご説明したところで、実際に導入や使い方についてを解説します。

1 husky initでセットアップする

まず、以下を実行してみましょう。

$ npx husky-init && yarn

(お使いのパッケージマネージャーに適宜読み替えてください。)

... 実はこれだけで大体Git hooksの設定が完了してます!ここで既にcore.hooksPathが変更されており(デフォルトだと.husky/配下)、スクリプトを追加する準備は整っています。

あとはここにスクリプトを追加するだけなのですが、もう少し詳細に見てみましょう。 とりあえず導入を進めたい!という方はとばしても大丈夫です。

この状態で.huskyを開くと以下のようになっていると思います。

.husky
├── _
│   └── husky.sh
├── pre-commit
└── .gitignore

(少し見づらいですが、”_”というディレクトリの中にhusky.shが入っています。)

基本的にhusky.sh.gitignoreを触ることはありません。

pre-commitの中身を見てみると、以下のようになっています。

#!/bin/sh
. "$(dirname "$0")/_/husky.sh" 

npm test // ここに実行したい処理を書く

とりあえずでnpm testが入っていますが、この要領で実行したい処理を書けばその通りに実行されます。

このファイルの編集やpre-commit以外のフックについての説明は後ほど。。

次に、プロジェクトのpackage.jsonを見てみましょう。こちらも更新されています。

{
  "name": "hoge",
  "dependencies": {
    ...
  },
  "devDependencies": {
   ...
    "husky": "^6.0.0"
  },
  "scripts": {
    ...
    "prepare": "husky install"
  }
}

ここで、huskyのv6が無事入っていることの他に、"prepare": "husky install"が入っていることに気づくかと思います。

prepareここにある通りnpmのライフサイクルの一つです。

これがあることで、Gitでコードを共有している他のメンバーは特別な手順を踏むことなくhuskyにコードを守ってもらえます🐶

2 husky addして実行したい処理を設定する

では、実際に処理を記述していきます。

ここでは、huskyと相性がよく、セットで扱われることの多いcommitlintを使ってコミットメッセージの規約チェックを導入する手順について説明します。

と言ってもこちらも簡単な数ステップで済みます。

まず、以下を実行してみましょう。

yarn add -D @commitlint/cli @commitlint/config-conventional

echo "module.exports = {extends: ['@commitlint/config-conventional']}" > commitlint.config.js

1行目は見慣れたyarn addですね。ここで必要なパッケージを入れておきます。

2行目はcommitlintの設定ファイルを作っています。設定の内容については言及しないので、ここでは@commitlint/config-conventionalを採用しています。

お好みに応じて自由に変更してください。

ここまでで準備は終了です!いよいよフックの作成に入ります。

npx husky add .husky/commit-msg 'yarn commitlint --edit $1'

hookの作成は以上です。

add <ファイル> <実行したい処理>のように記載すればそれに合ったファイルが作成されます。

ちなみに$1はcommit-msgフックが受け取るパラメータで、コミットメッセージを受け取ります。

念の為、作成されたファイルの中身を確認してみましょう

#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

npx --no-install commitlint --edit $1

無事に入ってますね!では最後に、正しく動いてるか確認しましょう。

まずは、commitできないパターンから。

$ git commit -m "hogehoge"
⧗   input: hogehoge
✖   subject may not be empty [subject-empty]
✖   type may not be empty [type-empty]

✖   found 2 problems, 0 warnings
ⓘ   Get help: https://github.com/conventional-changelog/commitlint/#what-is-commitlint

ルールに反しているのでコミットに失敗しました。

では次に、ルールにしたがってコミットした時の挙動を確認します。

$ git commit -m "fix: hogehoge"
[master 1b495a5] fix: hogehoge
 1 file changed, 1 insertion(+)

無事にコミットが通りました!

このように、Git hooksとhuskyの力でリポジトリの治安を守ることができます。

huskyなしでも導入は可能ですが、hookをより簡単に扱える上他パッケージとの依存もなく、6kBと非常に軽いのでhooksの運用を考えるときは一緒にhuskyも入れると便利かと思います!

補足:v7について

実はこのブログを書いている途中、huskyのv7がリリースされました。

記事書き直しか、、、??と思いビクビクしながらリリースノートを開いたところ、以下のように書いてありました。

Improve .husky/ directory structure. .husky/.gitignore is now unnecessary and can be removed.

Improve error output (shorter)

Update husky-init CLI

Update husky-4-to-7 CLI

Drop Node 10 support

との事でした。試しにv7を自分で入れて確かめてみたところ、確かに.husky/.gitignoreが消えていました。(代わりに"_"の中に入っていました)

その他husky initした時に表示されるログが少し親切になるなど小さな差はいくつかありましたが、 リリースノートの通り、特に大きな機能面の差はないので、Node 10を使っている方以外は特に気にする必要はなさそうです。

終わりに

弊社では様々なポジションのエンジニアを募集しています。 まずはカジュアルにお話を、という形でも結構ですので是非気軽にご応募ください!

reazon.jp