Commitlint
コミットlint設定とルールを使用して、Conventional Commitsフォーマットに対してコミットメッセージを検証します。
このスキルを使用するタイミング
以下の場合にこのスキルを使用します:
- リポジトリにcommitlintを設定する
- commitlintルールと共有可能設定を構成する
- pre-commitフックやCI/CDパイプラインにcommitlintを統合する
- commitlint設定からルールを抽出して、コミットメッセージ生成用のLLMプロンプトを生成する
- プログラムでコミットメッセージを検証する
- commitlint設定または検証エラーをトラブルシューティングする
- commitlintのルール構文と重大度レベルを理解する
- リポジトリ内のcommitlint設定ファイルを検出する
コア機能
設定の検出
Commitlintはcosmiconfigを使用して、以下の優先順位で設定ファイルを検索します:
専用設定ファイル:
.commitlintrc
.commitlintrc.json
.commitlintrc.yaml
.commitlintrc.yml
.commitlintrc.js
.commitlintrc.cjs
.commitlintrc.mjs
.commitlintrc.ts
.commitlintrc.cts
commitlint.config.js
commitlint.config.cjs
commitlint.config.mjs
commitlint.config.ts
commitlint.config.cts
パッケージファイル:
commitlintフィールドがあるpackage.jsoncommitlintフィールドがあるpackage.yaml(PNPM)
設定フォーマット
JavaScript ES Modules (推奨):
// commitlint.config.js または commitlint.config.mjs
export default {
extends: ['@commitlint/config-conventional'],
};
TypeScript:
// commitlint.config.ts
import type { UserConfig } from '@commitlint/types';
import { RuleConfigSeverity } from '@commitlint/types';
const config: UserConfig = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [RuleConfigSeverity.Error, 'always', ['feat', 'fix', 'docs']],
},
};
export default config;
JSON:
{
"extends": ["@commitlint/config-conventional"]
}
YAML:
extends:
- "@commitlint/config-conventional"
ルールの構成
ルールは配列として構成します:[level, applicability, value]
重大度レベル:
| レベル | 意味 | TypeScript Enum |
|---|---|---|
| 0 | 無効 | RuleConfigSeverity.Disabled |
| 1 | 警告 | RuleConfigSeverity.Warning |
| 2 | エラー | RuleConfigSeverity.Error |
適用性:
'always'- ルールが一致しなければならない'never'- ルールが一致してはならない
ルールの例:
rules: {
// タイプが列挙されていない場合エラー
'type-enum': [2, 'always', ['feat', 'fix', 'docs', 'style', 'refactor', 'perf', 'test', 'build', 'ci', 'chore', 'revert']],
// タイプが空の場合エラー
'type-empty': [2, 'never'],
// 件名が空の場合エラー
'subject-empty': [2, 'never'],
// ヘッダーが100文字を超える場合エラー
'header-max-length': [2, 'always', 100],
// 件名がピリオドで終わる場合エラー
'subject-full-stop': [2, 'never', '.'],
// ボディの前に空白行がない場合警告
'body-leading-blank': [1, 'always'],
}
一般的な構成
@commitlint/config-conventional
最も広く使われている共有可能設定。デフォルトのエラーレベルルール:
| ルール | 構成 | 合格例 | 不合格例 |
|---|---|---|---|
type-enum | ['build', 'chore', 'ci', 'docs', 'feat', 'fix', 'perf', 'refactor', 'revert', 'style', 'test'] | fix: message | foo: message |
type-case | 'lowerCase' | fix: message | FIX: message |
type-empty | never | fix: message | : message |
subject-case | never + ['sentence-case', 'start-case', 'pascal-case', 'upper-case'] | fix: some message | fix: Some Message |
subject-empty | never | fix: message | fix: |
subject-full-stop | never, '.' | fix: message | fix: message. |
header-max-length | 100 | 短いヘッダー | ヘッダー > 100文字 |
body-leading-blank | always (警告) | ボディの前に空白行 | 空白行なし |
body-max-line-length | 100 | 行 <= 100文字 | 行 > 100文字 |
footer-leading-blank | always (警告) | フッターの前に空白行 | 空白行なし |
footer-max-line-length | 100 | 行 <= 100文字 | 行 > 100文字 |
完全な構成スキーマ
// commitlint.config.js
export default {
// 共有可能設定を拡張(ノード解決で解決)
extends: ['@commitlint/config-conventional'],
// コミットメッセージ解析用のパーサープリセット
parserPreset: 'conventional-changelog-atom',
// 出力フォーマッター
formatter: '@commitlint/format',
// カスタムルール(継承したルールを上書き)
rules: {
'type-enum': [2, 'always', ['feat', 'fix', 'docs', 'style', 'refactor', 'test', 'chore']],
},
// 特定のコミットを無視するための真を返す関数
// デフォルトの無視(マージコミット、リバート、セマバータグ)とマージ
ignores: [(commit) => commit.includes('WIP')],
// デフォルトの無視パターンを使用するかどうか
// デフォルトパターン: 'Merge pull request', 'Revert X', 'v1.2.3' など
defaultIgnores: true,
// 失敗時に表示されるカスタムヘルプURL
helpUrl: 'https://example.com/commit-guidelines',
// プロンプト設定(@commitlint/cz-commitlint用)
prompt: {
messages: {},
questions: {
type: {
description: '変更のタイプを選択してください:',
},
},
},
};
CLIの使用方法
インストール
# npm
npm install -D @commitlint/cli @commitlint/config-conventional
# yarn
yarn add -D @commitlint/cli @commitlint/config-conventional
# pnpm
pnpm add -D @commitlint/cli @commitlint/config-conventional
一般的なコマンド
# 最後のコミットをlint
npx commitlint --last
# コミット範囲をlint
npx commitlint --from HEAD~5
# 特定のコミットからlint
npx commitlint --from abc1234
# 標準入力からメッセージをlint
echo "feat: add feature" | npx commitlint
# ファイルからメッセージをlint
npx commitlint < .git/COMMIT_EDITMSG
# 編集フラグでlint(.git/COMMIT_EDITMSGを読み取る)
npx commitlint --edit
# カスタム設定パスでlint
npx commitlint --config ./custom-commitlint.config.js
# 解決された設定を表示
npx commitlint --print-config
# 厳格モード(警告が終了コード2になる)
npx commitlint --last --strict
終了コード
| コード | 意味 |
|---|---|
| 0 | 成功(エラーなし) |
| 1 | lintエラーが見つかった |
| 2 | 警告が見つかった(厳格モードのみ) |
| 3 | エラーが見つかった(厳格モード) |
| 9 | 設定ファイルが見つからない(-gフラグ使用時) |
ルールリファレンス
タイプルール
| ルール | 条件 | デフォルト適用性 |
|---|---|---|
type-enum | typeが値に含まれている | always |
type-case | typeがvalueの大文字小文字ケース | always, 'lower-case' |
type-empty | typeが空 | never |
type-max-length | typeがvalue以下の文字数 | always, Infinity |
type-min-length | typeがvalue以上の文字数 | always, 0 |
件名ルール
| ルール | 条件 | デフォルト適用性 |
|---|---|---|
subject-case | subjectがvalueの大文字小文字ケース | always |
subject-empty | subjectが空 | never |
subject-full-stop | subjectがvalueで終わる | never, '.' |
subject-max-length | subjectがvalue以下の文字数 | always, Infinity |
subject-min-length | subjectがvalue以上の文字数 | always, 0 |
subject-exclamation-mark | subjectに:の前に感嘆符がある | never |
スコープルール
| ルール | 条件 | デフォルト適用性 |
|---|---|---|
scope-enum | scopeが値に含まれている | always, [] |
scope-case | scopeがvalueの大文字小文字ケース | always, 'lower-case' |
scope-empty | scopeが空 | never |
scope-max-length | scopeがvalue以下の文字数 | always, Infinity |
scope-min-length | scopeがvalue以上の文字数 | always, 0 |
ヘッダールール
| ルール | 条件 | デフォルト適用性 |
|---|---|---|
header-case | headerがvalueの大文字小文字ケース | always, 'lower-case' |
header-full-stop | headerがvalueで終わる | never, '.' |
header-max-length | headerがvalue以下の文字数 | always, 72 |
header-min-length | headerがvalue以上の文字数 | always, 0 |
header-trim | headerに先頭/末尾の空白がない | always |
ボディルール
| ルール | 条件 | デフォルト適用性 |
|---|---|---|
body-leading-blank | bodyが空白行で始まる | always |
body-empty | bodyが空 | never |
body-max-length | bodyがvalue以下の文字数 | always, Infinity |
body-max-line-length | bodyの行がvalue以下の文字数(URLを除く) | always, Infinity |
body-min-length | bodyがvalue以上の文字数 | always, 0 |
body-case | bodyがvalueの大文字小文字ケース | always, 'lower-case' |
body-full-stop | bodyがvalueで終わる | never, '.' |
フッタールール
| ルール | 条件 | デフォルト適用性 |
|---|---|---|
footer-leading-blank | footerが空白行で始まる | always |
footer-empty | footerが空 | never |
footer-max-length | footerがvalue以下の文字数 | always, Infinity |
footer-max-line-length | footerの行がvalue以下の文字数 | always, Infinity |
footer-min-length | footerがvalue以上の文字数 | always, 0 |
大文字小文字の値
大文字小文字をチェックするルール (*-case) の場合:
[
'lower-case', // 小文字
'upper-case', // 大文字
'camel-case', // キャメルケース
'kebab-case', // ケバブケース
'pascal-case', // パスカルケース
'sentence-case', // センテンスケース
'snake-case', // スネークケース
'start-case', // スタートケース
]
プログラムでの使用
設定の読み込み
import load from '@commitlint/load';
async function getCommitlintConfig() {
const config = await load();
console.log(config.rules);
return config;
}
メッセージの検証
import load from '@commitlint/load';
import lint from '@commitlint/lint';
async function validateMessage(message) {
const config = await load();
const result = await lint(message, config.rules);
return {
valid: result.valid,
errors: result.errors,
warnings: result.warnings,
};
}
LLM統合パターン
プロンプト生成のためのルール抽出
commitlint設定からLLMフレンドリーな制約を生成:
def extract_rules_for_prompt(config: dict) -> str:
"""commitlintルールをLLMフレンドリーな形式に抽出します。"""
rules = config.get("rules", {})
prompt_parts = []
# type-enumが存在する場合
if "type-enum" in rules:
level, applicability, types = rules["type-enum"]
if level > 0 and applicability == "always":
prompt_parts.append(f"許可されるコミットタイプ: {', '.join(types)}")
# scope-enumが存在する場合
if "scope-enum" in rules:
level, applicability, scopes = rules["scope-enum"]
if level > 0 and applicability == "always":
prompt_parts.append(f"許可されるスコープ: {', '.join(scopes)}")
# header-max-lengthが存在する場合
if "header-max-length" in rules:
level, applicability, max_len = rules["header-max-length"]
if level > 0 and applicability == "always":
prompt_parts.append(f"コミットヘッダーの最大長: {max_len}文字")
# subject-caseが存在する場合
if "subject-case" in rules:
level, applicability, cases = rules["subject-case"]
if level > 0:
if applicability == "always":
prompt_parts.append(f"件名は次のいずれかのケースである必要があります: {', '.join(cases)}")
elif applicability == "never":
prompt_parts.append(f"件名は次のいずれのケースでもあってはいけません: {', '.join(cases)}")
if not prompt_parts:
return "このリポジトリでは、定義済みのコミットルール制約は見つかりませんでした。"
return "\n".join(prompt_parts)
生成されたコミットメッセージの検証
import subprocess
import json
def validate_commit_message(message: str) -> dict:
"""commitlintを使用してコミットメッセージを検証します。"""
result = subprocess.run(
["npx", "commitlint"],
input=message,
text=True,
capture_output=True
)
return {
"valid": result.returncode == 0,
"errors": result.stdout.split("\n") if result.stdout else []
}
ベストプラクティス
設定構成
- 継承から始める:
@commitlint/config-conventionalをベースとして使用 - ルールを選択的に上書き: 特定の要件に合わせてルールをカスタマイズ
- 設定をバージョン管理する: チーム全体で一貫性を確保
- CI/CDで検証: 開発ワークフローの一部としてコミットlintを実行
チーム統合
# Huskyを使用したpre-commitフック
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit $1'
# またはnpm pkg setを使用(Husky 9+)
npm pkg set scripts.prepare="husky"
echo "npx --no -- commitlint --edit $1" > .husky/commit-msg
CI/CD設定
GitHub Actions:
name: Commitlint
on: [pull_request]
jobs:
commitlint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: actions/setup-node@v3
- run: npm install -D @commitlint/cli @commitlint/config-conventional
- run: npx commitlint --from ${{ github.event.pull_request.base.sha }} --to ${{ github.event.pull_request.head.sha }} --verbose
CircleCI:
version: 2.1
jobs:
commitlint:
docker:
- image: cimg/node:lts
steps:
- checkout
- run:
name: Install dependencies
command: npm install -D @commitlint/cli @commitlint/config-conventional
- run:
name: Validate commits
command: npx commitlint --from HEAD~$CIRCLE_COMMIT_RANGE_COUNT --verbose
トラブルシューティング
| 問題 | 解決策 |
|---|---|
| 設定が見つからない | --print-configを使用して解決された設定を確認 |
| ルールが無視される | 複数の設定ファイルをマージする extendsの動作を理解 |
| 予期しない検証結果 | ルールのlevel(重大度)が正しく設定されているか確認 |
| フォーマットエラー | 設定ファイル構文(JSON vs YAML vs JS)を検証 |
| スコープの問題 | scope-enumが[]に設定されていると、空のスコープまたはスコープなしのみ許可 |