From 17a048b78ccb3c0d61788aa3d21539c71b064d1a Mon Sep 17 00:00:00 2001 From: RafaelGSS Date: Thu, 9 Feb 2023 11:52:41 -0300 Subject: [PATCH] lib: support security prepare --- lib/prepare_release.js | 112 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 100 insertions(+), 12 deletions(-) diff --git a/lib/prepare_release.js b/lib/prepare_release.js index 750eab2a..5bf036ce 100644 --- a/lib/prepare_release.js +++ b/lib/prepare_release.js @@ -57,6 +57,7 @@ export default class ReleasePreparation { }; this.stagingBranch = `v${this.versionComponents.major}.x-staging`; + this.releaseBranch = `v${this.versionComponents.major}.x`; const upstreamHref = runSync('git', [ 'config', '--get', @@ -67,9 +68,89 @@ export default class ReleasePreparation { } } + async prepareSecurity() { + const { cli, newVersion, versionComponents, releaseBranch } = this; + + // Create new proposal branch. + cli.startSpinner(`Creating new proposal branch for ${newVersion}`); + await this.createProposalBranch(releaseBranch); + cli.stopSpinner(`Created new proposal branch for ${newVersion}`); + + let cherryPicked = await cli.prompt('Were all patches cherry picked?'); + while (!cherryPicked) { + cli.warn( + 'Cherry-pick all the patches in another terminal before proceeding' + ); + cherryPicked = await cli.prompt('Were all patches cherry picked?'); + } + + // Update version and release info in src/node_version.h. + cli.startSpinner(`Updating 'src/node_version.h' for ${newVersion}`); + await this.updateNodeVersion(); + cli.stopSpinner(`Updated 'src/node_version.h' for ${newVersion}`); + + // Update any REPLACEME tags in the docs. + cli.startSpinner('Updating REPLACEME items in docs'); + await this.updateREPLACEMEs(); + cli.stopSpinner('Updated REPLACEME items in docs'); + + // Fetch date to use in release commit & changelogs. + const todayDate = new Date().toISOString().split('T')[0]; + this.date = await cli.prompt('Enter release date in YYYY-MM-DD format:', + { questionType: 'input', defaultAnswer: todayDate }); + + cli.startSpinner('Updating CHANGELOG.md'); + await this.updateMainChangelog(); + cli.stopSpinner('Updated CHANGELOG.md'); + + cli.startSpinner(`Updating CHANGELOG_V${versionComponents.major}.md`); + await this.updateMajorChangelog(); + cli.stopSpinner(`Updated CHANGELOG_V${versionComponents.major}.md`); + + await cli.prompt('Finished editing the changelogs?', + { defaultAnswer: false }); + + // Create release commit. + const shouldCreateReleaseCommit = await cli.prompt( + 'Create release commit?'); + if (!shouldCreateReleaseCommit) { + cli.warn(`Aborting \`git node release\` for version ${newVersion}`); + return; + } + + // Proceed with release only after the releaser has amended + // it to their liking. + const createDefaultCommit = await this.createReleaseCommit(); + if (!createDefaultCommit) { + const lastCommitSha = runSync('git', ['rev-parse', '--short', 'HEAD']); + cli.warn(`Please manually edit commit ${lastCommitSha} by running ` + + '`git commit --amend` before proceeding.'); + + await cli.prompt( + 'Finished editing the release commit?', + { defaultAnswer: false }); + } + + cli.separator(); + cli.ok(`Release preparation for ${newVersion} complete.\n`); + cli.info( + 'To finish the release proposal, run: \n' + + ` $ git push -u ${this.upstream} v${newVersion}-proposal\n` + + 'Finally, proceed to Jenkins and begin the following CI jobs:\n' + + ' * https://ci.nodejs.org/job/node-test-pull-request/\n' + + ' * https://ci.nodejs.org/job/citgm-smoker/'); + cli.info( + 'If this release has deps/v8 changes, you\'ll also need to run:\n' + + ' * https://ci.nodejs.org/job/node-test-commit-v8-linux/' + ); + } + async prepare() { - const { cli, newVersion, versionComponents } = this; + const { cli, newVersion, versionComponents, isSecurityRelease } = this; + if (isSecurityRelease) { + return this.prepareSecurity(); + } // Check the branch diff to determine if the releaser // wants to backport any more commits before proceeding. cli.startSpinner('Fetching branch-diff'); @@ -431,15 +512,15 @@ export default class ReleasePreparation { await fs.writeFile(majorChangelogPath, arr.join('\n')); } - async createProposalBranch() { - const { upstream, newVersion, stagingBranch } = this; + async createProposalBranch(base = this.stagingBranch) { + const { upstream, newVersion } = this; const proposalBranch = `v${newVersion}-proposal`; await runAsync('git', [ 'checkout', '-b', proposalBranch, - `${upstream}/${stagingBranch}` + `${upstream}/${base}` ]); } @@ -618,28 +699,35 @@ export default class ReleasePreparation { } warnForWrongBranch() { - const { cli, stagingBranch, versionComponents } = this; + const { + cli, + stagingBranch, + releaseBranch, + versionComponents, + isSecurityRelease + } = this; const rev = this.getCurrentBranch(); - if (rev === stagingBranch) { - return false; - } - if (rev === 'HEAD') { cli.warn( 'You are in detached HEAD state. Please run git-node on a valid ' + 'branch'); return true; } + const targetBranch = isSecurityRelease ? releaseBranch : stagingBranch; + + if (rev === targetBranch) { + return false; + } cli.warn( 'You are trying to create a new release proposal branch for ' + `v${versionComponents.major}, but you're checked out on ` + - `${rev} and not ${stagingBranch}.`); + `${rev} and not ${targetBranch}.`); cli.separator(); cli.info( - `Switch to \`${stagingBranch}\` with \`git` + - ` checkout ${stagingBranch}\` before proceeding.`); + `Switch to \`${targetBranch}\` with \`git` + + ` checkout ${targetBranch}\` before proceeding.`); cli.separator(); return true; }