From 835350fedd09c369d9cf879937be0c84549f77e1 Mon Sep 17 00:00:00 2001 From: "kiloconnect[bot]" <240665456+kiloconnect[bot]@users.noreply.github.com> Date: Tue, 21 Apr 2026 14:12:30 +0000 Subject: [PATCH 1/2] chore(publish): defer release push and implement rebase retry loop Move the git push operation to the end of the publishing process to minimize race conditions. Replace the cherry-pick logic with a rebase onto origin/main and a retry loop to ensure the release commit is pushed successfully even if concurrent merges occur. --- script/publish.ts | 37 ++++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/script/publish.ts b/script/publish.ts index 9cd1c65cf9..20551005f2 100755 --- a/script/publish.ts +++ b/script/publish.ts @@ -90,13 +90,11 @@ await $`bun install` await import(`../packages/sdk/js/script/build.ts`) if (Script.release) { - // kilocode_change start - commit and tag both release and rc version bumps + // kilocode_change start - commit and tag the release locally; the push to + // origin is deferred until after all package publishing to minimise the + // window in which a concurrent merge to main could cause a race condition. await $`git commit -am "release: v${Script.version}"` await $`git tag v${Script.version}` - await $`git fetch origin` - await $`git cherry-pick HEAD..origin/main`.nothrow() - await $`git push origin HEAD --tags --no-verify --force-with-lease` - await new Promise((resolve) => setTimeout(resolve, 5_000)) // kilocode_change end // kilocode_change start @@ -140,6 +138,35 @@ await import(`../packages/kilo-vscode/script/publish.ts`) const dir = fileURLToPath(new URL("..", import.meta.url)) process.chdir(dir) +// kilocode_change start - push the release commit to origin after all packages +// are published. Rebasing (instead of cherry-picking) on top of origin/main +// handles concurrent merges cleanly. A retry loop covers the narrow window +// between fetch and push where another commit could land. +if (Script.release) { + const retries = 3 + for (let i = 1; i <= retries; i++) { + await $`git fetch origin main` + const rebase = await $`git rebase origin/main`.nothrow() + if (rebase.exitCode !== 0) { + console.error(`rebase failed (attempt ${i}/${retries}), aborting rebase`) + await $`git rebase --abort`.nothrow() + if (i === retries) + throw new Error("failed to rebase release commit onto origin/main after " + retries + " attempts") + await new Promise((r) => setTimeout(r, 3_000)) + continue + } + const push = await $`git push origin HEAD:main --tags --no-verify --force-with-lease`.nothrow() + if (push.exitCode === 0) { + console.log("release commit pushed successfully") + break + } + console.warn(`push rejected (attempt ${i}/${retries}), retrying...`) + if (i === retries) throw new Error("failed to push release commit after " + retries + " attempts") + await new Promise((r) => setTimeout(r, 3_000)) + } +} +// kilocode_change end + // kilocode_change start - extract latest changelog section for release notes function extractLatestSection(changelog: string): string { if (!changelog) return "" From 42524dbf64a38713caec9a39540ad232fafa96a6 Mon Sep 17 00:00:00 2001 From: "kiloconnect[bot]" <240665456+kiloconnect[bot]@users.noreply.github.com> Date: Tue, 21 Apr 2026 14:23:50 +0000 Subject: [PATCH 2/2] fix(cli): replace cherry-pick with rebase + retry in publish push Keep the original order (push before publish) to preserve the safe failure mode, but replace the cherry-pick with rebase and add a 3-attempt retry loop to shrink the race window. --- script/publish.ts | 57 +++++++++++++++++++++-------------------------- 1 file changed, 25 insertions(+), 32 deletions(-) diff --git a/script/publish.ts b/script/publish.ts index 20551005f2..5036461cbb 100755 --- a/script/publish.ts +++ b/script/publish.ts @@ -90,11 +90,33 @@ await $`bun install` await import(`../packages/sdk/js/script/build.ts`) if (Script.release) { - // kilocode_change start - commit and tag the release locally; the push to - // origin is deferred until after all package publishing to minimise the - // window in which a concurrent merge to main could cause a race condition. + // kilocode_change start - commit, tag, and push with rebase + retry to handle + // concurrent merges to main. Rebase (instead of cherry-pick) handles + // overlapping file changes cleanly, and the retry loop covers the narrow + // window between fetch and push where another commit could land. await $`git commit -am "release: v${Script.version}"` await $`git tag v${Script.version}` + const retries = 3 + for (let i = 1; i <= retries; i++) { + await $`git fetch origin main` + const rebase = await $`git rebase origin/main`.nothrow() + if (rebase.exitCode !== 0) { + console.error(`rebase failed (attempt ${i}/${retries}), aborting rebase`) + await $`git rebase --abort`.nothrow() + if (i === retries) + throw new Error("failed to rebase release commit onto origin/main after " + retries + " attempts") + await new Promise((r) => setTimeout(r, 3_000)) + continue + } + const push = await $`git push origin HEAD:main --tags --no-verify --force-with-lease`.nothrow() + if (push.exitCode === 0) { + console.log("release commit pushed successfully") + break + } + console.warn(`push rejected (attempt ${i}/${retries}), retrying...`) + if (i === retries) throw new Error("failed to push release commit after " + retries + " attempts") + await new Promise((r) => setTimeout(r, 3_000)) + } // kilocode_change end // kilocode_change start @@ -138,35 +160,6 @@ await import(`../packages/kilo-vscode/script/publish.ts`) const dir = fileURLToPath(new URL("..", import.meta.url)) process.chdir(dir) -// kilocode_change start - push the release commit to origin after all packages -// are published. Rebasing (instead of cherry-picking) on top of origin/main -// handles concurrent merges cleanly. A retry loop covers the narrow window -// between fetch and push where another commit could land. -if (Script.release) { - const retries = 3 - for (let i = 1; i <= retries; i++) { - await $`git fetch origin main` - const rebase = await $`git rebase origin/main`.nothrow() - if (rebase.exitCode !== 0) { - console.error(`rebase failed (attempt ${i}/${retries}), aborting rebase`) - await $`git rebase --abort`.nothrow() - if (i === retries) - throw new Error("failed to rebase release commit onto origin/main after " + retries + " attempts") - await new Promise((r) => setTimeout(r, 3_000)) - continue - } - const push = await $`git push origin HEAD:main --tags --no-verify --force-with-lease`.nothrow() - if (push.exitCode === 0) { - console.log("release commit pushed successfully") - break - } - console.warn(`push rejected (attempt ${i}/${retries}), retrying...`) - if (i === retries) throw new Error("failed to push release commit after " + retries + " attempts") - await new Promise((r) => setTimeout(r, 3_000)) - } -} -// kilocode_change end - // kilocode_change start - extract latest changelog section for release notes function extractLatestSection(changelog: string): string { if (!changelog) return ""