本投稿について
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.sample
はcommitする直前にフックされるスクリプト
のサンプル、pre-push.sample
はpushする直前にフックされるスクリプト
のサンプル(Git 1.8.2以降対応)になります。
pre-commit
やpre-push
という.sample
を除いた名前のファイルを作ると、commit
やpush
の各操作から呼ばれます。
導入
インストール
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