単独メンテナーの PR マージ儀式
メンテナーが自分のプルリクエストを直接マージできる場合と、承認を要求し管理者にもそれを強制するブランチ保護のもとでの儀式。
プロジェクトのブランチ保護のもとで PR を自己マージするための正準リファレンス。 ブランチ保護ページに記録されているとおり、
mainに対する現行の保護は、必須の承認レビュー数が 0 で、enforce_adminsはオフのままです。そのため、メンテナーは自分のプルリクエストを直接マージします。このページの一時切り替え儀式は、保護が承認を要求し、かつそれを管理者にも強制している場合にのみ適用されます。
1. デッドロック
現行の main の保護には、次の設定があります。
required_pull_request_reviews.required_approving_review_count: 0enforce_admins: false
これらの設定のもとでは、デッドロックは発生しません。メンテナーのプルリクエストは、必須のステータスチェックがグリーンになり、レビューの会話が解決されると、gh pr merge <pr-number> --squash --delete-branch でマージされます。
デッドロックが発生するのは、保護に次のより厳しい 2 つの設定が両方とも含まれている場合だけです。
required_pull_request_reviews.required_approving_review_count: 1enforce_admins: true
これらのルールは、複数メンテナーのシナリオでは正しいものです(すべての変更はピアレビューを経た PR を通じて main に到達し、管理者も例外ではありません)。しかし、単独メンテナーにとってはデッドロックを生み出します。GitHub のポリシーは自分が作成した PR の自己承認を許可せず、enforce_admins=true はレビュー承認要件に対する gh pr merge --admin のオーバーライドをブロックするためです。enforce_admins: false の場合、--admin は承認要件をバイパスするため切り替えは不要ですが、失敗している(赤の)必須チェックもバイパスしてしまうため、使用する前にそれらがグリーンであることを確認してください。
解決パスは 3 つあります。
| パス | 使用する場合 |
|---|---|
required_approving_review_count: 0 を恒久的に設定する(現行の設定) | リポジトリが無期限に単独メンテナーのままである場合にのみ採用する。保護のレビュー意図を覆す |
| 一時切り替え儀式 | 保護が enforce_admins=true で承認を要求している間の、単独メンテナーによる自己マージ。マージウィンドウの外では保護の不変条件を維持する |
| 書き込みアクセスを持つ CI ボットのアイデンティティを用意する | 複数メンテナーのオンボーディングが目前に迫っている場合にのみ採用する。ボットによる承認は、保護が意図する人間によるレビューを損なう |
このリポジトリは現在、恒久的な承認ゼロのパスを使用しています。保護が承認を要求し、かつそれを管理者にも強制する場合は常に、一時切り替え儀式が正準パスです。
2. 一時切り替え儀式
この儀式の不変条件は、儀式開始時の保護状態が儀式終了時の保護状態と等しいことです。緩和はマージウィンドウの内側にのみ存在します。
手動の形式:
# 1. Open the PR, push branch, wait for CI green.
gh pr create --base main --head <feature-branch> --title "..." --body "..."
gh pr checks <pr-number> # poll until all green
# 2. Relax approver-count to 0.
gh api repos/<owner>/<repo>/branches/main/protection/required_pull_request_reviews \
-X PATCH -F required_approving_review_count=0
# 3. Squash-merge with admin override (admin override now succeeds because the
# review-approval requirement is the only block enforce_admins refuses).
gh pr merge <pr-number> --squash --delete-branch --admin
# 4. Restore approver-count to 1.
gh api repos/<owner>/<repo>/branches/main/protection/required_pull_request_reviews \
-X PATCH -F required_approving_review_count=1ラップされた形式: 手順 2 → 3 → 4 を単一の非対話シーケンスで実行し、手順 3 が失敗した場合に try / finally によるロールバックの安全性を確保するアトミックな Python ラッパーについては、scripts/dev/admin_merge.py を参照してください。
python scripts/dev/admin_merge.py <pr-number>3. 不変条件
- 切り替えウィンドウを最小化。 手順 2 → 3 → 4 は単一の非対話シーケンスで実行されます。保護が緩和状態にあるのはマージ API 呼び出しの間だけであり、観測されたすべての実行で 1 秒未満です。
- 復元は無条件。 手順 3 が失敗しても、手順 4 は実行されます(ラッパーの
try/finally句が、あらゆる終了パスで復元を保証します)。失敗したマージ試行が、保護を緩和状態のまま残すことはありません。 enforce_adminsがオンである間、CI ゲートは全工程を通じて維持される。required_status_checksは切り替えの影響を受けません。enforce_admins: trueの場合、マージには引き続き、設定済みのステータスチェックコンテキストがグリーンであることが必要です。enforce_admins: falseの場合、手順 3 の--adminマージは失敗している(赤の)必須チェックもバイパスするため、その状態では儀式を実行しないでください。- 監査証跡が可視。
gh pr merge --adminの注記は、PR のマージメタデータに現れます。保護 API の切り替えは、リポジトリの監査ログにメンテナーのアイデンティティのもとで現れます。
4. 儀式を使うべきでない場合
保護の必須承認レビュー数が 0 であるか、enforce_admins がオフのままである間は、儀式は不要です。セクション 1 で説明しているとおり、直接マージしてください。
この儀式は、メンテナー自身の PR の自己マージのためのものです。ピアレビュアー(書き込みアクセスを持つコントリビューター)が利用可能な場合は、標準の gh pr review --approve + gh pr merge --squash --delete-branch のパスが正しく、儀式は不要です。
リポジトリが 2 人目のメンテナーをオンボーディングする場合は、儀式を廃止してください。標準の PR レビューフローが、いかなる切り替えもなしに、保護が意図する人間によるレビューを復元します。
5. 過去の実行の監査
儀式の各呼び出しは、オペレーターが事後に検査できる 3 つの永続的な面に記録されます。
- PR のマージメタデータ。
gh pr view <pr-number> --json mergedBy,mergedAt,mergeCommitは、マージのアクター、マージのタイムスタンプ、squash コミットの SHA を返します。mergedByのアクターは、手順 3 を実行したメンテナーのアイデンティティと一致します。 - リポジトリの監査ログ。
gh api /repos/<owner>/<repo>/actions/runs --jq '.workflow_runs[] | select(.event=="push")'は、マージ後の CI 実行をマージコミットと関連付けます。リポジトリの保護ルールの監査ログ(Settings → Branches の UI を介したgh api /repos/<owner>/<repo>/branches/main/protectionの履歴)は、マージのウィンドウを挟む手順 2 と手順 4 の切り替えを示します。 - メンテナーの git 履歴。
git log main --first-parent --onelineは、main上のすべての squash マージコミットを、コミットの件名に含まれる対応する PR 番号(gh pr merge --squashの既定による(#<pr-number>)の接尾辞)とともに一覧表示し、オペレーターが過去のあらゆる着地を、その PR と儀式の呼び出しまで遡れるようにします。
scripts/dev/admin_merge.py がエントリーポイントである場合、その非対話ログ(stderr)は、3 つの保護 API 呼び出しとマージ API 呼び出しを順番に示します。stderr を tee にパイプ(2>&1 | tee)すれば、GitHub 側のログ保持に依存することなく、実行ごとの監査フラグメントを取得できます。