Skip to content

Commit 608f4a1

Browse files
committed
feat(git-node): auto-fetch comparison branch when preparing release
1 parent 15ae401 commit 608f4a1

File tree

1 file changed

+60
-23
lines changed

1 file changed

+60
-23
lines changed

lib/prepare_release.js

Lines changed: 60 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { promises as fs } from 'node:fs';
44
import semver from 'semver';
55
import { replaceInFile } from 'replace-in-file';
66

7-
import { runAsync, runSync } from './run.js';
7+
import { forceRunAsync, runAsync, runSync } from './run.js';
88
import { writeJson, readJson } from './file.js';
99
import Request from './request.js';
1010
import auth from './auth.js';
@@ -171,7 +171,7 @@ export default class ReleasePreparation extends Session {
171171
// Check the branch diff to determine if the releaser
172172
// wants to backport any more commits before proceeding.
173173
cli.startSpinner('Fetching branch-diff');
174-
const raw = this.getBranchDiff({
174+
const raw = await this.getBranchDiff({
175175
onlyNotableChanges: false,
176176
comparisonBranch: newVersion
177177
});
@@ -335,18 +335,8 @@ export default class ReleasePreparation extends Session {
335335
return missing;
336336
}
337337

338-
async calculateNewVersion(major) {
339-
const { cli } = this;
340-
341-
cli.startSpinner(`Parsing CHANGELOG for most recent release of v${major}.x`);
342-
const data = await fs.readFile(
343-
path.resolve(`doc/changelogs/CHANGELOG_V${major}.md`),
344-
'utf8'
345-
);
346-
const [,, minor, patch] = /<a href="#(\d+)\.(\d+)\.(\d+)">\1\.\2\.\3<\/a><br\/>/.exec(data);
347-
348-
cli.stopSpinner(`Latest release on ${major}.x line is ${major}.${minor}.${patch}`);
349-
const changelog = this.getChangelog(`v${major}.${minor}.${patch}`);
338+
async calculateNewVersion({ tagName, major, minor, patch }) {
339+
const changelog = this.getChangelog(tagName);
350340

351341
const newVersion = { major, minor, patch };
352342
if (changelog.includes('SEMVER-MAJOR')) {
@@ -478,7 +468,7 @@ export default class ReleasePreparation extends Session {
478468
const data = await fs.readFile(majorChangelogPath, 'utf8');
479469
const arr = data.split('\n');
480470
const allCommits = this.getChangelog();
481-
const notableChanges = this.getBranchDiff({ onlyNotableChanges: true });
471+
const notableChanges = await this.getBranchDiff({ onlyNotableChanges: true });
482472
let releaseHeader = `## ${date}, Version ${newVersion}` +
483473
` ${releaseInfo}, @${username}\n`;
484474
if (isSecurityRelease) {
@@ -614,7 +604,7 @@ export default class ReleasePreparation extends Session {
614604
messageBody.push('This is a security release.\n\n');
615605
}
616606

617-
const notableChanges = this.getBranchDiff({
607+
const notableChanges = await this.getBranchDiff({
618608
onlyNotableChanges: true,
619609
format: 'plaintext'
620610
});
@@ -641,8 +631,9 @@ export default class ReleasePreparation extends Session {
641631
return useMessage;
642632
}
643633

644-
getBranchDiff(opts) {
634+
async getBranchDiff(opts) {
645635
const {
636+
cli,
646637
versionComponents = {},
647638
upstream,
648639
newVersion,
@@ -670,6 +661,7 @@ export default class ReleasePreparation extends Session {
670661
'semver-minor'
671662
];
672663

664+
await forceRunAsync('git', ['fetch', upstream, releaseBranch], { ignoreFailures: false });
673665
branchDiffOptions = [
674666
`${upstream}/${releaseBranch}`,
675667
proposalBranch,
@@ -688,20 +680,43 @@ export default class ReleasePreparation extends Session {
688680
'baking-for-lts'
689681
];
690682

691-
let comparisonBranch = 'main';
683+
let comparisonBranch = this.config.branch || 'main';
692684
const isSemverMinor = versionComponents.patch === 0;
693685
if (isLTS) {
686+
const res = await fetch('https://nodejs.org/dist/index.json');
687+
if (!res.ok) throw new Error('Failed to fetch', { cause: res });
688+
const [latest] = await res.json();
694689
// Assume Current branch matches tag with highest semver value.
695-
const tags = runSync('git',
696-
['tag', '-l', '--sort', '-version:refname']).trim();
697-
const highestVersionTag = tags.split('\n')[0];
698-
comparisonBranch = `v${semver.coerce(highestVersionTag).major}.x`;
690+
comparisonBranch = `v${semver.coerce(latest.version).major}.x`;
699691

700692
if (!isSemverMinor) {
701693
excludeLabels.push('semver-minor');
702694
}
703695
}
704696

697+
await forceRunAsync('git', ['fetch', upstream, comparisonBranch], { ignoreFailures: false });
698+
const commits = await forceRunAsync('git', ['rev-parse', 'FETCH_HEAD', comparisonBranch], {
699+
captureStdout: 'lines',
700+
ignoreFailures: true
701+
});
702+
if (commits == null) {
703+
const shouldCreateCompareBranch = await cli.prompt(
704+
`No local branch ${comparisonBranch}, do you want to create it?`);
705+
if (shouldCreateCompareBranch) {
706+
await forceRunAsync('git', ['branch', comparisonBranch, 'FETCH_HEAD'], {
707+
ignoreFailures: false
708+
});
709+
}
710+
} else if (commits[0] !== commits[1]) {
711+
const shouldUpBranch = cli.prompt(`Local ${comparisonBranch} branch is not in sync with ${
712+
upstream}/${comparisonBranch}, do you want to update it?`);
713+
if (shouldUpBranch) {
714+
await forceRunAsync('git', ['branch', '-f', comparisonBranch, 'FETCH_HEAD'], {
715+
ignoreFailures: false
716+
});
717+
}
718+
}
719+
705720
branchDiffOptions = [
706721
stagingBranch,
707722
comparisonBranch,
@@ -718,6 +733,27 @@ export default class ReleasePreparation extends Session {
718733
return runSync(branchDiff, branchDiffOptions);
719734
}
720735

736+
async getLastRelease(major) {
737+
const { cli } = this;
738+
739+
cli.startSpinner(`Parsing CHANGELOG for most recent release of v${major}.x`);
740+
const data = await fs.readFile(
741+
path.resolve(`doc/changelogs/CHANGELOG_V${major}.md`),
742+
'utf8'
743+
);
744+
const [,, minor, patch] = /<a href="#(\d+)\.(\d+)\.(\d+)">\1\.\2\.\3<\/a><br\/>/.exec(data);
745+
this.isLTS = data.includes('<th>LTS ');
746+
747+
cli.stopSpinner(`Latest release on ${major}.x line is ${major}.${minor}.${patch}${
748+
this.isLTS ? ' (LTS)' : ''
749+
}`);
750+
751+
return {
752+
tagName: await this.getLastRef(`v${major}.${minor}.${patch}`),
753+
major, minor, patch
754+
};
755+
}
756+
721757
async prepareLocalBranch() {
722758
const { cli } = this;
723759
if (this.newVersion) {
@@ -736,6 +772,7 @@ export default class ReleasePreparation extends Session {
736772
this.stagingBranch = `v${newVersion.major}.x-staging`;
737773
this.releaseBranch = `v${newVersion.major}.x`;
738774
await this.tryResetBranch();
775+
await this.getLastRelease(newVersion.major);
739776
return;
740777
}
741778

@@ -751,7 +788,7 @@ export default class ReleasePreparation extends Session {
751788
}
752789
this.stagingBranch = currentBranch;
753790
await this.tryResetBranch();
754-
this.versionComponents = await this.calculateNewVersion(match[1]);
791+
this.versionComponents = await this.calculateNewVersion(await this.getLastRelease(match[1]));
755792
const { major, minor, patch } = this.versionComponents;
756793
this.newVersion = `${major}.${minor}.${patch}`;
757794
this.releaseBranch = `v${major}.x`;

0 commit comments

Comments
 (0)