Skip to content

Commit 197134f

Browse files
authored
feat(git-node): rebase staging commits after promoting a release (#934)
If there are commits on the staging branch, we probably want to keep them on top of the release commit. Staging branches and release proposal are usually in sync, however it's never the case for security releases.
1 parent 52994d8 commit 197134f

File tree

1 file changed

+26
-5
lines changed

1 file changed

+26
-5
lines changed

lib/promote_release.js

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,12 @@ export default class ReleasePromotion extends Session {
142142
const workingOnNewReleaseCommit = await this.setupForNextRelease();
143143
cli.stopSpinner('Successfully set up for next release');
144144

145+
const shouldRebaseStagingBranch = await cli.prompt(
146+
'Rebase staging branch on top of the release commit?', { defaultAnswer: true });
147+
const tipOfStagingBranch = shouldRebaseStagingBranch
148+
? await this.rebaseStagingBranch(workingOnNewReleaseCommit)
149+
: workingOnNewReleaseCommit;
150+
145151
// Cherry pick release commit to master.
146152
const shouldCherryPick = await cli.prompt(
147153
'Cherry-pick release commit to the default branch?', { defaultAnswer: true });
@@ -200,7 +206,7 @@ export default class ReleasePromotion extends Session {
200206
}
201207

202208
// Push to the remote the release tag, and default, release, and staging branch.
203-
await this.pushToRemote(workingOnNewReleaseCommit);
209+
await this.pushToRemote(workingOnNewReleaseCommit, tipOfStagingBranch);
204210

205211
// Promote and sign the release builds.
206212
await this.promoteAndSignRelease();
@@ -440,7 +446,7 @@ export default class ReleasePromotion extends Session {
440446
return workingOnNewReleaseCommit.trim();
441447
}
442448

443-
async pushToRemote(workingOnNewReleaseCommit) {
449+
async pushToRemote(workingOnNewReleaseCommit, tipOfStagingBranch) {
444450
const { cli, dryRun, version, versionComponents, stagingBranch } = this;
445451
const releaseBranch = `v${versionComponents.major}.x`;
446452
const tagVersion = `v${version}`;
@@ -454,8 +460,8 @@ export default class ReleasePromotion extends Session {
454460
cli.info(`git push ${this.upstream} ${
455461
this.defaultBranch} ${
456462
tagVersion} ${
457-
workingOnNewReleaseCommit}:refs/heads/${releaseBranch} ${
458-
workingOnNewReleaseCommit}:refs/heads/${stagingBranch}`);
463+
workingOnNewReleaseCommit}:refs/heads/${releaseBranch} +${
464+
tipOfStagingBranch}:refs/heads/${stagingBranch}`);
459465
cli.warn('Once pushed, you must not delete the local tag');
460466
prompt = 'Ready to continue?';
461467
}
@@ -471,7 +477,7 @@ export default class ReleasePromotion extends Session {
471477
cli.startSpinner('Pushing to remote');
472478
await forceRunAsync('git', ['push', this.upstream, this.defaultBranch, tagVersion,
473479
`${workingOnNewReleaseCommit}:refs/heads/${releaseBranch}`,
474-
`${workingOnNewReleaseCommit}:refs/heads/${stagingBranch}`],
480+
`+${tipOfStagingBranch}:refs/heads/${stagingBranch}`],
475481
{ ignoreFailure: false });
476482
cli.stopSpinner(`Pushed ${tagVersion}, ${this.defaultBranch}, ${
477483
releaseBranch}, and ${stagingBranch} to remote`);
@@ -507,6 +513,21 @@ export default class ReleasePromotion extends Session {
507513
cli.stopSpinner('Release has been signed and promoted');
508514
}
509515

516+
async rebaseStagingBranch(workingOnNewReleaseCommit) {
517+
const { cli, stagingBranch, upstream } = this;
518+
cli.startSpinner('Fetch staging branch');
519+
await forceRunAsync('git', ['fetch', upstream, stagingBranch], { ignoreFailure: false });
520+
cli.updateSpinner('Reset and rebase');
521+
await forceRunAsync('git', ['reset', 'FETCH_HEAD', '--hard'], { ignoreFailure: false });
522+
await forceRunAsync('git',
523+
['rebase', workingOnNewReleaseCommit, ...this.gpgSign], { ignoreFailure: false });
524+
const tipOfStagingBranch = await forceRunAsync('git', ['rev-parse', 'HEAD'],
525+
{ ignoreFailure: false, captureStdout: true });
526+
cli.stopSpinner('Rebased successfully');
527+
528+
return tipOfStagingBranch.trim();
529+
}
530+
510531
async cherryPickToDefaultBranch() {
511532
this.defaultBranch ??= await this.getDefaultBranch();
512533
const releaseCommitSha = this.releaseCommitSha;

0 commit comments

Comments
 (0)