Rust プロジェクトで自動で Git hook をセットできる cargo-husky をつくった
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 ライセンスで配布されています.