## PR Auto Update ## 概要 Pull Request の説明とラベルを自動的に更新するコマンドです。Git の変更内容を分析して、適切な説明文とラベルを生成・設定します。 ## 使い方 ```bash /pr-auto-update [オプション] [PR 番号] ``` ### オプション - `--pr <番号>` : 対象の PR 番号を指定 (省略時は現在のブランチから自動検出) - `--description-only` : 説明文のみ更新 (ラベルは変更しない) - `--labels-only` : ラベルのみ更新 (説明文は変更しない) - `--dry-run` : 実際の更新は行わず、生成される内容のみ表示 - `--lang <言語>` : 言語を指定 (ja, en) ### 基本例 ```bash # 現在のブランチの PR を自動更新 /pr-auto-update # 特定の PR を更新 /pr-auto-update --pr 1234 # 説明文のみ更新 /pr-auto-update --description-only # ドライランで確認 /pr-auto-update --dry-run ``` ## 機能詳細 ### 1. PR の自動検出 現在のブランチから対応する PR を自動検出: ```bash # ブランチから 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. 説明文の自動生成 #### テンプレート処理の優先順位 1. **既存の PR 説明**: 既に記述されている内容を**完全に踏襲** 2. **プロジェクトテンプレート**: `.github/PULL_REQUEST_TEMPLATE.md` から構造を取得 3. **デフォルトテンプレート**: 上記が存在しない場合のフォールバック #### 既存内容の保持ルール **重要**: 既存の内容は変更しない - 書かれているセクションは保持 - 空のセクションのみ補完 - 機能的なコメント (Copilot review rule など) は保持 #### プロジェクトテンプレートの使用 ```bash # .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. ラベルの自動設定 #### ラベル取得の仕組み **優先順位**: 1. **`.github/labels.yml`**: プロジェクト固有のラベル定義から取得 2. **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` が存在する場合**: ```bash # ラベル定義から自動取得 grep "^- name:" .github/labels.yml | sed "s/^- name: '\?\([^']*\)'\?/\1/" # 例: プロジェクト固有のラベル体系を使用 ``` **GitHub API から取得する場合**: ```bash # 既存ラベルの一覧取得 gh api repos/{OWNER}/{REPO}/labels --jq '.[].name' # 例: bug, enhancement, documentation などの標準的なラベルを使用 ``` ### 5. 実行フロー ```bash #!/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`: ```json { "language": "ja", "max_labels": 3 } ``` ## よくあるパターン ### Flutter プロジェクト ```markdown ## What does this change? {機能名}を実装しました。ユーザーの{課題}を解決します。 ### 主な変更内容 - **UI 実装**: {画面名}を新規作成 - **状態管理**: 状態管理ライブラリを使用してステート管理を追加 - **API 統合**: GraphQL クエリ・ミューテーションを実装 - **テスト**: ウィジェットテスト・ユニットテストを追加 ### 技術仕様 - **アーキテクチャ**: {使用パターン} - **依存関係**: {新規追加したパッケージ} - **パフォーマンス**: {最適化内容} ``` ### Node.js プロジェクト ```markdown ## What does this change? {API 名}エンドポイントを実装しました。{ユースケース}に対応します。 ### 主な変更内容 - **API 実装**: {エンドポイント}を新規作成 - **バリデーション**: リクエスト検証ロジックを追加 - **データベース**: {テーブル名}への操作を実装 - **テスト**: 統合テスト・ユニットテストを追加 ### セキュリティ - **認証**: JWT トークン検証 - **認可**: ロールベースアクセス制御 - **入力検証**: SQL インジェクション対策 ``` ### CI/CD 改善 ```markdown ## What does this change? GitHub Actions ワークフローを改善しました。{効果}を実現します。 ### 改善内容 - **パフォーマンス**: ビルド時間を{時間}短縮 - **信頼性**: エラーハンドリングを強化 - **セキュリティ**: シークレット管理を改善 ### 技術詳細 - **並列化**: {ジョブ名}を並列実行 - **キャッシュ**: {キャッシュ対象}のキャッシュ戦略を最適化 - **モニタリング**: {メトリクス}の監視を追加 ``` ## 注意事項 1. **既存内容の完全保持**: - 既に記述されている内容は**一文字も変更しない** - 空のコメント部分とプレースホルダーのみ補完 - ユーザーが意図的に書いた内容を尊重 2. **テンプレート優先順位**: - 既存の PR 説明 > `.github/PULL_REQUEST_TEMPLATE.md` > デフォルト - プロジェクト固有のテンプレート構造を完全踏襲 3. **ラベル制約**: - `.github/labels.yml` が存在すれば優先使用 - 存在しない場合は GitHub API から既存ラベルを取得 - 新しいラベルの作成は禁止 - 最大 3 個まで自動選択 4. **安全な更新**: - `--dry-run` で事前確認を推奨 - 機密情報を含む変更の場合は警告表示 - バックアップとして元の説明を保存 5. **一貫性の維持**: - プロジェクトの既存 PR スタイルに合わせる - 言語 (日本語/英語) の統一 - ラベリング規則の継承 ## トラブルシューティング ### よくある問題 1. **PR が見つからない**: ブランチ名と PR の関連付けを確認 2. **権限エラー**: GitHub CLI の認証状態を確認 3. **ラベルが設定できない**: リポジトリの権限を確認 4. **HTML コメントがエスケープされる**: GitHub CLI の仕様により `` が `<!-- -->` に変換される ### GitHub CLI の HTML コメントエスケープ問題 **重要**: GitHub CLI(`gh pr edit`) は HTML コメントを自動エスケープします。また、シェルのリダイレクト処理で `EOF < /dev/null` などの不正な文字列が混入する場合があります。 #### 根本的解決策 1. **GitHub API の --field オプション使用**: `--field` を使用して適切なエスケープ処理 2. **シェル処理の簡素化**: 複雑なリダイレクトやパイプ処理を避ける 3. **テンプレート処理の単純化**: HTML コメント除去処理を廃止し、完全保持 4. **JSON エスケープの適切な処理**: 特殊文字を正しく処理 ### デバッグオプション ```bash # 詳細ログ出力 (実装時に追加) /pr-auto-update --verbose ```