npm でよく使っている husky というツールがあります.これは npm install などを hook して Git hook を自動でセットしてくれるツールで,リモートにプッシュする前のチェックを強制してくれます.もちろん CI でもテストを回しているのですが,プッシュ前にもチェックすることによりケアレスミスに早く気付くことができます.
Rust でもこの仕組みを使いたかったのですが,そういうツールが無かった&つくれそうだったので cargo-husky というツールをつくりました.
基本的な使い方
cargo-husky パッケージを Cargo.toml の dev-dependencies に追加して cargo test を実行するだけです.
[dev-dependencies] cargo-husky = "1"
$ cargo test
cargo はそのパッケージが必要になった段階でパッケージをダウンロードします. dev-dependencies なので cargo build ではなく cargo test を実行する必要があります.大抵開発中にテストを一度は実行すると思うので,意識して cargo test を呼ぶ必要はなく,知らぬ間に Git hook がセットされているという意図です.
.git/hooks/ を見ると,こんな感じの pre-push スクリプトが置かれているはずです.
#!/bin/sh # # This hook was set by cargo-husky v1.0.0: https://github.com/rhysd/cargo-husky#readme # Generated by script /path/to/cargo-husky/build.rs # Output at /path/to/target/debug/build/cargo-husky-xxxxxx/out # set -e echo '+cargo test' cargo test
これによって git push 前に cargo test が自動で実行されるようになります.
フックをカスタマイズしたいとき
デフォルトでは pre-push で cargo test を実行するフックが置かれますが,cargo-husky パッケージの feature flag を使ってこの挙動を変えることができます.
cargo book にもある通り,feature flag を指定するには [dev-dependencies.cargo-husky] というセクションをつくります.
[dev-dependencies.cargo-husky] version = "1" default-features = false # デフォルトの挙動を無効化する features = ["precommit-hook", "run-cargo-test", "run-cargo-clippy"]
features の配列に指定している値が有効にする機能です.この例では「コミット前に実行する(precommit-hook)」,「cargo test を実行する(run-cargo-test)」,「cargo clippy を実行する(run-cargo-clippy)」機能を有効にすることにより,毎コミット前に cargo test と cargo clippy を実行する git hook pre-commit が生成されます.
利用可能な feature flag は下記の通りです.
| feature flag | 意味 | デフォルト値 |
|---|---|---|
prepush-hook |
pre-push hook を生成 |
有効 |
precommit-hook |
pre-commit hook を生成 |
無効 |
postmerge-hook |
post-merge hook を生成 |
無効 |
run-cargo-test |
hook で cargo test を実行 |
有効 |
run-cargo-clippy |
hook で cargo clippy を実行 |
無効 |
user-hooks |
次の章を参照 | 無効 |
すでに生成された hook がある場合は,一旦 .git/hooks/ 以下を削除してから cargo test を実行してパッケージを再コンパイルしてください.
さらにフックをカスタマイズしたいとき
feature flag は固定値しか記述できないため,「自前で用意したスクリプトを実行したい」「cargo に特定のオプションを渡して実行したい」といったカスタマイズはできません.
さらなるカスタマイズを可能にするために,user-hooks という feature flag が用意されています.
[dev-dependencies.cargo-husky] version = "1" default-features = false features = ["user-hooks"]
この flag が有効になっていると,cargo-husky は自前で git hook を生成せず,リポジトリ直下に置かれている .cargo-husky というディレクトリを探し,その中のスクリプトを代わりに .git/hooks に配置します.
your-repository/
├── .git
└── .cargo-husky
└── hooks
├── post-merge
└── pre-commit
例えばディレクトリ構成がこのようになっているとき, pre-commit および post-merge が .git/hooks 以下に置かれる対象になります.
cargo-husky は .git/hooks にスクリプトを置く際,ファイルの先頭にメタ情報(cargo-husky のバージョンなど)をヘッダとして挿入します.
その際,# 始まりを行コメントと想定しているため,それに準じた言語でスクリプトを書く必要があります.また,実行可能属性がついていないファイルはスクリプトとして認識せず無視します.なのでスクリプトには実行可能属性を付けておいてください(chmod +x).これは意図しないファイルが .git/hooks に置かれてしまわないようにするためです.
実装
husky は npm の install フックなどで Git hook をセットしますが,cargo にはそういった仕組みはありません.
代わりに cargo の build script 機能を濫用することで cargo-husky は実装されています. 本来は外部ライブラリのビルドなどを設定するためのbuild.rs内でフックをセットする処理を行っています.
cargo がビルド時に自動でセットする $OUT_DIR に設定されたディレクトリを元にプロジェクトの .git ディレクトリを特定するので,万一 $OUT_DIR がリポジトリの外になっているような特殊なケースでは動きません.
cargo-husky は Linux/macOS/Windows で stable チャンネルのツールチェーンを使ってテストされており(Linux と macOS は Travis CI,Windows は Appveyor),MIT ライセンスで配布されています.