fix: promote-rc force-updates target ref instead of delete+recreate
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 17s
Universal: PR Check / Branch Policy (pull_request) Successful in 3s
Universal: Build & Release / Promote to RC (pull_request) Failing after 19s
Universal: Build & Release / Build & Release Pipeline (pull_request) Has been skipped
Universal: PR Check / Secret Scan (pull_request) Successful in 15s
Universal: PR Check / Validate PR (pull_request) Successful in 9s
Generic: Repo Health / Access control (pull_request) Successful in 3s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Project CI / Lint & Validate (pull_request) Successful in 1m7s
Platform: mokocli CI / Gate 1: Code Quality (pull_request) Successful in 2m10s
Generic: Project CI / Tests (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: mokocli CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report: Scripts Governance (pull_request) Has been cancelled
Generic: Repo Health / Report: Repository Health (pull_request) Has been cancelled

branch_rename.php deleted the target branch then recreated it. When the target (rc) is a protected branch, the delete silently fails and the recreate returns HTTP 409 'branch already exists', breaking every PR-to-main promote-rc. Force-update the ref in place via PATCH git/refs/heads/{to} (sha+force) when the target exists; still create it when it doesn't. Requires the CI token to have force-push permission on the protected rc branch.
This commit is contained in:
2026-07-04 16:49:15 -05:00
parent fd3c847fb1
commit e79908f275
2 changed files with 39 additions and 16 deletions
+3
View File
@@ -12,6 +12,9 @@ BRIEF: Release changelog
# Changelog
## [Unreleased]
### Fixed
- branch_rename.php (promote-rc): force-update the target branch ref in place instead of delete+recreate, so promotion works when the target (e.g. `rc`) is a protected branch that cannot be deleted — previously the delete silently failed and the recreate returned HTTP 409
## [09.41.00] --- 2026-07-04
## [09.41.00] --- 2026-07-04
+36 -16
View File
@@ -64,26 +64,46 @@ class BranchRenameCli extends CliFramework
return 1;
}
// Step 2: Delete target branch if it already exists
// Step 2: Point the target branch at the source commit.
// If the target already exists (e.g. a protected `rc` branch that cannot be
// deleted), force-update its ref in place instead of delete+recreate: deleting a
// protected branch fails, which then makes the recreate return HTTP 409.
$sourceSha = '';
if (isset($check['body']['commit']['id']) && is_string($check['body']['commit']['id'])) {
$sourceSha = $check['body']['commit']['id'];
}
$targetCheck = $this->apiRequest('GET', "{$apiBase}/branches/{$to}", $headers);
if ($targetCheck['code'] === 200) {
echo "Target branch '{$to}' already exists — deleting\n";
echo "Target branch '{$to}' already exists - force-updating to {$from}
";
if (!$this->dryRun) {
$this->apiRequest('DELETE', "{$apiBase}/branches/{$to}", $headers);
if ($sourceSha === '') {
$this->log('ERROR', "Cannot resolve HEAD commit of source '{$from}'");
return 1;
}
$ref = $this->apiRequest('PATCH', "{$apiBase}/git/refs/heads/{$to}", $headers, [
'sha' => $sourceSha,
'force' => true,
]);
if ($ref['code'] < 200 || $ref['code'] >= 300) {
$this->log('ERROR', "Failed to force-update '{$to}': HTTP {$ref['code']} (needs force-push perm)");
$this->log('ERROR', json_encode($ref['body']));
return 1;
}
}
}
// Step 3: Create new branch from source
echo "Creating branch: {$to} (from {$from})\n";
if (!$this->dryRun) {
$create = $this->apiRequest('POST', "{$apiBase}/branches", $headers, [
'new_branch_name' => $to,
'old_branch_name' => $from,
]);
if ($create['code'] < 200 || $create['code'] >= 300) {
$this->log('ERROR', "Failed to create branch '{$to}': HTTP {$create['code']}");
$this->log('ERROR', json_encode($create['body']));
return 1;
} else {
echo "Creating branch: {$to} (from {$from})
";
if (!$this->dryRun) {
$create = $this->apiRequest('POST', "{$apiBase}/branches", $headers, [
'new_branch_name' => $to,
'old_branch_name' => $from,
]);
if ($create['code'] < 200 || $create['code'] >= 300) {
$this->log('ERROR', "Failed to create branch '{$to}': HTTP {$create['code']}");
$this->log('ERROR', json_encode($create['body']));
return 1;
}
}
}