【小ネタ】huskyを使って工夫してみた

2023.07.07

こんにちは、高崎@アノテーション です。

はじめに

複数のメンバーで JavaScript による開発をしていて、他メンバーのライブラリアップデートにより本流が更新されたため自身の環境へ pull すると、パッケージファイル(package.json や package-lock.json)は更新されるものの、ライブラリを格納している node_modules が ignore 対象でそのままになっている場合、自身のローカル環境が上手く動作しないことがあります。

git pull するときは必ず npm ci(もしくは yarn)せよ

というルールで運用しても良いですが、今回は husky という githook(例えば commit する前にテストをする、といったことを自動で行う)を簡単に行えるツールを利用して、自身が作業している環境に対して git pull 時に一緒にライブラリもアップデートする仕組みを実装してみました。

husky のインストールとセットアップ

husky は npm によるインストール以外に、npx によるインストールを行わないとセットアップできませんので以下のコマンドを実行します。

$ npm i -D husky 
$ npx husky install

2 つを実行することで以下のようなファイル構成が出来るかと思います。

¥
├ .husky
│ ├ _
│ │ ├ .gitignore
│ │ └ husky.sh
  :

.huskyディレクトリの配下に_(アンダーバー)を記したディレクトリと、その配下に.gitignore(配下全ファイルを ignore 対象に定義している)とhusky.sh(フックシェル)を生成します。

また、上記は ignore 対象ですので clone 時に出来ないため package.json に下記を定義しておいて最初に npm i する際の仕掛けを作っておく必要があります。

package.json

 :
  "scripts": {
    "prepare": "husky install"
 :

pull を拾う仕掛けを作る

さて、pull はpost-mergeのイベントになりますので、下記を実行して.huskyディレクトリ配下にpost-mergeというファイルを生成します。

$ npx husky add .husky/post-merge "npm ci"

実行することによって出来上がるファイルは下記で、自動で実行属性が付加されます。

.husky/post-merge

#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npm ci

これで、git pull 時にマージが走るとインストールを再度行うことになります。

もう少し手を加える

さて、このままですとマージが走ると無条件でインストールを行うようになり、あまり経済的ではありません。

ライブラリアップデートの作業は下記のような流れになります。

必要なアップデートが入れば package.json が変わる
→合わせて package-lock.json も変わる
→その状態で git に push される

すなわち、アップデートの確認は package-lock.json の変化を見れば良いことになりますので、post-merge のスクリプトを下記のように修正します(ハイライトされた箇所です)。

post-merge

#!/use/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

# pull される前後を比較し、package-lock.json の変更があれば npm ci を行う
(git diff @{1} HEAD --name-only | grep "package-lock.json" > /dev/null) && npm ci || echo "No need npm install operation."

これで、pull された際 package-lock.json が変わればライブラリのインストール整理を行うようになります。

npm ci ではなく yarn を使われている場合は適宜修正してください。

修正箇所と husky.sh との関係について

変更した箇所は1行で書きましたが、下記のように複数行に分けて書くと思った通り動作しません。

post-merge

# pull される前後を比較し、package-lock.json の変更があれば npm ci を行う
git diff @{1} HEAD --name-only | grep "package-lock.json" > /dev/null
if [ $? = 0 ]; then
  npm ci
else
  echo "No need npm install operation."
fi

このように書くと、差分が無い場合
No need npm install operation.
は出ず
husky - post-merge hook exited with code 1 (error)
のように出ます。

これはフックシェルである husky.sh(下記)の 24 行目が影響しています。

  :
  readonly husky_skip_init=1
  export husky_skip_init
  sh -e "$0" "$@"
  exitCode="$?"

  if [ $exitCode != 0 ]; then
    echo "husky - $hook_name hook exited with code $exitCode (error)"
  fi
  :

sh -e指定で実行することで、処理の結果が0以外になると即終了、すなわち post-merge の 5 行目で 1 が返されるのでその後処理しません。

ですので、カスタマイズされる場合は 常時 0 を返せるよう工夫して記載する必要がありますのでご留意ください。

おわりに

今回は husky を用いたライブラリを自動で整理し直すやり方を実践してみました。

ちなみに、環境変数HUSKYを用意して0を設定することでこの自動化をスキップすることが出来ます。

手動で git pull / npm ci を行いたい場合はご参考まで。

アノテーション株式会社について

アノテーション株式会社は、クラスメソッド社のグループ企業として「オペレーション・エクセレンス」を担える企業を目指してチャレンジを続けています。
「らしく働く、らしく生きる」のスローガンを掲げ、様々な背景をもつ多様なメンバーが自由度の高い働き方を通してお客様へサービスを提供し続けてきました。
現在当社では一緒に会社を盛り上げていただけるメンバーを募集中です。
少しでもご興味あれば、アノテーション株式会社WEBサイト をご覧ください。