search
LoginSignup
20

More than 5 years have passed since last update.

posted at

updated at

Gitフックを仕込むgrunt-githooksについて紹介するよ

本投稿について

Gruntプラグインを紹介していくGrunt Plugins Advent Calendar 2013の12/1の投稿です。
さっき作ったばかりなので、まだすっきりさっぱりしたカレンダーになってますので、どうぞよろしくお願いします。
http://qiita.com/advent-calendar/2013/grunt-plugins

grunt-githooksとは

GitフックでGruntタスクを実行する仕込みをしてくれるGruntプラグインです。

https://npmjs.org/package/grunt-githooks
https://github.com/rhumaric/grunt-githooks

Gitフックとは

Gitではcommitやpush, rebase, checkoutなどいくつかの操作ができますが、それらの特定の操作を行ったときに、スクリプトを実行することができる仕組みがあり、それをGitフックと呼びます。

どんな種類のフックがあるのか

まずはソースコードをGitバージョン管理するために、git initで初期化してください。
% git init
すると、カレントディレクトリに.gitディレクトリが作られますが、その中の.git/hooksディレクトリにGitのクライアントサイドフックのサンプルが提供されています。

% tree -L 3 -a
.
|-- .git
|   |-- HEAD
|   |-- config
|   |-- description
|   |-- hooks
|   |   |-- applypatch-msg.sample
|   |   |-- commit-msg.sample
|   |   |-- post-update.sample
|   |   |-- pre-applypatch.sample
|   |   |-- pre-commit.sample
|   |   |-- pre-push.sample
|   |   |-- pre-rebase.sample
|   |   |-- prepare-commit-msg.sample
|   |   `-- update.sample
|   |-- info
|   |   `-- exclude
|   |-- objects
|   |   |-- info
|   |   `-- pack
|   `-- refs
|       |-- heads
|       `— tags

ファイル名を見ると想像できるように、pre-commit.samplecommitする直前にフックされるスクリプトのサンプル、pre-push.samplepushする直前にフックされるスクリプトのサンプル(Git 1.8.2以降対応)になります。
pre-commitpre-pushという.sampleを除いた名前のファイルを作ると、commitpushの各操作から呼ばれます。

導入

インストール

npm installでgrunt-githooksをインストールします。package.jsonのdevDependenciesに追加されるように--save-devオプションを忘れずに。
% npm install grunt-githooks --save-dev

Gruntfile.js

grunt-githooks タスクをロードするように追記し、githooksタスクを定義します。
以下はシェルスクリプトのテンプレートを使ったタスク定義の例です。
他にもtemplateにはNodeで定義できるnode_modules/grunt-githooks/templates/node.js.hbテンプレートを指定可能です。

module.exports = function(grunt) {
  grunt.initConfig({
    githooks: {
      options: {
        dest: '.git/hooks', //省略可
        hashbang: '#!/bin/sh',
        template: './node_modules/grunt-githooks/templates/shell.hb',
        startMarker: '## GRUNT-GRUNTHOOKS START',
        endMarker: '## GRUNT-GRUNTHOOKS END'
      },
      setup: {
        'pre-commit': 'git-pre-commit'
      }
    }
  });
  grunt.registerTask('git-pre-commit', [“jshint"]); // jshintタスクの定義は省略
  grunt.loadNpmTasks('grunt-githooks');
};  

実行

% grunt githooks
Running "githooks:setup" (githooks) task

Binding `git-pre-commit` to `pre-commit` Git hook.
OK

Done, without errors.


% tree -L 3 -a
.
|-- .git
|   |-- HEAD
|   |-- config
|   |-- description
|   |-- hooks
|   |   |-- applypatch-msg.sample
|   |   |-- commit-msg.sample
|   |   |-- post-update.sample
|   |   |-- pre-applypatch.sample
|   |   |-- pre-commit
|   |   |-- pre-commit.sample
|   |   |-- pre-push.sample
|   |   |-- pre-rebase.sample
|   |   |-- prepare-commit-msg.sample
|   |   `-- update.sample
|   |-- info
|   |   `-- exclude
|   |-- objects
|   |   |-- info
|   |   `-- pack
|   `-- refs
|       |-- heads
|       `— tags

すると.git/hooks/pre-commit ファイルが生成されています。

% less .git/hooks/pre-commit
#!/bin/sh
## GRUNT-GRUNTHOOKS START
(cd /Users/shoito/workspaces/gac2013 && grunt git-pre-commit)

## GRUNT-GRUNTHOOKS END

pre-commitフックが仕込まれたので、さっそくgit commitして動作確認してみます。

% git commit
Running "jshint:gruntfile" (jshint) task
>> 1 file lint free.

Running "jshint:lib_test" (jshint) task
Linting lib/htmlparser.js ...ERROR
[L65:C35] W116: Expected '===' and instead saw '=='.
        if ( html.indexOf("<!--") == 0 ) {
[L70:C15] W116: Expected '{' and instead saw 'handler'.

              handler.comment( html.substring( 4, index ) );

(略)
Warning: Task "jshint:lib_test" failed. Use --force to continue.


Aborted due to warnings.

このケースでは、git commitしたがpre-commitフックで呼ばれるGruntタスクのjshintタスクによるコーディングルールのチェックをパスできず、git commitが失敗に終わりました。
警告に従い、コードを修正した後に再度git commitしましょう。

まとめ

このように、grunt-githooksプラグインを使い、チーム内で共通のGitフックを仕込んでおくことができます。
例えば、pre-commitフックにコーディングルールのチェックやテストのGruntタスクが呼ばれるように仕込んでおくと「誰だよ、テスト通ってないコードをpushしたやつは!?」という事態が防止できます。

Tips

コーディングルールのチェックやテスト(軽いもの)のGruntタスクの実行はファイル変更の監視を行っているwatchタスクに仕込んでおいて、PhantomJS/CasperJSなどを使ったUIテストのタスク(重いもの)はpre-commitフックから呼び出すようにしておくなどすると良いと思います。

Q&A

Q. なぜ、githooksタスクからわざわざgit-pre-commitを呼ぶようにしているか?

    setup: {
      'pre-commit': 'git-pre-commit'
    } 
(略)
grunt.registerTask('git-pre-commit', ['jshint']); 

A. このようにしておくと、pre-commitタスクで実行したいGruntタスクに変更があっても、Gruntfile.jsの定義を更新すればよく、grunt githooksを実行して.git/hooks/pre-commitを更新する必要がないためです。この例のjshintだと軽すぎますが、うちではcasperjsが含まれた定義になってます。

Appendix

Grunt Plugins
http://gruntjs.com/plugins

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
20