🙌

Git hooks を使ってコミットのたびに go test してもらう

2023/06/06に公開

先に

ざっくり以下の手順でOKです。

1. Git hooks の参照先ディレクトリを変更する

git config --local core.hooksPath .githooks

2. pre-commitを書く

.githooks/pre-commit
#!/bin/sh

# Run go test
output=$(go test -v ./...) 
exit_code=$?

# Check if the test failed
if [ $exit_code -ne 0 ]; then
  echo "go test failed:"
  echo "$output"
  exit 1 # Return Error Code
fi

# Output succeeded
echo "$output"

exit 0

3. pre-commitに実行権限を与える

chmod a+x ./.githooks/pre-commit

なにか書いてコミットしてみる

テストが無い場合
$ git add calc/num.go 
vscode ➜ /workspace (main) $ git commit -m "multiplyを追加"
?       go-unittest     [no test files]
?       go-unittest/calc        [no test files]
[main 80d05b3] multiplyを追加
 1 file changed, 5 insertions(+), 1 deletion(-)
テストを追加してみた
$ git add calc/num_test.go 
vscode ➜ /workspace (main) $ git commit -m "add num_test"
?       go-unittest     [no test files]
=== RUN   TestAdd
--- PASS: TestAdd (0.00s)
PASS
ok      go-unittest/calc        (cached)
[main fbb1309] add num_test
 1 file changed, 19 insertions(+)
 create mode 100644 calc/num_test.go
テストがfailした例
git add calc/num_test.go 
vscode ➜ /workspace (main) $ git commit -m "mod num_test"
go test failed:
?       go-unittest     [no test files]
=== RUN   TestAdd
    num_test.go:18: 
                Error Trace:    /workspace/calc/num_test.go:18
                Error:          Not equal: 
                                expected: 8
                                actual  : 7
                Test:           TestAdd
--- FAIL: TestAdd (0.00s)
FAIL
FAIL    go-unittest/calc        0.003s
FAIL

$ git status
On branch main
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   calc/num_test.go

テストにfailするとcommit処理が止まるため、直後のgit statusでcommitされていないことがわかります。

説明

今回の手順のポイントを説明します。

.git/hooksはgit管理できない

Git hooks の機能はデフォルトではそのリポジトリの.git/hooks下を参照するようになっています。お手元にリポジトリがあるなら見ていただければと思いますが、hooks以下にはサンプルとして各hookのサンプルコードが格納されているので、例えば./git/hooks/pre-commit.sample./git/hooks/pre-commitとリネームし、中身をよしなに編集すればそのまま使うことができます。

しかしながら、.gitディレクトリの内容はgit管理することができないため、このままでは実行したいスクリプトそのものをバージョン管理することができません。

そこでgit config --local core.hooksPath .githooksというコマンドを使うと、そのリポジトリにおけるGit hooksの格納先を.githooksに変更することができます。[1]
これにより、スクリプト本体もリポジトリに含めることができ、バージョン管理も行えるようになります。

終了コードで処理を切り分ける

go testの結果は$output、終了コードは特殊変数$?に格納されます。$?を検査してgo testが成功したかどうかをチェックし、failしていたらexit 1でスクリプトを終了するようにします。
Git hooksのpre-commitは、終了コードが0以外だった場合はコミット処理を中止する仕組みになっています。

作ったhooksには実行権限を与える

作成したスクリプトファイルはそのままでは実行権限がないので、事前に実行権限を与えておきます。これを行わないとGit hooksはスクリプトファイルを実行することができず、権限不足で実行できないことは特にcommit時にも出力されないようです。[2]

まとめ

以上です!

今回はGit hooksのpre-commitを使って、コミットのたびにgo testを実行させる環境を作ってみましたが、スクリプトの内容次第でさまざまなことが行えますので、他の言語や開発環境にも応用できそうです。

ではまた!

おまけ

VSCodeのCommitボタンでもちゃんと発動します。

commit on vscode

「Open Git Log」や「Show Command Output」を押すと結果を確認することができます。

おまけ2

pre-commitをスルーしたい場合には--no-verifyオプションをつけてcommitしましょう。乱用は禁物です。

git hookしないという選択肢
git commit -m "testしないでいったんコミット" --no-verify

参考記事

脚注
  1. .githooksはお好みで任意のディレクトリ名にしていただいてOKです。 ↩︎

  2. 個人的にどハマりしました。 ↩︎

Discussion