diff --git a/lib/prepare_release.js b/lib/prepare_release.js
index 9f3c3866..6b0aa131 100644
--- a/lib/prepare_release.js
+++ b/lib/prepare_release.js
@@ -4,7 +4,7 @@ import { promises as fs } from 'node:fs';
import semver from 'semver';
import { replaceInFile } from 'replace-in-file';
-import { runAsync, runSync } from './run.js';
+import { forceRunAsync, runAsync, runSync } from './run.js';
import { writeJson, readJson } from './file.js';
import Request from './request.js';
import auth from './auth.js';
@@ -171,7 +171,7 @@ export default class ReleasePreparation extends Session {
// Check the branch diff to determine if the releaser
// wants to backport any more commits before proceeding.
cli.startSpinner('Fetching branch-diff');
- const raw = this.getBranchDiff({
+ const raw = await this.getBranchDiff({
onlyNotableChanges: false,
comparisonBranch: newVersion
});
@@ -181,10 +181,9 @@ export default class ReleasePreparation extends Session {
const outstandingCommits = diff.length - 1;
if (outstandingCommits !== 0) {
- const staging = `v${semver.major(newVersion)}.x-staging`;
const proceed = await cli.prompt(
`There are ${outstandingCommits} commits that may be ` +
- `backported to ${staging} - do you still want to proceed?`,
+ `backported to ${this.stagingBranch} - do you still want to proceed?`,
{ defaultAnswer: false });
if (!proceed) {
@@ -335,18 +334,8 @@ export default class ReleasePreparation extends Session {
return missing;
}
- async calculateNewVersion(major) {
- const { cli } = this;
-
- cli.startSpinner(`Parsing CHANGELOG for most recent release of v${major}.x`);
- const data = await fs.readFile(
- path.resolve(`doc/changelogs/CHANGELOG_V${major}.md`),
- 'utf8'
- );
- const [,, minor, patch] = /\1\.\2\.\3<\/a>
/.exec(data);
-
- cli.stopSpinner(`Latest release on ${major}.x line is ${major}.${minor}.${patch}`);
- const changelog = this.getChangelog(`v${major}.${minor}.${patch}`);
+ async calculateNewVersion({ tagName, major, minor, patch }) {
+ const changelog = this.getChangelog(tagName);
const newVersion = { major, minor, patch };
if (changelog.includes('SEMVER-MAJOR')) {
@@ -478,7 +467,7 @@ export default class ReleasePreparation extends Session {
const data = await fs.readFile(majorChangelogPath, 'utf8');
const arr = data.split('\n');
const allCommits = this.getChangelog();
- const notableChanges = this.getBranchDiff({ onlyNotableChanges: true });
+ const notableChanges = await this.getBranchDiff({ onlyNotableChanges: true });
let releaseHeader = `## ${date}, Version ${newVersion}` +
` ${releaseInfo}, @${username}\n`;
if (isSecurityRelease) {
@@ -532,14 +521,14 @@ export default class ReleasePreparation extends Session {
}
async createProposalBranch(base = this.stagingBranch) {
- const { upstream, newVersion } = this;
+ const { newVersion } = this;
const proposalBranch = `v${newVersion}-proposal`;
await runAsync('git', [
'checkout',
'-b',
proposalBranch,
- `${upstream}/${base}`
+ base
]);
return proposalBranch;
}
@@ -614,7 +603,7 @@ export default class ReleasePreparation extends Session {
messageBody.push('This is a security release.\n\n');
}
- const notableChanges = this.getBranchDiff({
+ const notableChanges = await this.getBranchDiff({
onlyNotableChanges: true,
format: 'plaintext'
});
@@ -641,8 +630,9 @@ export default class ReleasePreparation extends Session {
return useMessage;
}
- getBranchDiff(opts) {
+ async getBranchDiff(opts) {
const {
+ cli,
versionComponents = {},
upstream,
newVersion,
@@ -670,6 +660,10 @@ export default class ReleasePreparation extends Session {
'semver-minor'
];
+ await forceRunAsync('git', ['remote', 'set-branches', '--add', upstream, releaseBranch], {
+ ignoreFailures: false
+ });
+ await forceRunAsync('git', ['fetch', upstream, releaseBranch], { ignoreFailures: false });
branchDiffOptions = [
`${upstream}/${releaseBranch}`,
proposalBranch,
@@ -688,20 +682,43 @@ export default class ReleasePreparation extends Session {
'baking-for-lts'
];
- let comparisonBranch = 'main';
+ let comparisonBranch = this.config.branch || 'main';
const isSemverMinor = versionComponents.patch === 0;
if (isLTS) {
+ const res = await fetch('https://nodejs.org/dist/index.json');
+ if (!res.ok) throw new Error('Failed to fetch', { cause: res });
+ const [latest] = await res.json();
// Assume Current branch matches tag with highest semver value.
- const tags = runSync('git',
- ['tag', '-l', '--sort', '-version:refname']).trim();
- const highestVersionTag = tags.split('\n')[0];
- comparisonBranch = `v${semver.coerce(highestVersionTag).major}.x`;
+ comparisonBranch = `v${semver.coerce(latest.version).major}.x`;
if (!isSemverMinor) {
excludeLabels.push('semver-minor');
}
}
+ await forceRunAsync('git', ['fetch', upstream, comparisonBranch], { ignoreFailures: false });
+ const commits = await forceRunAsync('git', ['rev-parse', 'FETCH_HEAD', comparisonBranch], {
+ captureStdout: 'lines',
+ ignoreFailures: true
+ });
+ if (commits == null) {
+ const shouldCreateCompareBranch = await cli.prompt(
+ `No local branch ${comparisonBranch}, do you want to create it?`);
+ if (shouldCreateCompareBranch) {
+ await forceRunAsync('git', ['branch', comparisonBranch, 'FETCH_HEAD'], {
+ ignoreFailures: false
+ });
+ }
+ } else if (commits[0] !== commits[1]) {
+ const shouldUpBranch = cli.prompt(`Local ${comparisonBranch} branch is not in sync with ${
+ upstream}/${comparisonBranch}, do you want to update it?`);
+ if (shouldUpBranch) {
+ await forceRunAsync('git', ['branch', '-f', comparisonBranch, 'FETCH_HEAD'], {
+ ignoreFailures: false
+ });
+ }
+ }
+
branchDiffOptions = [
stagingBranch,
comparisonBranch,
@@ -718,6 +735,27 @@ export default class ReleasePreparation extends Session {
return runSync(branchDiff, branchDiffOptions);
}
+ async getLastRelease(major) {
+ const { cli } = this;
+
+ cli.startSpinner(`Parsing CHANGELOG for most recent release of v${major}.x`);
+ const data = await fs.readFile(
+ path.resolve(`doc/changelogs/CHANGELOG_V${major}.md`),
+ 'utf8'
+ );
+ const [,, minor, patch] = /\1\.\2\.\3<\/a>
/.exec(data);
+ this.isLTS = data.includes('