🕌

【React】huskyでコードフォーマットとコミットメッセージ生成を自動化する

2023/04/01に公開
tl;dr
pnpm add -D husky lint-staged eslint prettier eslint-config-prettier prettier-plugin-tailwindcss commitlint commitlint-config-gitmoji commitizen cz-customizable
pnpm exec husky install
pnpm exec husky add .husky/pre-commit 'pnpm exec lint-staged'
pnpm exec husky add .husky/commit-msg 'pnpm exec -- commitlint --edit $1'
pnpm exec husky add .husky/prepare-commit-msg "exec < /dev/tty && node_modules/.bin/cz --hook || true"
package.json
{
  "scripts": {
+    "prepare": "husky install"
  },
+  "lint-staged": {
+    "*.{js,jsx,ts,tsx,json,md}": [
+      "prettier --write"
+    ]
+  },
+  "config": {
+    "commitizen": {
+      "path": "cz-customizable"
+    }
+  }
}
prettier.config.js
module.exports = {
  plugins: [require("prettier-plugin-tailwindcss")],
};
commitlint.config.js
module.exports = {
  extends: ["gitmoji"]
};
.cz-config.js
module.exports = {
  types: [
    { name: "feat \t\t✨ 新機能追加", value: "✨ feat" },
    { name: "fix \t\t🐛 バグ修正", value: "🐛 fix" },
    {
      name: "test \t\t✅ テストの追加や既存テストの修正",
      value: "✅ test",
    },
    {
      name: "refactor \t🔨 バグ修正や新機能追加以外のコード修正",
      value: "🔨 refactor",
    },
    {
      name: "style \t💄 プログラムの動きに影響を与えない変更",
      value: "💄 style",
    },
    { name: "docs \t\t📝 ドキュメントのみの変更", value: "📝 docs" },
    {
      name: "perf \t\t⚡ パフォーマンス改善のためのコード修正",
      value: "⚡ perf",
    },
    {
      name: "chore \t🔧 ビルドプロセスやドキュメント生成のような補助ツールやライブラリの変更",
      value: "🔧 chore",
    },
  ],
  skipQuestions: ["body", "footer"],
  scopes: ["api", "ui"],
  allowCustomScopes: true,
  subjectLimit: 100,
};

はじめに

開発体験を向上させるために、Prettierなどのフォーマッタを導入したり、コミットメッセージのルールを設けたりすることがあると思います。
特にチームで開発している場合、統一したルールを設定したり、そのためのツールを導入することは必須と言えます。
しかし、せっかく設定したルールをついつい忘れてしまうことがあります。
その結果、フォーマット忘れによってコードに余計な差分が発生してしまったり、コミットメッセージの書き方がバラバラになったりします。
そこで、huskyを導入して統一的なGit hooksの環境を構築します。

前提

以下の開発環境に導入することを想定しています。

  • React
  • TypeScript
  • tailwindcss
  • pnpm

プロジェクトにGit hooksを導入する

Git hooksを設定すると、コミット時にフォーマッタをかけるといったことを自動的に行うことができます。
しかし、Git hooksの設定は.git/hooks/にインストールされるため、Gitで管理することができません。
このままではローカルにレポジトリをクローンするたびに手動でGit hooksの設定をする必要があります。

そこで、Git hooksを管理するためのライブラリhuskyをインストールします。

pnpm add -D husky

# npmの場合
npm install -D husky
# yarnの場合
yarn add --dev husky

インストールしたら、以下のコマンドでGit hooksを有効にします。

pnpm exec husky install

# npmの場合
npx husky inistall
# yarnの場合
yarn run husky install

レポジトリをローカルにクローンした際に自動でhuskyをインストールするように、package.jsonに設定を追加します。

package.json
{
  "scripts": {
+    "prepare": "husky install"
  }
}

// npmも同様
{
  "scripts": {
+    "prepare": "husky install"
  }
}
// yarnの場合
{
  "scripts": {
+    "postinstall": "husky install",
+    "prepack": "pinst --disable",
+    "postpack": "pinst --enable"
  }
}

これでチーム内でGit hooksの設定を共有することができるようになりました👏

コミット時に自動でコードをフォーマットするようにする

