git hooks を git 管理して monorepo でも動かせるように作ってみる
最近 husky を使っていたアプリケーションが monorepo に組み込まれ、husky の設定を見直さなければいけなくなってしまった。husky は monorepo サポートしているが、monorepo の root に husky をインストールしないといけないため、root に husky を devDependencies に入れただけの package.json を作らないといけないと思うと「それってスマートなんだろうか」と考えるようになってしまった。
そのため、「husky が git hooks を簡単に扱えるようにするものなんだったら、git hooks を自分で書けるようになったらいいんじゃないか」と思い、自分で git hooks を書いてみることにした。
試せる環境
GitHub - tyankatsu0105/sandbox-git-hooks
git hooks で使いたいファイルを git 管理する
.git/hooks
に決まった名前のファイルを置くと、git コマンドにフックしてそのファイル内の処理を動かしてくれる。Git - githooks Documentation
実行権限つけないと動かないので注意
しかし、.git
内は git 管理できないので、どこか別の場所で hooks ファイルを管理しないといけない。
cp
で.git/hooks
に移すことを前提でディレクトリを作るといい。
.
├── .git
└── .githooks
└── pre-commit
そして、.githooks
の中を実際に移動する script をこんな感じで置くといい。
#!/bin/sh
cd `dirname $0`
GIT_HOOKS_DIRECTORY='../.githooks/'
ORIGINAL_GIT_HOOKS_DIRECTORY='../.git/hooks/'
cp -f -r $GIT_HOOKS_DIRECTORY $ORIGINAL_GIT_HOOKS_DIRECTORY
chmod -R +x $ORIGINAL_GIT_HOOKS_DIRECTORY
ファイルの中の処理を書く
今回はpre-commit
の処理を書く
- 差分ファイルの名前をリストで取得
- リストを for で回す
- 差分ファイル名中のディレクトリ文字列で条件分岐
- 該当のディレクトリに移動して処理実行
- 処理でエラーが出たらその時点で処理中断する
- 処理実行はディレクトリ毎に一回だけ
#!/bin/sh
function my_error() {
echo ""
echo "🤦 Oops... There is an error..."
echo ""
exit 1
}
FILES=$(git diff --name-only HEAD)
ROOT=$(git rev-parse --show-toplevel)
IS_CHACKED_LIBS_UTILS=false
IS_CHACKED_APPS_CLIENT=false
for file in $FILES; do
cd $ROOT
case "$file" in
libs/utils/*)
if $IS_CHACKED_LIBS_UTILS; then continue
fi
IS_CHACKED_LIBS_UTILS=true
cd libs/utils
npm run git-hooks:pre-commit || my_error
;;
apps/client/*)
if $IS_CHACKED_APPS_CLIENT; then continue
fi
IS_CHACKED_APPS_CLIENT=true
cd apps/client
npm run git-hooks:pre-commit || my_error
;;
*)
;;
esac
done
shell script 構文に強くないので、もっとスマートに書きたい
感想
husky 便利だけど、monorepo にした途端めんどうになるので、husky をやめて自分たちで git hooks 処理を作って管理するようにすると npm 依存しなくて済むし、汎用的になるかもしれない。