git-coda — 自動 Ship ワークフロー は何をしますか?
git-coda はプロジェクト名です。coda(終曲)は楽章の終結部であり、開発の歴史全体を一つの音符に収束させ、main に戻します。ワークフローの動詞は依然として ship です。
目標と成功基準
現在のワークスペースの変更を、次のパスで完全に実行します:
現在の変更 → 最新の main を元にブランチを作成 → commit → PR → squash merge → main に戻る
ユーザーが ship を呼び出すことは、ワークフロー全体の実行が承認されたことを意味します。プロセスは連続して完了する必要があり、ブランチ名、コミットメッセージ、PR タイトル、PR 本文、競合解決方法、検証結果や続行の可否などについて確認を求めてはいけません。
この承認には以下が含まれます:
- 現在の機能コードを、最新の
mainのオンライン機能と互換性を持つように変更すること。 - 今回の変更または競合によって生じたテスト、lint、型、ビルド、CI の問題を修正すること。
- 必要な修正コミットを作成し、再度プッシュすること。
- マージ前に最新の
mainを同期し、新たに発生した競合を解決して再検証すること。
以下の条件がすべて満たされた場合のみ、完了とみなします:
- PR が
mainに正常にマージされていること。「PR が作成された」や「自動マージが有効になった」を完了とみなさない。 - 必須チェックが通過しているか、リポジトリに実行可能なチェックがないこと。
- ローカルで
mainに戻り、可能な限りマージ後のコミットに fast-forward していること。
コア実行原則
shipのデフォルト結果は「公開成功」であり、「問題を見つけてユーザーに返す」ではありません。- コマンドが失敗したら、まず原因を診断してフローを回復します。同じ失敗コマンドを機械的に繰り返さない。
- 最新の
origin/mainの既存のオンライン機能と、現在の変更のプロダクト目標を共通の制約として、両方を優先的に保持します。 - 根本原因を修正します。テストのスキップ、アサーションの削除、ルールの無効化、
--no-verifyの使用などによる偽の通過は生成しません。 - force push、
reset --hard、コミットの破棄、ユーザーの変更の上書きなどの破壊的な手段は使用しません。 - 認証/権限の欠如、他者による承認が必要、プラットフォームやネットワークの持続的な障害、コードとコンテキストから要求意図がどうしても判断できない場合のみ、一時停止し、ユーザーが処理する必要がある唯一の事項を説明します。
ステップ 0:情報収集
開始前に現在の状態を確認します:
git status --short --branch
git branch --show-current
git diff --stat
git diff --cached --stat
git remote -v
gh auth status
同時に、現在のブランチ、未コミットの変更、プッシュされていないコミットを記録し、ユーザーの作業を漏らさないようにします。未コミットの変更がない場合は、現在のブランチに origin/main に対して公開可能なコミットがあるかどうかを確認します。どちらもない場合は、公開する内容がないことを伝えて停止します。
以下の情報を自動的に決定します:
1. ブランチ名
- ユーザーが既に提供している場合はそのまま使用します。
- それ以外の場合は、変更内容に基づいて推論し、形式は
<type>/<short-desc>とします。 - ターゲットリポジトリの既存のブランチプレフィックスや命名規則に従います。規則がない場合はセマンティックな名前を使用します。
- ローカルまたはリモートに同名ブランチが既に存在する場合は、自動的に短いサフィックスを追加し、命名の競合で中断しないようにします。
2. コミットメッセージ
- ユーザーが既に提供している場合はそのまま使用します。
- それ以外の場合は、完全な差分から Conventional Commits 形式で生成します:
<type>(<scope>): <description>。
3. PR タイトルと本文
- PR タイトルはデフォルトでコミットメッセージを使用します。
- PR 本文は差分から自動生成し、2~3 行のサマリー、変更ファイルの概要、検証結果を含めます。
- PR の名前や本文について個別に質問しません。
決定後、使用するブランチとコミットを簡単にユーザーに伝え、すぐに続行し、返信を待ちません。
ステップ 1:メインブランチの同期
ワークスペースに変更がある場合は、一意の ID を持つスタッシュを作成し、対応する参照を記録します。変更がない場合は空のスタッシュを作成しません。
git stash push --include-untracked -m "git-coda: <timestamp>"
git fetch --prune origin
fetch 後、ステップ 0 で記録した元のブランチと元の HEAD を基準に、公開可能なコミットを確認します。元の機能ブランチに既にコミットがある場合は、そのブランチを保持し、origin/main を直接マージし、その後今回のスタッシュを復元します。main に切り替えてから元のブランチにコミットがあるかどうかを判断してはいけません。元のブランチに公開可能なコミットがない場合のみ、origin/main から公開用ブランチを新規作成します。
まずリモートの HEAD またはリポジトリの設定からターゲットのメインブランチを確認します。以下の main はプレースホルダ名であり、リポジトリが master などの名前を使用している場合は、すべて実際のブランチに置き換えます。
最新のメインブランチのコミットハッシュをユーザーに伝えます。
ローカルの main が fast-forward できない場合:
- ローカルの固有のコミットを確認し、それらを削除または上書きしてはいけません。
- 最新の
origin/mainから直接公開用ブランチを作成し、公開フローを続行します。 - 最終的に
mainに戻るときにこれらのローカルコミットを保持し、ローカルのmainが上書きされていないことを明確に説明します。これは完了したリモートマージに影響しません。
ステップ 2:ブランチの作成
git checkout -b <branch-name> origin/main
現在の作業が既に公開可能なコミットを含む機能ブランチ上にある場合は、それを引き続き使用できます。最初に最新の origin/main をマージし、「競合処理フロー」に従って問題を解決します。同じ変更を重複してコミットしないでください。
ステップ 3:変更の復元とコミット
git stash apply <recorded-stash>
git add -A
git commit -m "<commit-message>"
stash apply で競合が発生した場合は、「競合処理フロー」に従って自分で解決します。すべての変更が復元され、コミットされたことを確認してから、今回作成したスタッシュを削除します。ユーザーの既存のスタッシュを誤って削除しないでください。
コミットフックの失敗は検証失敗とみなします。根本原因を修正し、再度ステージングしてコミットします。--no-verify は使用しないでください。
競合処理フロー
以下のフローは、stash apply、最新の main へのマージ、リモートブランチの非 fast-forward、PR のマージ競合に適用されます。
-
事実を取得します:
git status git diff --name-only --diff-filter=U git log --oneline --left-right origin/main...HEAD -
競合ファイルの base、現在のブランチ、相手のブランチのバージョンを個別に確認し、関連する呼び出し元、テスト、型、設定、最近のコミットを読みます。競合マーカーだけを見たり、ファイル全体を盲目的に
oursやtheirsで選択したりしないでください。 -
両方の意図を判断します:
origin/mainは現在のオンラインの振る舞いを表します。今回の機能がそれを明示的に置き換える場合を除き、保持する必要があります。- 現在のブランチは今回の機能の目標を表します。競合を解消するために静かに削除してはいけません。
- 両方に有効な機能が追加されている場合は、インターフェース、データフロー、境界条件を統合し、両方が成立するようにします。
- テストの競合は最終的なプロダクトの振る舞いに基づいて更新します。ロックファイルと生成ファイルは、リポジトリで指定されたツールを使用して再生成します。
-
競合マーカーがなくなるように編集し、
git addを実行し、git diff --name-only --diff-filter=Uが空であることを確認します。 -
競合ファイルに関連する最小限の検証を実行し、次にリポジトリで要求される完全な検証を実行します。
-
マージまたは修正コミットを作成し、元のフローを続行します。
競合ブロックだけでは判断できない場合は、実装、テスト、呼び出しチェーン、コミット履歴、プロジェクトドキュメントから証拠を収集し続けます。ただし、2 つの振る舞いが相互に排他的で、プロダクトの意図がそれでも判断できない場合のみ、ユーザーに質問し、選択肢、影響、推奨事項を一度に明確に説明します。
ステップ 3.5:ローカル検証
現在の変更とリポジトリの説明に基づいて、既存の検証コマンドを探します:
- 優先的に
AGENTS.md、CONTRIBUTING.md、README.mdを読みます。 - 次に
package.json、pyproject.toml、Makefile、CI 設定などのプロジェクトファイルを確認します。 - 必要に応じて、ロックファイルに従ってプロジェクトの既存のパッケージマネージャーを使用して依存関係をインストールし、依存関係を勝手にアップグレードしないでください。
- 最初に変更範囲に直接関連するチェックを実行し、次にリポジトリで要求される lint、typecheck、test、build を実行します。
- 信頼できるコマンドが見つからない場合は、どの検証をスキップしたかとその理由を明確にユーザーに伝え、自分でコマンドをでっち上げないでください。
検証失敗修正ループ
検証に失敗しても一時停止せず、次のループで処理します:
- 完全なエラー出力を保持し、最初の根本原因を特定します。最後の連鎖エラーのみを修正しないでください。
- 失敗がプロダクトコード、テスト期待値、型、フォーマット、依存関係、設定、環境のいずれによるものかを判断します。
- オンライン機能と現在の機能を満たす、最小限の正しい修正を行います:
- リポジトリに既存のフォーマッターや lint autofix がある場合は優先的に使用し、残りの問題を処理します。
- コードの欠陥はコードを修正します。
- テストは、今回の機能が意図的に振る舞いを変更し、元の期待値が既に時代遅れになっている場合にのみ更新します。有効なテストを弱めたり、スキップしたり、削除したりしてはいけません。
- 環境に依存関係が不足している場合は、プロジェクトのロックファイルに従って環境を復元します。関係のないアップグレードを行ってはいけません。
- 最初に失敗した最小チェックを再実行し、次にすべての必須チェックを再実行します。
- 新しい失敗が見つかったら、診断と修正を続け、すべてが合格するまで繰り返します。
最初の push 前に発生した修正は、現在のコミットに含めることができます。最初の push 後に発生した修正は、新しいコミットを作成して通常通り push し、履歴を整理するために force push しないでください。毎回のラウンドで新しい証拠や実質的な進展がある限り、失敗の回数が多くても諦めずに進めます。
ステップ 4:プッシュと PR の作成
git push -u origin <branch-name>
push が拒否された場合は、まず分類して処理します:
- リモートの同名ブランチが無関係な内容の場合:自動的に別の一意のブランチ名を使用して push します。
- リモートの同名ブランチが同じ機能に属し、新しいコミットがある場合:fetch 後、リモートブランチをマージし、競合を解決して検証し、再度 push します。
- ネットワークまたは GitHub の一時的な障害の場合:短時間リトライし、サービスの復旧状況を確認します。
- 認証または書き込み権限の欠如の場合:完全な作業現場を保持し、ユーザーに認証を完了するよう依頼します。
gh pr create を使用して PR を作成します:
- base は
main - head は新しいブランチ
- タイトルはコミットメッセージを使用
- 本文には 2~3 行のサマリー、変更ファイルの概要、検証結果、および
🤖 Finale via git-codaを含める
PR が既に存在する場合はそれを再利用し、重複して作成しません。作成後、PR の URL をユーザーに表示し、続行し、返信を待ちません。
ステップ 5:CI の待機と修正
gh pr checks <pr-number> --watch など、リポジトリで利用可能なコマンドを使用して、必須チェックを待ちます。
チェックが失敗した場合:
gh pr checks、gh run view --log-failedなどのコマンドを使用して、失敗したタスクとログを読み取ります。- ローカルで再現できる場合は再現し、「検証失敗修正ループ」に従って根本原因を修正します。
- 修正をコミットして通常通り push し、CI を再度待ちます。
- 必須チェックがすべて合格するまで繰り返します。
チェックが長時間 pending 状態の場合は、workflow、同時実行キュー、GitHub のステータスを確認します。失敗したタスクを再実行できる場合は自分で処理します。タスクを承認できる権限を持つ人の承認が必要な場合、または外部サービスが持続的に利用できない場合のみ、ユーザーの介入を求めます。
ステップ 6:最新の main の同期と Squash マージ
マージ前に再度メインブランチを同期します:
git fetch origin main
git merge --no-edit origin/main
競合がある場合は「競合処理フロー」に従って解決し、ローカル検証を再実行し、push して CI を待ちます。その後、直接マージします:
gh pr merge <pr-number> --squash --delete-branch
マージ時に base が更新されたり競合が発生したりした場合は、同期、解決、検証、push、CI 待機のループを再実行し、再度マージします。 リポジトリがマージキューをサポートしている場合は、キューに入った後も待機を続け、PR が実際にマージされたことを確認します。
ブランチ保護を迂回してはいけません。必須の承認、署名、権限が不足している場合は、これは外部ブロックです。GitHub の正確な返却情報を表示し、既に完了したステップとユーザーが実行する必要があるアクションを説明します。
ステップ 7:main に戻り、結果を確認
git checkout main
git pull --ff-only origin main
git status --short --branch
gh pr view <pr-number> --json state,mergedAt,mergeCommit,url
PR の状態が merged であることを確認し、最終的な main のコミットハッシュを出力します。ローカルの main に事前に存在する固有のコミットがあり、fast-forward できない場合は、それらを保持し、破壊的な処理を行いません。それでもリモートの PR がマージされたことを確認し、ローカルの状態を明確に説明します。
最終的な状態を出力します:
🚀 Ship 完了!
✓ ブランチ <branch-name> が main にマージされました
✓ ローカル検証と必須 CI が通過しました
✓ 現在 main 上で、最新コードに同期されています(<commit-hash>)
✓ PR: <pr-url>
エラー処理の原則
- 各ステップの操作前に、何をしているかを簡潔に説明します。
- いずれかのコマンドが失敗した場合、まず診断、修正、リトライを行い、正常なマージを目標に続行します。
- force push、
reset --hard、その他の破壊的な回復方法は使用しません。 - 競合を積極的に解決し、最新のオンライン機能と現在の機能目標の両方を保護します。
- テスト、lint、型チェック、ビルド、コミットフック、CI の失敗を積極的に修正します。
- 自動推論されたブランチ名、コミットメッセージ、PR タイトル、PR 本文、または続行について確認を求めません。
- 修正可能な失敗、複数回のデバッグが必要な場合、またはプロセスの所要時間をブロックとみなしません。
- 現在の環境では確実に解決できない外部条件の場合のみ一時停止します。作業を継続できるブランチ、コミット、ログを保持します。
- 一時停止時には、明確で最小限の、実行可能なユーザーアクションのみを提供し、完了後は元の位置から続行できるようにします。
よくある境界ケース
| 状況 | 処理方法 |
|---|---|
| 現在非 main ブランチで、ワークスペースに変更のみある場合 | stash → origin/main から新ブランチを作成 → apply → 競合解決 |
| 現在のブランチに既に公開可能なコミットがある場合 | コミットを保持し、最新の origin/main をマージして続行 |
| 未コミットの変更がない場合 | origin/main に対する公開可能なコミットを確認。どちらもなければ停止 |
| ブランチ名が既に存在する場合 | 自動的に一意のサフィックスを生成。同じ機能のリモートブランチがある場合は再利用して統合 |
stash apply またはマージ競合 | 両方の機能の意図を分析し、実装を統合し、検証して続行 |
| lint/test/typecheck/build の失敗 | 根本原因を修正し、ループ検証。失敗をユーザーに委ねない |
| CI の失敗 | ログを読み、ローカルで再現、修正、push、次の CI を待つ |
| マージ前に main が更新された場合 | 最新の origin/main をマージし、競合を解決し、検証ループを再実行 |
gh が未ログイン | gh auth login の実行を促す |
| 必須の人力承認または書き込み権限の欠如 | 現場を保持し、ユーザーが完了する必要がある唯一の外部アクションを説明 |
ユーザーが --no-squash を指定 | --merge を使用 |