GitLabにpushするときコミットメッセージをチェックする

2018/01/12

RedmineのチケットとGitのコミットを紐づけるために、コミットメッセージにrefs #チケット番号を入れることをルール化したい。

クライアントサイドフックであるpre-commit hookを使ってチェックできるが、各リポジトリの.git/hooks/pre-commitにフックを記載しなければいけなく、リポジトリごとに各人でそれぞれ対応しなければいけない。他のメンバーへの展開は漏れが出てくる可能性があるので、個人でpre-commit hookを設定する以外にもサーバサイドフックでチェックするようにしたい。
公式サイトでもサーバサイドフックは「システム管理者がプロジェクトのポリシーを強制させるために使うもの」と書かれている。

pushされたときにコミットメッセージを確認するにはpre-receive hookを使う。通常であればサーバの.git/hooksにpre-receiveを書くが、GitLabの場合は.git/custom_hooks以下に書くことになっている。(GitLabのCustom Git Hooks

pre-receiveの中は次のように作成した。

pre-receiveはプッシュされた直後に1回だけ呼び出され、標準入力から次のようなフォーマット(更新前のコミットID 更新後のコミットID ブランチの参照情報)で渡される。

ブランチごとに渡されるようなので全体をwhileループで囲んでいるが、主要な処理はgit log --oneline --format=%s "$oldrev".."$newrev" | grep -v '^Merge branch.*' | awk '{if (! / refs #[0-9]+$/ ) exit 1}'の部分となる。

Redmine番号の記載はコミットメッセージのどの部分にあっても紐づけができるのだが、コミットログ一覧を見たときの見やすさのため、ルールとして概要を記載するコミットメッセージ1行目の行末にRedmine番号を記載させるようにする。git log --onelineでコミットメッセージの1行目を取り出し、format=%sでコミットメッセージ概要部分だけに絞って出力している。
取り出すコミットの範囲は$oldrevから$newrevとすると、今回のプッシュに含まれる全コミットが取得できる。
取り出した全コミットをひとつずつ見ていく。行単位に処理をするのでawkを使うと便利。refs #チケット番号の前後に文字列がくっついてはいけないので、正規表現を refs #[0-9]+$(一番初めがスペース)とし、この正規表現にマッチしない行が出てきた時点でawkからexit 1する。
awkの終了ステータスを確認して1であればpre-receive自体の終了ステータスを1としてexitしてあげると、プッシュが中止される。

その他の細かい制御として、ブランチを作るときにもpre-receiveが走るのでその場合はチェックをしないようにしたり、ブランチからmasterへマージするときのデフォルトのメッセージ'Merge branch ... into master'となっているコミットはチェック対象からgrep -vで省いたりしている。

-Linux
-