ステージされているファイルにフォーマッタをかけるために、lint-stagedをインストールします。

pnpm add -D lint-staged

次に、実際にlint-stagedで実行するリンタ(ESLint)やフォーマッタ(Prettier)をインストールします。

pnpm add -D eslint prettier eslint-config-prettier prettier-plugin-tailwindcss

その後、以下のコマンドで対話的にESLintの設定ファイル.eslintrc.*を作成します。

pnpm exec eslint --init

加えて、Prettierの設定ファイルを作成します。
とりあえずtailwindcssのためのプラグインを実行するための記述のみを書きます。

prettier.config.js
module.exports = {
  plugins: [require("prettier-plugin-tailwindcss")],
};

そして、lint-stagedで使用するフォーマッタと対象ファイルをpackage.jsonで指定します。

package.json
+  "lint-staged": {
+    "*.{js,jsx,ts,tsx,json,md}": [
+      "prettier --write"
+    ]
+  }

最後に、コミットの直前にlint-stagedを実行するように、huskyに設定を追加します。

pnpm exec husky add .husky/pre-commit 'pnpm exec lint-staged'

これで必ずフォーマット済みのコードをコミットできるようになりました👏

コミット時に自動でコミットメッセージの形式を検査する

コミットする直前にコミットメッセージが特定の形式になっているかどうかをチェックします。
今回は、コミットメッセージの接頭辞を絵文字にするgitmojiの設定をします。

まずは、commitlint本体とgitmojiの設定をインストールします。

pnpm add -D commitlint commitlint-config-gitmoji

次に、commitlintでgitmojiの設定を使うための設定ファイルを記述します。

commitlint.config.js
module.exports = {
  extends: ["gitmoji"]
};

最後に、コミット時にcommitlintを実行するように、huskyに設定を追加します。

pnpm exec husky add .husky/commit-msg 'pnpm exec -- commitlint --edit $1'

これでコミットメッセージのルールを遵守できるようになりました👏

対話的にコミットメッセージを書くようにする

commitlintを導入することでgitmojiの形式になっていることは保証できますが、コミットメッセージを書くたびにいちいちルールを思い出すのは骨が折れます。
そこで、対話的にコミットメッセージ書くようにし、確実にルールに沿ったコミットメッセージになるようにします。

まず、commitizenをインストールします。
設定をカスタマイズするために、cz-customizableも同時にインストールします。

pnpm add -D commitizen cz-customizable

package.jsonに記述を追加します。

package.json
+  "config": {
+    "commitizen": {
+      "path": "cz-customizable"
+    }
+  }

そして、commitizenにルールを追加します。
今回はgitmojiのためのルールになります。

.cz-config.js
module.exports = {
  types: [
    { name: "feat \t\t✨ 新機能追加", value: "✨ feat" },
    { name: "fix \t\t🐛 バグ修正", value: "🐛 fix" },
    {
      name: "test \t\t✅ テストの追加や既存テストの修正",
      value: "✅ test",
    },
    {
      name: "refactor \t🔨 バグ修正や新機能追加以外のコード修正",
      value: "🔨 refactor",
    },
    {
      name: "style \t💄 プログラムの動きに影響を与えない変更",
      value: "💄 style",
    },
    { name: "docs \t\t📝 ドキュメントのみの変更", value: "📝 docs" },
    {
      name: "perf \t\t⚡ パフォーマンス改善のためのコード修正",
      value: "⚡ perf",
    },
    {
      name: "chore \t🔧 ビルドプロセスやドキュメント生成のような補助ツールやライブラリの変更",
      value: "🔧 chore",
    },
  ],
  skipQuestions: ["body", "footer"],
  scopes: ["api", "ui"],
  allowCustomScopes: true,
  subjectLimit: 100,
};

最後に、コミット時にcommitizenを実行するように、huskyに設定を追加します。

pnpm exec husky add .husky/prepare-commit-msg "exec < /dev/tty && node_modules/.bin/cz --hook || true"

これでコミットメッセージのルールを覚えずに済むようになりました👏

最後に

開発体験を向上させるために、こうした環境構築はどんどんしていきたいです。

参考

https://dotengineerblog.net/how-to-enforce-the-commit-message-rule-for-everybody/

Discussion