俺のgithookはこうだ (commit/pushをカスタマイズ)
TL;DR
- ブランチごとにcommitできるユーザを制限する
- ブランチごとにpushできるユーザを制限する
- ブランチごとにcommit前にテストを実行する
- ブランチごとにpush前にテストを実行する
- ここにコード置いてます
前書き
gitを使ったバージョン管理皆さん使ってますよね?(無言の圧)
皆さんご存じの通り,githookを使うことで,コミット前に自動テストを実行できたりします.
しかし,個人的に欲しい機能を付けようとすると,都度githookについて調べて,hook設定して・・・って工程が毎回かかってたので,この際テンプレート化するかって事で作ってみました.
(何番煎じやねん)
だた,あくまで本記事で紹介しているテンプレート類は各個人のお行儀のもとに成り立たせています.
(コミット制限もローカルの設定ファイルを編集すれば変えられます)
そのため,厳密にコミットを制限したりしたい場合は,ご自身で調査いただければと思います.
ぱっと持ってきてさっと使いたいという方は是非ご参考ください.
前準備
過去の偉人がすでにいい感じの使い方について解説されていますが,本記事でも参考に利用させていただくことにします.
- hookスクリプトは
.githooks
というディレクトリに入れる (hookスクリプトもリポジトリで管理) - hookスクリプトの参照先を
.githooks
に切り替える - hoosスクリプト実行時に参照するユーザ名を
git config --local
で設定する
なお,工程2のhookスクリプト参照先の変更と工程3のユーザ名設定は下記コマンドで行います.
# hookスクリプトの参照先を設定します
git config --local core.hooksPath .githooks
# ユーザ名設定 (以降のアクセス制御時はこの設定名を参照します)
git config --local user.name "your-name"
注意点として,リモートに配置しているリポジトリをcloneしてもらう場合,clone後にhookスクリプトの参照先設定を切り替える必要があるので,そこは「お行儀」としてREADMEなり手順書等に書いておきましょう.
また,hookスクリプトの参照先設定時に"--local"でなく"--global"を設定するとややこしいことになるので,絶対に"--local"で設定しましょう.
ブランチごとにcommit/pushできるユーザを制限する
仕様
- ブランチごとにcommit/pushできるユーザを制限する
- ブランチの設定にワイルドカードを使える(例:feature-*)
- ユーザの設定にワイルドカードを使える(例:usr-*)
- 設定ファイルに記載されてるいずれにも該当しない場合はアクセス制御なしとして全ユーザがcommit/push可
認証用の設定
認証を行うための設定を書きます.
ワイルドカードを使える様にしている都合上,複数の設定が該当することがありますが,その場合は最初に該当した設定でアクセス制御します.
# masterブランチはcommit禁止 (merge-commitはできる)
[master]
# mainブランチはcommit禁止 (merge-commitはできる)
[main]
# developブランチはrootのみcommit可
[develop]
root
# パターン"feature-*"に当てはまるブランチの設定
# ユーザ名の指定に * を利用すると全てのユーザがcommit可になります
[feature-*]
*
# 上記に当てはまらないブランチへのcommit/pushをすべて禁止するには
# 下記の設定のコメントアウトを外す
# [*]
認証用のshellスクリプト
commit/pushで認証用スクリプトは共通化して利用します.
中身が気になる方はご覧ください.
ぱっと使いたいだけの方は読み飛ばしてください.
(shellベタ書きなのであんまりきれいじゃないですごめんね.)
コードを表示する
#!/bin/sh
usage(){
echo "$0 [username] [branch] [config file]"
echo "$? = 0: permission exist, 1: no permission or error"
}
if test $# -ne 3; then
usage;
exit 1
fi
username=`echo "$1" | sed 's/^ *\| *$//'`
branch=`echo "$2" | sed 's/^ *\| *$//'`
configfile=`echo "$3" | sed 's/^ *\| *$//'`
# 対象ブランチの設定があるか 0:なし, 1:あり
is_restricted=0
while read line
do
# 前後の空白文字・改行文字を除去
line=`echo "$line" | sed 's/^ *\| *$//'`
# 空行かチェック
if test -z "$line"; then
# 空行の為処理スキップ
continue
fi
# 読み込んだ行がコメント行か確認
echo "$line" | grep -v '^#.*' > /dev/null
if test $? -ne 0; then
# コメント行の為処理スキップ
continue
fi
section=`echo "$line" | sed -n -e 's/\[\(.\+\)\]/\1/p'`
if test -n "$section" ; then
# セクション名の場合
if test $is_restricted -ne 0; then
# 既にヒットする設定があったが次ブランチの設定に移ったので権限無し
exit 1
fi
case "$branch" in
$section)
is_restricted=1
;;
esac
elif test $is_restricted -ne 0; then
# チェック対象のブランチ
# 対象ブランチの設定に記述があるか確認
case "$username" in
$line)
exit 0;
;;
esac
fi
done < $configfile
# 設定ファイルに記述あれば 1 (権限無し), 記述無ければ 0 (権限あり)
exit $is_restricted
ブランチごとにcommit/pushできるユーザを制限する
仕様
- ブランチごとにcommit/push時にテストコマンドを実行する
- ブランチの設定にワイルドカードを使える(例:feature-*)
- 複数の設定に該当する場合は該当するすべての設定に記載のテストコマンドを実行する
テスト用の設定
テストを行うための設定を書きます.
ワイルドカードを使える様にしている都合上,複数の設定が該当することがありますが,その場合は該当するすべての設定に記載のテストコマンドを実行します.
commit時の設定ファイルとpush時の設定ファイルが別なので,「push時はunittestも合格してないとpushできないが,commitに関してはflake8でテスト合格してればOK」的なこともできます.
自作のテストを実行することもできます.
(終了コードが成功時0,失敗時1になるようなスクリプトを作って,設定ファイルに書けば動かします.)
以下push時に実行するテストの設定サンプル.
# flake8, unittestのテストが成功しなければコミット・プッシュ中断
[master]
flake8 ./
python -m unittest discover ./
[main]
flake8 ./
python -m unittest discover ./
[develop]
flake8 ./
python -m unittest discover ./
# feature-*ブランチは,flake8だけテスト
[feature-*]
flake8 ./
テスト用のshellスクリプト
commit/pushでテスト用スクリプトは共通化して利用します.
(アクセス制御の時と同様.)
中身が気になる方はご覧ください.
ぱっと使いたいだけの方は読み飛ばしてください.
(shellベタ書きなのであんまりきれいじゃないですごめんね.)
コードを表示する
#!/bin/sh
usage(){
echo "$0 [branch] [config file]"
echo "$? = 0: test success, 1: test failure"
}
if test $# -ne 2; then
usage;
exit 1
fi
branch=`echo "$1" | sed 's/^ *\| *$//'`
configfile=`echo "$2" | sed 's/^ *\| *$//'`
# 対象ブランチの設定か 0:対象外の設定, 1:対象の設定
is_target=0
working_section=""
while read line
do
# 前後の空白文字・改行文字を除去
line=`echo "$line" | sed 's/^ *\| *$//'`
# 空行かチェック
if test -z "$line"; then
# 空行の為処理スキップ
continue
fi
# 読み込んだ行がコメント行か確認
echo "$line" | grep -v '^#.*' > /dev/null
if test $? -ne 0; then
# コメント行の為処理スキップ
continue
fi
section=`echo "$line" | sed -n -e 's/\[\(.\+\)\]/\1/p'`
if test -n "$section" ; then
# セクション名の場合
case "$branch" in
$section)
working_section=$section
is_target=1
;;
*)
is_target=0
;;
esac
elif test $is_target -ne 0; then
# テスト対象のブランチ
eval $line > /dev/null
code=$?
if test $code -ne 0; then
# 異常終了した
echo "TEST FAIL: section=[\"$working_section\"] code=[\"${line}\"]"
exit $code
fi
fi
done < $configfile
# 全てのテストが完了 or テスト項目の設定無ければ正常終了 (push許可)
exit 0
hookスクリプト
具体的にpre-commit または pre-push を下記のように記述します.
(下記はpre-commitの例)
#!/bin/sh
# Redirect output to stderr.
exec 1>&2
# ブランチ名
branch=`git symbolic-ref HEAD | sed -e 's:^refs/heads/::'`
# 操作ユーザ名
user=`git config user.name`
# commit可能ユーザかチェック
./.githooks/authorize.sh $user $branch .githooks/commit_users.conf
if test $? -ne 0; then
# 権限なし
echo "No permission to commit [$branch] branch for $user."
exit 1
fi
./.githooks/pretest.sh $branch .githooks/commit_pretest.conf
if test $? -ne 0; then
# 何かしらのテストコマンドが終了コード0以外で終了
echo "Fail to pre-test on commit [$branch]."
exit 1
fi
exit 0
おわりに
ぱっと使えて便利と自称してます.
ただお行儀良くないと「全ブランチ全ユーザアクセスOK,push前テストなし」という設定もできちゃうので注意.
(ローカルで設定だけ書き換えられるともうどうしようもない)
あとhookスクリプトの参照先を変更しないと空気.