13 KiB
13 KiB
PR Auto Update
概要
Pull Request の説明とラベルを自動的に更新するコマンドです。Git の変更内容を分析して、適切な説明文とラベルを生成・設定します。
使い方
/pr-auto-update [オプション] [PR 番号]
オプション
--pr <番号>: 対象の PR 番号を指定 (省略時は現在のブランチから自動検出)--description-only: 説明文のみ更新 (ラベルは変更しない)--labels-only: ラベルのみ更新 (説明文は変更しない)--dry-run: 実際の更新は行わず、生成される内容のみ表示--lang <言語>: 言語を指定 (ja, en)
基本例
# 現在のブランチの PR を自動更新
/pr-auto-update
# 特定の PR を更新
/pr-auto-update --pr 1234
# 説明文のみ更新
/pr-auto-update --description-only
# ドライランで確認
/pr-auto-update --dry-run
機能詳細
1. PR の自動検出
現在のブランチから対応する PR を自動検出:
# ブランチから PR を検索
gh pr list --head $(git branch --show-current) --json number,title,url
2. 変更内容の分析
以下の情報を収集・分析:
- ファイル変更: 追加・削除・変更されたファイル
- コード分析: import 文、関数定義、クラス定義の変更
- テスト: テストファイルの有無と内容
- ドキュメント: README、docs の更新
- 設定: package.json、pubspec.yaml、設定ファイルの変更
- CI/CD: GitHub Actions、workflow の変更
3. 説明文の自動生成
テンプレート処理の優先順位
- 既存の PR 説明: 既に記述されている内容を完全に踏襲
- プロジェクトテンプレート:
.github/PULL_REQUEST_TEMPLATE.mdから構造を取得 - デフォルトテンプレート: 上記が存在しない場合のフォールバック
既存内容の保持ルール
重要: 既存の内容は変更しない
- 書かれているセクションは保持
- 空のセクションのみ補完
- 機能的なコメント (Copilot review rule など) は保持
プロジェクトテンプレートの使用
# .github/PULL_REQUEST_TEMPLATE.md の構造を解析
parse_template_structure() {
local template_file="$1"
if [ -f "$template_file" ]; then
# セクション構造を抽出
grep -E '^##|^###' "$template_file"
# コメントプレースホルダーを特定
grep -E '<!--.*-->' "$template_file"
# 既存のテンプレート構造を完全に踏襲
cat "$template_file"
fi
}
4. ラベルの自動設定
ラベル取得の仕組み
優先順位:
.github/labels.yml: プロジェクト固有のラベル定義から取得- GitHub API:
gh api repos/{OWNER}/{REPO}/labels --jq '.[].name'で既存ラベルを取得
自動判定ルール
ファイルパターンベース:
- ドキュメント:
*.md,README,docs/→documentation|docs|docを含むラベル - テスト:
test,spec→test|testingを含むラベル - CI/CD:
.github/,*.yml,Dockerfile→ci|build|infra|opsを含むラベル - 依存関係:
package.json,pubspec.yaml,requirements.txt→dependencies|depsを含むラベル
変更内容ベース:
- バグ修正:
fix|bug|error|crash|修正→bug|fixを含むラベル - 新機能:
feat|feature|add|implement|新機能|実装→feature|enhancement|featを含むラベル - リファクタリング:
refactor|clean|リファクタ→refactor|cleanup|cleanを含むラベル - パフォーマンス:
performance|perf|optimize|パフォーマンス→performance|perfを含むラベル - セキュリティ:
security|secure|セキュリティ→securityを含むラベル
制約
- 最大 3 個まで: 自動選択されるラベル数の上限
- 既存ラベルのみ: 新しいラベルの作成は禁止
- 部分マッチ: ラベル名にキーワードが含まれているかで判定
実際の使用例
.github/labels.yml が存在する場合:
# ラベル定義から自動取得
grep "^- name:" .github/labels.yml | sed "s/^- name: '\?\([^']*\)'\?/\1/"
# 例: プロジェクト固有のラベル体系を使用
GitHub API から取得する場合:
# 既存ラベルの一覧取得
gh api repos/{OWNER}/{REPO}/labels --jq '.[].name'
# 例: bug, enhancement, documentation などの標準的なラベルを使用
5. 実行フロー
#!/bin/bash
# 1. PR の検出・取得
detect_pr() {
if [ -n "$PR_NUMBER" ]; then
echo $PR_NUMBER
else
gh pr list --head $(git branch --show-current) --json number --jq '.[0].number'
fi
}
# 2. 変更内容の分析
analyze_changes() {
local pr_number=$1
# ファイル変更の取得
gh pr diff $pr_number --name-only
# 内容分析
gh pr diff $pr_number | head -1000
}
# 3. 説明文の生成
generate_description() {
local pr_number=$1
local changes=$2
# 現在の PR 説明を取得
local current_body=$(gh pr view $pr_number --json body --jq -r .body)
# 既存内容があればそのまま使用
if [ -n "$current_body" ]; then
echo "$current_body"
else
# テンプレートから新規生成
local template_file=".github/PULL_REQUEST_TEMPLATE.md"
if [ -f "$template_file" ]; then
generate_from_template "$(cat "$template_file")" "$changes"
else
generate_from_template "" "$changes"
fi
fi
}
# テンプレートからの生成
generate_from_template() {
local template="$1"
local changes="$2"
if [ -n "$template" ]; then
# テンプレートをそのまま使用 (HTML コメント保持)
echo "$template"
else
# デフォルトフォーマットで生成
echo "## What does this change?"
echo ""
echo "$changes"
fi
}
# 4. ラベルの決定
determine_labels() {
local changes=$1
local file_list=$2
local pr_number=$3
# 利用可能なラベルを取得
local available_labels=()
if [ -f ".github/labels.yml" ]; then
# labels.yml からラベル名を抽出
available_labels=($(grep "^- name:" .github/labels.yml | sed "s/^- name: '\?\([^']*\)'\?/\1/"))
else
# GitHub API からラベルを取得
local repo_info=$(gh repo view --json owner,name)
local owner=$(echo "$repo_info" | jq -r .owner.login)
local repo=$(echo "$repo_info" | jq -r .name)
available_labels=($(gh api "repos/$owner/$repo/labels" --jq '.[].name'))
fi
local suggested_labels=()
# 汎用的なパターンマッチング
analyze_change_patterns "$file_list" "$changes" available_labels suggested_labels
# 最大 3 個に制限
echo "${suggested_labels[@]:0:3}"
}
# 変更パターンからラベルを決定
analyze_change_patterns() {
local file_list="$1"
local changes="$2"
local -n available_ref=$3
local -n suggested_ref=$4
# ファイルタイプによる判定
if echo "$file_list" | grep -q "\.md$\|README\|docs/"; then
add_matching_label "documentation\|docs\|doc" available_ref suggested_ref
fi
if echo "$file_list" | grep -q "test\|spec"; then
add_matching_label "test\|testing" available_ref suggested_ref
fi
# 変更内容による判定
if echo "$changes" | grep -iq "fix\|bug\|error\|crash\|修正"; then
add_matching_label "bug\|fix" available_ref suggested_ref
fi
if echo "$changes" | grep -iq "feat\|feature\|add\|implement\|新機能\|実装"; then
add_matching_label "feature\|enhancement\|feat" available_ref suggested_ref
fi
}
# マッチするラベルを追加
add_matching_label() {
local pattern="$1"
local -n available_ref=$2
local -n suggested_ref=$3
# すでに 3 個ある場合はスキップ
if [ ${#suggested_ref[@]} -ge 3 ]; then
return
fi
# パターンにマッチする最初のラベルを追加
for available_label in "${available_ref[@]}"; do
if echo "$available_label" | grep -iq "$pattern"; then
# 重複チェック
local already_exists=false
for existing in "${suggested_ref[@]}"; do
if [ "$existing" = "$available_label" ]; then
already_exists=true
break
fi
done
if [ "$already_exists" = false ]; then
suggested_ref+=("$available_label")
return
fi
fi
done
}
# 旧関数の互換性のため残しておく
find_and_add_label() {
add_matching_label "$@"
}
# 5. PR の更新
update_pr() {
local pr_number=$1
local description="$2"
local labels="$3"
if [ "$DRY_RUN" = "true" ]; then
echo "=== DRY RUN ==="
echo "Description:"
echo "$description"
echo "Labels: $labels"
else
# リポジトリ情報を取得
local repo_info=$(gh repo view --json owner,name)
local owner=$(echo "$repo_info" | jq -r .owner.login)
local repo=$(echo "$repo_info" | jq -r .name)
# GitHub API を使用して本文を更新 (HTML コメント保持)
# JSON エスケープを適切に処理
local escaped_body=$(echo "$description" | jq -R -s .)
gh api \
--method PATCH \
"/repos/$owner/$repo/pulls/$pr_number" \
--field body="$description"
# ラベルは通常の gh コマンドで問題なし
if [ -n "$labels" ]; then
gh pr edit $pr_number --add-label "$labels"
fi
fi
}
設定ファイル (今後の拡張用)
~/.claude/pr-auto-update.config:
{
"language": "ja",
"max_labels": 3
}
よくあるパターン
Flutter プロジェクト
## What does this change?
{機能名}を実装しました。ユーザーの{課題}を解決します。
### 主な変更内容
- **UI 実装**: {画面名}を新規作成
- **状態管理**: 状態管理ライブラリを使用してステート管理を追加
- **API 統合**: GraphQL クエリ・ミューテーションを実装
- **テスト**: ウィジェットテスト・ユニットテストを追加
### 技術仕様
- **アーキテクチャ**: {使用パターン}
- **依存関係**: {新規追加したパッケージ}
- **パフォーマンス**: {最適化内容}
Node.js プロジェクト
## What does this change?
{API 名}エンドポイントを実装しました。{ユースケース}に対応します。
### 主な変更内容
- **API 実装**: {エンドポイント}を新規作成
- **バリデーション**: リクエスト検証ロジックを追加
- **データベース**: {テーブル名}への操作を実装
- **テスト**: 統合テスト・ユニットテストを追加
### セキュリティ
- **認証**: JWT トークン検証
- **認可**: ロールベースアクセス制御
- **入力検証**: SQL インジェクション対策
CI/CD 改善
## What does this change?
GitHub Actions ワークフローを改善しました。{効果}を実現します。
### 改善内容
- **パフォーマンス**: ビルド時間を{時間}短縮
- **信頼性**: エラーハンドリングを強化
- **セキュリティ**: シークレット管理を改善
### 技術詳細
- **並列化**: {ジョブ名}を並列実行
- **キャッシュ**: {キャッシュ対象}のキャッシュ戦略を最適化
- **モニタリング**: {メトリクス}の監視を追加
注意事項
-
既存内容の完全保持:
- 既に記述されている内容は一文字も変更しない
- 空のコメント部分とプレースホルダーのみ補完
- ユーザーが意図的に書いた内容を尊重
-
テンプレート優先順位:
- 既存の PR 説明 >
.github/PULL_REQUEST_TEMPLATE.md> デフォルト - プロジェクト固有のテンプレート構造を完全踏襲
- 既存の PR 説明 >
-
ラベル制約:
.github/labels.ymlが存在すれば優先使用- 存在しない場合は GitHub API から既存ラベルを取得
- 新しいラベルの作成は禁止
- 最大 3 個まで自動選択
-
安全な更新:
--dry-runで事前確認を推奨- 機密情報を含む変更の場合は警告表示
- バックアップとして元の説明を保存
-
一貫性の維持:
- プロジェクトの既存 PR スタイルに合わせる
- 言語 (日本語/英語) の統一
- ラベリング規則の継承
トラブルシューティング
よくある問題
- PR が見つからない: ブランチ名と PR の関連付けを確認
- 権限エラー: GitHub CLI の認証状態を確認
- ラベルが設定できない: リポジトリの権限を確認
- HTML コメントがエスケープされる: GitHub CLI の仕様により
<!-- -->が<!-- -->に変換される
GitHub CLI の HTML コメントエスケープ問題
重要: GitHub CLI(gh pr edit) は HTML コメントを自動エスケープします。また、シェルのリダイレクト処理で EOF < /dev/null などの不正な文字列が混入する場合があります。
根本的解決策
- GitHub API の --field オプション使用:
--fieldを使用して適切なエスケープ処理 - シェル処理の簡素化: 複雑なリダイレクトやパイプ処理を避ける
- テンプレート処理の単純化: HTML コメント除去処理を廃止し、完全保持
- JSON エスケープの適切な処理: 特殊文字を正しく処理
デバッグオプション
# 詳細ログ出力 (実装時に追加)
/pr-auto-update --verbose