@node-core/utils 6.2.1 → 6.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/ncu-ci.js CHANGED
@@ -119,6 +119,11 @@ const args = yargs(hideBin(process.argv))
119
119
  'If not provided, the command will use the SHA of the last approved commit.',
120
120
  type: 'string'
121
121
  })
122
+ .option('check-for-duplicates', {
123
+ describe: 'When set, NCU will query Jenkins recent builds to ensure ' +
124
+ 'there is not an existing job for the same commit.',
125
+ type: 'boolean'
126
+ })
122
127
  .option('owner', {
123
128
  default: '',
124
129
  describe: 'GitHub repository owner'
@@ -298,7 +303,10 @@ class RunPRJobCommand {
298
303
  this.cli.setExitCode(1);
299
304
  return;
300
305
  }
301
- const jobRunner = new RunPRJob(cli, request, owner, repo, prid, this.argv.certifySafe);
306
+ const { certifySafe, checkForDuplicates } = this.argv;
307
+ const jobRunner = new RunPRJob(
308
+ cli, request, owner, repo, prid,
309
+ certifySafe, checkForDuplicates);
302
310
  if (!(await jobRunner.start())) {
303
311
  this.cli.setExitCode(1);
304
312
  process.exitCode = 1;
@@ -189,6 +189,7 @@ async function main(state, argv, cli, dir) {
189
189
  cli.warn(
190
190
  'Failed to detect previous session. ' +
191
191
  'please run `git node land --abort`');
192
+ cli.setErrorCode(1);
192
193
  return;
193
194
  }
194
195
 
@@ -197,17 +198,12 @@ async function main(state, argv, cli, dir) {
197
198
  cli.warn(
198
199
  'Previous `git node land` session for ' +
199
200
  `${session.pullName} in progress.`);
200
- cli.log('run `git node land --abort` before starting a new session');
201
- return;
201
+ const response = await cli.prompt('Do you want to abort it?', { defaultAnswer: false });
202
+ if (!response) throw new Error('Please run `git node land --abort`');
203
+ await session.abort();
204
+ session = new LandingSession(cli, req, dir, argv);
202
205
  }
203
- session = new LandingSession(cli, req, dir, argv);
204
206
  const metadata = await getMetadata(session.argv, argv.skipRefs, cli);
205
- if (argv.backport) {
206
- const split = metadata.metadata.split('\n')[0];
207
- if (split === 'PR-URL: ') {
208
- cli.error('Commit message is missing PR-URL');
209
- }
210
- }
211
207
  return session.start(metadata);
212
208
  } else if (state === APPLY) {
213
209
  return session.apply();
@@ -92,110 +92,93 @@ export function builder(yargs) {
92
92
  }
93
93
 
94
94
  export function handler(argv) {
95
+ const logStream = process.stdout.isTTY ? process.stdout : process.stderr;
96
+ const cli = new CLI(logStream);
97
+
95
98
  if (argv.start) {
96
- return startSecurityRelease(argv);
99
+ return startSecurityRelease(cli, argv);
97
100
  }
98
101
  if (argv.sync) {
99
- return syncSecurityRelease(argv);
102
+ return syncSecurityRelease(cli, argv);
100
103
  }
101
104
  if (argv['update-date']) {
102
- return updateReleaseDate(argv);
105
+ return updateReleaseDate(cli, argv);
103
106
  }
104
107
  if (argv['pre-release']) {
105
- return createPreRelease(argv);
108
+ return createPreRelease(cli, argv);
106
109
  }
107
110
  if (argv['add-report']) {
108
- return addReport(argv);
111
+ return addReport(cli, argv);
109
112
  }
110
113
  if (argv['remove-report']) {
111
- return removeReport(argv);
114
+ return removeReport(cli, argv);
112
115
  }
113
116
  if (argv['notify-pre-release']) {
114
- return notifyPreRelease(argv);
117
+ return notifyPreRelease(cli, argv);
115
118
  }
116
119
  if (argv['request-cve']) {
117
- return requestCVEs(argv);
120
+ return requestCVEs(cli, argv);
118
121
  }
119
122
  if (argv['post-release']) {
120
- return createPostRelease(argv);
123
+ return createPostRelease(cli, argv);
121
124
  }
122
125
  if (argv.cleanup) {
123
- return cleanupSecurityRelease(argv);
126
+ return cleanupSecurityRelease(cli, argv);
124
127
  }
125
128
  yargsInstance.showHelp();
126
129
  }
127
130
 
128
- async function removeReport(argv) {
131
+ async function removeReport(cli, argv) {
129
132
  const reportId = argv['remove-report'];
130
- const logStream = process.stdout.isTTY ? process.stdout : process.stderr;
131
- const cli = new CLI(logStream);
132
133
  const update = new UpdateSecurityRelease(cli);
133
134
  return update.removeReport(reportId);
134
135
  }
135
136
 
136
- async function addReport(argv) {
137
+ async function addReport(cli, argv) {
137
138
  const reportId = argv['add-report'];
138
- const logStream = process.stdout.isTTY ? process.stdout : process.stderr;
139
- const cli = new CLI(logStream);
140
139
  const update = new UpdateSecurityRelease(cli);
141
140
  return update.addReport(reportId);
142
141
  }
143
142
 
144
- async function updateReleaseDate(argv) {
143
+ async function updateReleaseDate(cli, argv) {
145
144
  const releaseDate = argv['update-date'];
146
- const logStream = process.stdout.isTTY ? process.stdout : process.stderr;
147
- const cli = new CLI(logStream);
148
145
  const update = new UpdateSecurityRelease(cli);
149
146
  return update.updateReleaseDate(releaseDate);
150
147
  }
151
148
 
152
- async function createPreRelease(argv) {
149
+ async function createPreRelease(cli, argv) {
153
150
  const nodejsOrgFolder = argv['pre-release'];
154
- const logStream = process.stdout.isTTY ? process.stdout : process.stderr;
155
- const cli = new CLI(logStream);
156
151
  const preRelease = new SecurityBlog(cli);
157
152
  return preRelease.createPreRelease(nodejsOrgFolder);
158
153
  }
159
154
 
160
- async function requestCVEs() {
161
- const logStream = process.stdout.isTTY ? process.stdout : process.stderr;
162
- const cli = new CLI(logStream);
155
+ async function requestCVEs(cli) {
163
156
  const hackerOneCve = new UpdateSecurityRelease(cli);
164
157
  return hackerOneCve.requestCVEs();
165
158
  }
166
159
 
167
- async function createPostRelease(argv) {
160
+ async function createPostRelease(cli, argv) {
168
161
  const nodejsOrgFolder = argv['post-release'];
169
- const logStream = process.stdout.isTTY ? process.stdout : process.stderr;
170
- const cli = new CLI(logStream);
171
162
  const blog = new SecurityBlog(cli);
172
163
  return blog.createPostRelease(nodejsOrgFolder);
173
164
  }
174
165
 
175
- async function startSecurityRelease() {
176
- const logStream = process.stdout.isTTY ? process.stdout : process.stderr;
177
- const cli = new CLI(logStream);
166
+ async function startSecurityRelease(cli) {
178
167
  const release = new PrepareSecurityRelease(cli);
179
168
  return release.start();
180
169
  }
181
170
 
182
- async function cleanupSecurityRelease() {
183
- const logStream = process.stdout.isTTY ? process.stdout : process.stderr;
184
- const cli = new CLI(logStream);
171
+ async function cleanupSecurityRelease(cli) {
185
172
  const release = new PrepareSecurityRelease(cli);
186
173
  return release.cleanup();
187
174
  }
188
175
 
189
- async function syncSecurityRelease(argv) {
190
- const logStream = process.stdout.isTTY ? process.stdout : process.stderr;
191
- const cli = new CLI(logStream);
176
+ async function syncSecurityRelease(cli) {
192
177
  const release = new UpdateSecurityRelease(cli);
193
178
  return release.sync();
194
179
  }
195
180
 
196
- async function notifyPreRelease() {
197
- const logStream = process.stdout.isTTY ? process.stdout : process.stderr;
198
- const cli = new CLI(logStream);
181
+ async function notifyPreRelease(cli) {
199
182
  const preRelease = new SecurityAnnouncement(cli);
200
183
  return preRelease.notifyPreRelease();
201
184
  }
package/lib/auth.js CHANGED
@@ -1,32 +1,28 @@
1
1
  import fs from 'node:fs';
2
2
  import { ClientRequest } from 'node:http';
3
3
 
4
+ // eslint-disable-next-line import/no-unresolved
4
5
  import ghauth from 'ghauth';
5
6
 
6
7
  import { clearCachedConfig, encryptValue, getMergedConfig, getNcurcPath } from './config.js';
7
8
 
8
9
  export default lazy(auth);
9
10
 
10
- function errorExit(message) {
11
- process.stderr.write(`${message}\n`);
12
- process.exit(1);
13
- }
14
-
15
11
  function check(username, token, format = /^[A-Za-z0-9_]+$/) {
16
12
  if (typeof username !== 'string') {
17
- errorExit(`username must be a string, received ${typeof username}`);
13
+ throw new Error(`username must be a string, received ${typeof username}`);
18
14
  }
19
15
  if (!/^[a-zA-Z0-9-]+$/.test(username)) {
20
- errorExit(
16
+ throw new Error(
21
17
  'username may only contain alphanumeric characters or hyphens, ' +
22
18
  `received ${username}`
23
19
  );
24
20
  }
25
21
  if (typeof token !== 'string') {
26
- errorExit(`token must be a string, received ${typeof token}`);
22
+ throw new Error(`token must be a string, received ${typeof token}`);
27
23
  }
28
24
  if (!format.test(token)) {
29
- errorExit(`token is misformatted: ${token}`);
25
+ throw new Error(`token is misformatted: ${token}`);
30
26
  }
31
27
  }
32
28
 
@@ -51,7 +47,7 @@ async function tryCreateGitHubToken(githubAuth) {
51
47
  noDeviceFlow: true
52
48
  });
53
49
  } catch (e) {
54
- errorExit(`Could not get token: ${e.message}`);
50
+ throw new Error(`Could not get token: ${e.message}`, { cause: e });
55
51
  }
56
52
  return credentials;
57
53
  }
@@ -93,7 +89,7 @@ async function auth(
93
89
  get jenkins() {
94
90
  const { username, jenkins_token } = getMergedConfig();
95
91
  if (!username || !jenkins_token) {
96
- errorExit(
92
+ throw new Error(
97
93
  'Get your Jenkins API token in https://ci.nodejs.org/me/security ' +
98
94
  'and run the following command to add it to your ncu config: ' +
99
95
  'ncu-config --global set -x jenkins_token'
@@ -15,9 +15,8 @@ const {
15
15
  } = CIFailureParser;
16
16
 
17
17
  export class PRBuild extends TestBuild {
18
- constructor(cli, request, id, skipMoreThan) {
18
+ constructor(cli, request, id, skipMoreThan, tree = PR_TREE) {
19
19
  const path = `job/node-test-pull-request/${id}/`;
20
- const tree = PR_TREE;
21
20
  super(cli, request, path, tree);
22
21
  this.skipMoreThan = skipMoreThan;
23
22
  this.commitBuild = null;
package/lib/ci/run_ci.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { FormData } from 'undici';
2
2
 
3
3
  import {
4
+ JobParser,
4
5
  CI_DOMAIN,
5
6
  CI_TYPES,
6
7
  CI_TYPES_KEYS
@@ -8,6 +9,7 @@ import {
8
9
  import PRData from '../pr_data.js';
9
10
  import { debuglog } from '../verbosity.js';
10
11
  import PRChecker from '../pr_checker.js';
12
+ import { PRBuild } from './build-types/pr_build.js';
11
13
 
12
14
  export const CI_CRUMB_URL = `https://${CI_DOMAIN}/crumbIssuer/api/json`;
13
15
  const CI_PR_NAME = CI_TYPES.get(CI_TYPES_KEYS.PR).jobName;
@@ -17,7 +19,7 @@ const CI_V8_NAME = CI_TYPES.get(CI_TYPES_KEYS.V8).jobName;
17
19
  export const CI_V8_URL = `https://${CI_DOMAIN}/job/${CI_V8_NAME}/build`;
18
20
 
19
21
  export class RunPRJob {
20
- constructor(cli, request, owner, repo, prid, certifySafe) {
22
+ constructor(cli, request, owner, repo, prid, certifySafe, checkForDuplicates) {
21
23
  this.cli = cli;
22
24
  this.request = request;
23
25
  this.owner = owner;
@@ -29,6 +31,7 @@ export class RunPRJob {
29
31
  Promise.all([this.prData.getReviews(), this.prData.getCommits()]).then(() =>
30
32
  (this.certifySafe = new PRChecker(cli, this.prData, request, {}).getApprovedTipOfHead())
31
33
  );
34
+ this.checkForDuplicates = checkForDuplicates;
32
35
  }
33
36
 
34
37
  async getCrumb() {
@@ -70,7 +73,7 @@ export class RunPRJob {
70
73
  }
71
74
 
72
75
  async start() {
73
- const { cli, certifySafe } = this;
76
+ const { cli, request, certifySafe, checkForDuplicates } = this;
74
77
 
75
78
  if (!(await certifySafe)) {
76
79
  cli.error('Refusing to run CI on potentially unsafe PR');
@@ -87,9 +90,25 @@ export class RunPRJob {
87
90
  }
88
91
  cli.stopSpinner('Jenkins credentials valid');
89
92
 
93
+ if (checkForDuplicates) {
94
+ await this.prData.getComments();
95
+ const { jobid, link } = new JobParser(this.prData.comments).parse().get('PR') ?? {};
96
+ const { actions } = jobid
97
+ ? (await new PRBuild(cli, request, jobid, undefined, 'actions[parameters[name,value]]')
98
+ .getBuildData())
99
+ : {};
100
+ const { parameters } = actions?.find(a => 'parameters' in a) ?? {};
101
+ if (parameters?.find(c => c.name === 'COMMIT_SHA_CHECK')?.value === certifySafe) {
102
+ cli.info('Existing CI run found: ' + link);
103
+ cli.error('Refusing to start a potentially duplicate CI job. Use the ' +
104
+ '"Resume build" button in the Jenkins UI, or start a new CI manually.');
105
+ return false;
106
+ }
107
+ }
108
+
90
109
  try {
91
110
  cli.startSpinner('Starting PR CI job');
92
- const response = await this.request.fetch(CI_PR_URL, {
111
+ const response = await request.fetch(CI_PR_URL, {
93
112
  method: 'POST',
94
113
  headers: {
95
114
  'Jenkins-Crumb': crumb
@@ -106,10 +125,10 @@ export class RunPRJob {
106
125
 
107
126
  // check if the job need a v8 build and trigger it
108
127
  await this.prData.getPR();
109
- const labels = this.prData.pr.labels;
110
- if (labels.nodes.map(i => i.name).includes('v8 engine')) {
128
+ const { labels } = this.prData.pr;
129
+ if (labels?.nodes.some(i => i.name === 'v8 engine')) {
111
130
  cli.startSpinner('Starting V8 CI job');
112
- const response = await this.request.fetch(CI_V8_URL, {
131
+ const response = await request.fetch(CI_V8_URL, {
113
132
  method: 'POST',
114
133
  headers: {
115
134
  'Jenkins-Crumb': crumb
@@ -82,12 +82,38 @@ export default class LandingSession extends Session {
82
82
  }
83
83
 
84
84
  async downloadAndPatch() {
85
- const { cli, upstream, prid, expectedCommitShas } = this;
85
+ const { cli, upstream, prid, expectedCommitShas, crossRepoPR } = this;
86
+
87
+ if (crossRepoPR) {
88
+ const { owner, repo } = this;
89
+ cli.warn(`The configured remote "${upstream}" does not point to the ` +
90
+ 'Pull Request repository.');
91
+ const isHeadAMergeCommit = () => {
92
+ try {
93
+ return runSync('git', ['rev-parse', 'FETCH_HEAD^2']).trim();
94
+ } catch {}
95
+ };
96
+ do {
97
+ cli.warn('Fetching commits from a different upstream might require user intervention.');
98
+
99
+ cli.info('Run the following command to fetch the commit(s):');
100
+ cli.info(`git fetch git@github.com:${owner}/${repo}.git refs/pull/${prid}/merge`);
101
+ const response = await cli.prompt('Should NCU run the command for you?',
102
+ { defaultAnswer: false });
103
+ if (response) {
104
+ cli.startSpinner(`Downloading patch for ${prid}`);
105
+ await forceRunAsync('git',
106
+ ['fetch', `git@github.com:${owner}/${repo}.git`, `refs/pull/${prid}/merge`],
107
+ { ignoreFailure: false });
108
+ }
109
+ } while (!isHeadAMergeCommit());
110
+ } else {
111
+ cli.startSpinner(`Downloading patch for ${prid}`);
112
+ await runAsync('git', [
113
+ 'fetch', upstream,
114
+ `refs/pull/${prid}/merge`]);
115
+ }
86
116
 
87
- cli.startSpinner(`Downloading patch for ${prid}`);
88
- await runAsync('git', [
89
- 'fetch', upstream,
90
- `refs/pull/${prid}/merge`]);
91
117
  // We fetched the commit that would result if we used `git merge`.
92
118
  // ^1 and ^2 refer to the PR base and the PR head, respectively.
93
119
  const [base, head] = await runAsync('git',
@@ -286,6 +312,12 @@ export default class LandingSession extends Session {
286
312
  cli.warn('Not yet ready to amend, run `git node land --abort`');
287
313
  return;
288
314
  }
315
+
316
+ const BACKPORT_RE = /^BACKPORT-PR-URL\s*:\s*(\S+)$/i;
317
+ const PR_RE = /^PR-URL\s*:\s*(\S+)$/i;
318
+ const REVIEW_RE = /^Reviewed-By\s*:\s*(\S+)$/i;
319
+ const CVE_RE = /^CVE-ID\s*:\s*(\S+)$/im;
320
+
289
321
  this.startAmending();
290
322
 
291
323
  const rev = this.getCurrentRev();
@@ -296,52 +328,40 @@ export default class LandingSession extends Session {
296
328
  // git has very specific rules about what is a trailer and what is not.
297
329
  // Instead of trying to implement those ourselves, let git parse the
298
330
  // original commit message and see if it outputs any trailers.
299
- const originalHasTrailers = runSync('git', [
331
+ const originalTrailers = runSync('git', [
300
332
  'interpret-trailers', '--parse', '--no-divider'
301
333
  ], {
302
334
  input: `${original}\n`
303
- }).trim().length !== 0;
335
+ });
336
+ const containCVETrailer = CVE_RE.test(originalTrailers);
304
337
 
305
338
  const metadata = this.metadata.trim().split('\n');
306
- const amended = original.split('\n');
307
-
308
- // If the original commit message already contains trailers (such as
309
- // "Co-authored-by"), we simply add our own metadata after those. Otherwise,
310
- // we have to add an empty line so that git recognizes our own metadata as
311
- // trailers in the amended commit message.
312
- if (!originalHasTrailers) {
313
- amended.push('');
314
- }
339
+ const amended = original.slice(0, -originalTrailers.length).trim().split('\n');
315
340
 
316
- const BACKPORT_RE = /BACKPORT-PR-URL\s*:\s*(\S+)/i;
317
- const PR_RE = /PR-URL\s*:\s*(\S+)/i;
318
- const REVIEW_RE = /Reviewed-By\s*:\s*(\S+)/i;
319
- const CVE_RE = /CVE-ID\s*:\s*(\S+)/i;
341
+ // Only keep existing trailers that we won't add ourselves
342
+ const keptTrailers = originalTrailers.split('\n').filter(line =>
343
+ !!line &&
344
+ (!PR_RE.test(line) || this.backport) &&
345
+ !BACKPORT_RE.test(line) &&
346
+ !REVIEW_RE.test(line));
347
+ amended.push('', ...keptTrailers);
320
348
 
321
- let containCVETrailer = false;
322
349
  for (const line of metadata) {
323
- if (line.length !== 0 && original.includes(line)) {
324
- if (line.match(CVE_RE)) {
325
- containCVETrailer = true;
326
- }
327
- if (originalHasTrailers) {
328
- cli.warn(`Found ${line}, skipping..`);
329
- } else {
330
- cli.error('Git found no trailers in the original commit message, ' +
331
- `but '${line}' is present and should be a trailer.`);
332
- process.exit(1); // make it work with git rebase -x
333
- }
334
- } else {
335
- if (line.match(BACKPORT_RE)) {
336
- let prIndex = amended.findIndex(datum => datum.match(PR_RE));
337
- if (prIndex === -1) {
338
- prIndex = amended.findIndex(datum => datum.match(REVIEW_RE)) - 1;
339
- }
340
- amended.splice(prIndex + 1, 0, line);
341
- } else {
342
- amended.push(line);
350
+ if (keptTrailers.includes(line)) {
351
+ cli.warn(`Found ${line}, skipping..`);
352
+ continue;
353
+ }
354
+
355
+ if (BACKPORT_RE.test(line)) {
356
+ const prIndex =
357
+ (amended.findIndex(datum => PR_RE.test(datum)) + 1) ||
358
+ amended.findIndex(datum => REVIEW_RE.test(datum));
359
+ if (prIndex !== -1) {
360
+ amended.splice(prIndex, 0, line);
361
+ continue;
343
362
  }
344
363
  }
364
+ amended.push(line);
345
365
  }
346
366
 
347
367
  if (!containCVETrailer && this.includeCVE) {
@@ -472,7 +492,7 @@ export default class LandingSession extends Session {
472
492
  }
473
493
 
474
494
  cli.separator();
475
- cli.log('The following commits are ready to be pushed to ' +
495
+ cli.log('The following commits are landable on ' +
476
496
  `${upstream}/${branch}`);
477
497
  cli.log(`- ${strayVerbose.join('\n- ')}`);
478
498
  cli.separator();
@@ -483,8 +503,15 @@ export default class LandingSession extends Session {
483
503
  willBeLanded = `${head}...${willBeLanded}`;
484
504
  }
485
505
 
506
+ cli.startSpinner('Removing temporary files');
486
507
  this.cleanFiles();
487
- cli.log('Temporary files removed.');
508
+ cli.stopSpinner('Temporary files removed.');
509
+ if (this.crossRepoPR) {
510
+ cli.warn(`The configured remote "${upstream}" does not point to the ` +
511
+ 'Pull Request repository.');
512
+ cli.info(`${willBeLanded} should likely not be pushed to "${upstream}"`);
513
+ return;
514
+ }
488
515
  cli.log('To finish landing:');
489
516
  cli.log('1. Run: ');
490
517
  cli.log(` git push ${upstream} ${branch}`);
@@ -31,7 +31,6 @@ export default class MetadataGenerator {
31
31
  const parser = new LinkParser(owner, repo, op);
32
32
  const fixes = parser.getFixes();
33
33
  const refs = parser.getRefs().filter(f => f !== prUrl);
34
- const altPrUrl = parser.getAltPrUrl();
35
34
 
36
35
  const meta = [];
37
36
 
@@ -44,17 +43,10 @@ export default class MetadataGenerator {
44
43
  meta.push(...refs.map((ref) => `Refs: ${ref}`));
45
44
  }
46
45
  const backport = this.argv ? this.argv.backport : undefined;
47
- if (backport) {
48
- meta.unshift(`Backport-PR-URL: ${prUrl}`);
49
- meta.unshift(`PR-URL: ${altPrUrl}`);
50
- } else {
51
- // Reviews are only added here as backports should not contain reviews
52
- // for the backport itself in the metadata
53
- meta.unshift(`PR-URL: ${prUrl}`);
54
- meta.push(
55
- ...reviewedBy.map((r) => `Reviewed-By: ${r.reviewer.getContact()}`)
56
- );
57
- }
46
+ meta.unshift(`${backport ? 'Backport-' : ''}PR-URL: ${prUrl}`);
47
+ meta.push(
48
+ ...reviewedBy.map((r) => `Reviewed-By: ${r.reviewer.getContact()}`)
49
+ );
58
50
  meta.push(''); // creates final EOL
59
51
  return meta.join('\n');
60
52
  }
@@ -133,88 +133,12 @@ export default class ReleasePreparation extends Session {
133
133
  return true;
134
134
  }
135
135
 
136
- async prepareSecurity() {
137
- const {
138
- cli,
139
- newVersion,
140
- versionComponents,
141
- releaseBranch,
142
- filterLabels
143
- } = this;
144
-
145
- // Create new proposal branch.
146
- cli.startSpinner(`Switching to proposal branch for ${newVersion}`);
147
- const proposalBranch = await this.createProposalBranch(releaseBranch);
148
- cli.stopSpinner(`Switched to proposal branch for ${newVersion}`);
149
-
150
- const success = await this.cherryPickSecurityPRs(filterLabels);
151
- if (!success) {
152
- cli.error('Aborting security release preparation. ' +
153
- 'Remember to exclude the proposal branch.' +
154
- 'git branch -D ' + proposalBranch);
155
- return;
156
- }
157
- // Update version and release info in src/node_version.h.
158
- cli.startSpinner(`Updating 'src/node_version.h' for ${newVersion}`);
159
- await this.updateNodeVersion();
160
- cli.stopSpinner(`Updated 'src/node_version.h' for ${newVersion}`);
161
-
162
- // Update any REPLACEME tags in the docs.
163
- cli.startSpinner('Updating REPLACEME items in docs');
164
- await this.updateREPLACEMEs();
165
- cli.stopSpinner('Updated REPLACEME items in docs');
166
-
167
- // Fetch date to use in release commit & changelogs.
168
- const todayDate = new Date().toISOString().split('T')[0];
169
- this.date = await cli.prompt('Enter release date in YYYY-MM-DD format:',
170
- { questionType: 'input', defaultAnswer: todayDate });
171
-
172
- cli.startSpinner('Updating CHANGELOG.md');
173
- await this.updateMainChangelog();
174
- cli.stopSpinner('Updated CHANGELOG.md');
175
-
176
- cli.startSpinner(`Updating CHANGELOG_V${versionComponents.major}.md`);
177
- await this.updateMajorChangelog();
178
- cli.stopSpinner(`Updated CHANGELOG_V${versionComponents.major}.md`);
179
-
180
- // Create release commit.
181
- const shouldCreateReleaseCommit = await cli.prompt(
182
- 'Create release commit?');
183
- if (!shouldCreateReleaseCommit) {
184
- cli.warn(`Aborting \`git node release\` for version ${newVersion}`);
185
- return;
186
- }
187
-
188
- // Proceed with release only after the releaser has amended
189
- // it to their liking.
190
- const createDefaultCommit = await this.createReleaseCommit();
191
- if (!createDefaultCommit) {
192
- const lastCommitSha = runSync('git', ['rev-parse', '--short', 'HEAD']);
193
- cli.warn(`Please manually edit commit ${lastCommitSha} by running ` +
194
- '`git commit --amend` before proceeding.');
195
- }
196
-
197
- cli.separator();
198
- cli.ok(`Release preparation for ${newVersion} complete.\n`);
199
- cli.info(
200
- 'To finish the release proposal, run: \n' +
201
- ` $ git push -u ${this.upstream} v${newVersion}-proposal\n` +
202
- 'Finally, proceed to Jenkins and begin the following CI jobs:\n' +
203
- ' * https://ci.nodejs.org/job/node-test-pull-request/\n' +
204
- ' * https://ci.nodejs.org/job/citgm-smoker/');
205
- cli.info(
206
- 'If this release has deps/v8 changes, you\'ll also need to run:\n' +
207
- ' * https://ci.nodejs.org/job/node-test-commit-v8-linux/'
208
- );
209
- }
210
-
211
136
  async prepare() {
212
137
  const { cli, newVersion, versionComponents, isSecurityRelease } = this;
213
138
 
214
139
  if (isSecurityRelease) {
215
140
  this.config.owner = 'nodejs-private';
216
141
  this.config.repo = 'node-private';
217
- return this.prepareSecurity();
218
142
  }
219
143
 
220
144
  const runBranchDiff = await cli.prompt(
@@ -222,30 +146,38 @@ export default class ReleasePreparation extends Session {
222
146
  '(recommended except for Maintenance releases)?',
223
147
  { defaultAnswer: this.runBranchDiff });
224
148
  if (runBranchDiff) {
225
- // TODO: UPDATE re-use
226
- // Check the branch diff to determine if the releaser
227
- // wants to backport any more commits before proceeding.
228
- cli.startSpinner('Fetching branch-diff');
229
- const raw = await this.getBranchDiff({
230
- onlyNotableChanges: false,
231
- comparisonBranch: newVersion
232
- });
233
-
234
- const diff = raw.split('*');
235
- cli.stopSpinner('Got branch diff');
236
-
237
- const outstandingCommits = diff.length - 1;
238
- if (outstandingCommits !== 0) {
239
- const proceed = await cli.prompt(
149
+ if (isSecurityRelease) {
150
+ const success = await this.cherryPickSecurityPRs(this.filterLabels);
151
+ if (!success) {
152
+ cli.error('Aborting security release preparation.');
153
+ return;
154
+ }
155
+ } else {
156
+ // TODO: UPDATE re-use
157
+ // Check the branch diff to determine if the releaser
158
+ // wants to backport any more commits before proceeding.
159
+ cli.startSpinner('Fetching branch-diff');
160
+ const raw = await this.getBranchDiff({
161
+ onlyNotableChanges: false,
162
+ comparisonBranch: newVersion
163
+ });
164
+
165
+ const diff = raw.split('*');
166
+ cli.stopSpinner('Got branch diff');
167
+
168
+ const outstandingCommits = diff.length - 1;
169
+ if (outstandingCommits !== 0) {
170
+ const proceed = await cli.prompt(
240
171
  `There are ${outstandingCommits} commits that may be ` +
241
172
  `backported to ${this.stagingBranch} - do you still want to proceed?`,
242
173
  { defaultAnswer: false });
243
174
 
244
- if (!proceed) {
245
- const seeDiff = await cli.prompt(
246
- 'Do you want to see the branch diff?');
247
- if (seeDiff) cli.log(raw);
248
- return;
175
+ if (!proceed) {
176
+ const seeDiff = await cli.prompt(
177
+ 'Do you want to see the branch diff?');
178
+ if (seeDiff) cli.log(raw);
179
+ return;
180
+ }
249
181
  }
250
182
  }
251
183
  }