Skip to content

Commit f7939b5

Browse files
committed
feat: auto-close promoted channel release PRs
When a cycle promotes (channel → main) or graduates (channel → channel), an open release PR on the source channel offers another prerelease of a cycle that already moved on. Close it with an explanatory comment when the destination's version/release PR is created; a fresh one is created if new work lands on the channel.
1 parent 60e1f7a commit f7939b5

3 files changed

Lines changed: 66 additions & 0 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@varlock/bumpy': patch
3+
---
4+
5+
When a prerelease cycle is promoted (channel → main) or graduated (channel → channel), any lingering release PR on the source channel is now closed automatically with an explanatory comment — merging it would have offered another prerelease of a cycle that already moved on.

docs/prereleases.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,8 @@ When the prerelease has been tested and you're ready to ship the real `1.2.0`:
204204
- Deletes `.bumpy/next/`
205205
3. Merge that PR → bumpy publishes `1.2.0` to `@latest`, tags `v1.2.0`, and creates a stable GitHub release.
206206

207+
If the channel had an open release PR at promotion time (offering one more rc), it's now obsolete — bumpy closes it automatically with an explanatory comment when it creates the stable version PR. A fresh release PR appears if new work lands on the channel branch. The same applies to a source channel's release PR after a graduation merge (e.g. `alpha` → `beta`).
208+
207209
There is no special promotion mode. Promotion is literally "the bump files arrive on `main` and the stable flow eats them."
208210

209211
> The final stable `CHANGELOG.md` entry includes every change from the prerelease cycle — consumers of `@latest` see the full picture, not just the last rc's delta. Individual rc release notes remain available on the GitHub releases page.

packages/bumpy/src/commands/ci.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -681,10 +681,65 @@ async function createVersionPr(
681681
}
682682
}
683683

684+
// A promotion merge makes any open release PR on the source channel obsolete
685+
await closePromotedChannelReleasePrs(rootDir, config, plan.bumpFiles);
686+
684687
// Switch back to the base branch
685688
runArgs(['git', 'checkout', baseBranch], { cwd: rootDir });
686689
}
687690

691+
/**
692+
* Close lingering channel release PRs whose cycles were promoted: once a channel's
693+
* bump files are pending on this branch (via a promotion or graduation merge), the
694+
* source channel's own release PR is obsolete — merging it would re-publish a cycle
695+
* that's already moving to its next stage. A fresh release PR is created automatically
696+
* if new work lands on the channel branch.
697+
*/
698+
async function closePromotedChannelReleasePrs(
699+
rootDir: string,
700+
config: BumpyConfig,
701+
bumpFiles: BumpFile[],
702+
/** The channel whose release PR is being maintained right now (never close our own) */
703+
currentChannel?: ResolvedChannel,
704+
): Promise<void> {
705+
const promoted = [...new Set(bumpFiles.map((bf) => bf.channel))].filter(
706+
(name): name is string => name != null && name !== currentChannel?.name,
707+
);
708+
if (promoted.length === 0) return;
709+
710+
const channels = resolveChannels(config);
711+
for (const name of promoted) {
712+
const channel = channels.get(name);
713+
if (!channel) continue;
714+
const pr = tryRunArgs(
715+
['gh', 'pr', 'list', '--head', channel.versionPr.branch, '--json', 'number', '--jq', '.[0].number'],
716+
{ cwd: rootDir },
717+
);
718+
if (!pr) continue;
719+
const validPr = validatePrNumber(pr);
720+
log.step(`Closing release PR #${validPr} — the "${name}" cycle's changes are pending here now...`);
721+
try {
722+
await withPatToken(() =>
723+
runArgsAsync(
724+
[
725+
'gh',
726+
'pr',
727+
'close',
728+
validPr,
729+
'--comment',
730+
`Closing — the \`${name}\` cycle's bump files were promoted and are now pending a release here. ` +
731+
`A new release PR will be created automatically if more changes land on \`${channel.branch}\`.`,
732+
],
733+
{ cwd: rootDir },
734+
),
735+
);
736+
log.success(`🐸 Closed obsolete release PR #${validPr}`);
737+
} catch (e) {
738+
log.warn(` Failed to close release PR #${validPr}: ${e}`);
739+
}
740+
}
741+
}
742+
688743
// ---- channel (prerelease) release flow ----
689744

690745
/** Read the push event's before/after range, if running on a GitHub Actions push event */
@@ -919,6 +974,10 @@ async function createChannelReleasePr(
919974
await enableAutoMerge(rootDir, prNumber);
920975
}
921976

977+
// A graduation merge (e.g. alpha → beta) makes any open release PR on the
978+
// source channel obsolete
979+
await closePromotedChannelReleasePrs(rootDir, config, result.movedFiles, channel);
980+
922981
// Switch back to the channel branch
923982
runArgs(['git', 'checkout', baseBranch], { cwd: rootDir });
924983
}

0 commit comments

Comments
 (0)