@node-core/utils 5.3.0 → 5.4.0

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.
@@ -79,7 +79,7 @@ export function builder(yargs) {
79
79
  'Request CVEs for a security release of Node.js based on' +
80
80
  ' the next-security-release/vulnerabilities.json'
81
81
  ).example(
82
- 'git node security --post-release' +
82
+ 'git node security --post-release',
83
83
  'Create the post-release announcement on the Nodejs.org repo'
84
84
  );
85
85
  }
package/lib/pr_checker.js CHANGED
@@ -29,6 +29,7 @@ const GITHUB_SUCCESS_CONCLUSIONS = ['SUCCESS', 'NEUTRAL', 'SKIPPED'];
29
29
  const FAST_TRACK_RE = /^Fast-track has been requested by @(.+?)\. Please 👍 to approve\.$/;
30
30
  const FAST_TRACK_MIN_APPROVALS = 2;
31
31
  const GIT_CONFIG_GUIDE_URL = 'https://github.com/nodejs/node/blob/99b1ada/doc/guides/contributing/pull-requests.md#step-1-fork';
32
+ const IGNORED_CHECK_SLUGS = ['dependabot', 'codecov'];
32
33
 
33
34
  // eslint-disable-next-line no-extend-native
34
35
  Array.prototype.findLastIndex ??= function findLastIndex(fn) {
@@ -373,9 +374,9 @@ export default class PRChecker {
373
374
 
374
375
  // GitHub new Check API
375
376
  for (const { status, conclusion, app } of checkSuites.nodes) {
376
- if (app && app.slug === 'dependabot') {
377
- // Ignore Dependabot check suites. They are expected to show up
378
- // sometimes and never complete.
377
+ if (app && IGNORED_CHECK_SLUGS.includes(app.slug)) {
378
+ // Ignore Dependabot and Codecov check suites.
379
+ // They are expected to show up sometimes and never complete.
379
380
  continue;
380
381
  }
381
382
 
@@ -2,7 +2,7 @@ import path from 'node:path';
2
2
  import { promises as fs } from 'node:fs';
3
3
 
4
4
  import semver from 'semver';
5
- import replace from 'replace-in-file';
5
+ import { replaceInFile } from 'replace-in-file';
6
6
 
7
7
  import { getMergedConfig } from './config.js';
8
8
  import { runAsync, runSync } from './run.js';
@@ -427,7 +427,7 @@ export default class ReleasePreparation {
427
427
  async updateREPLACEMEs() {
428
428
  const { newVersion } = this;
429
429
 
430
- await replace({
430
+ await replaceInFile({
431
431
  files: 'doc/api/*.md',
432
432
  from: /REPLACEME/g,
433
433
  to: `v${newVersion}`
@@ -6,7 +6,6 @@ import {
6
6
  NEXT_SECURITY_RELEASE_BRANCH,
7
7
  NEXT_SECURITY_RELEASE_FOLDER,
8
8
  NEXT_SECURITY_RELEASE_REPOSITORY,
9
- PLACEHOLDERS,
10
9
  checkoutOnSecurityReleaseBranch,
11
10
  commitAndPushVulnerabilitiesJSON,
12
11
  validateDate,
@@ -37,22 +36,15 @@ export default class PrepareSecurityRelease {
37
36
  const createVulnerabilitiesJSON = await this.promptVulnerabilitiesJSON();
38
37
 
39
38
  let securityReleasePRUrl;
39
+ const content = await this.buildDescription(releaseDate, securityReleasePRUrl);
40
40
  if (createVulnerabilitiesJSON) {
41
- securityReleasePRUrl = await this.startVulnerabilitiesJSONCreation(releaseDate);
41
+ securityReleasePRUrl = await this.startVulnerabilitiesJSONCreation(releaseDate, content);
42
42
  }
43
43
 
44
- const createIssue = await this.promptCreateRelaseIssue();
45
-
46
- if (createIssue) {
47
- const content = await this.buildIssue(releaseDate, securityReleasePRUrl);
48
- await createIssue(
49
- this.title, content, this.repository, { cli: this.cli, repository: this.repository });
50
- };
51
-
52
44
  this.cli.ok('Done!');
53
45
  }
54
46
 
55
- async startVulnerabilitiesJSONCreation(releaseDate) {
47
+ async startVulnerabilitiesJSONCreation(releaseDate, content) {
56
48
  // checkout on the next-security-release branch
57
49
  checkoutOnSecurityReleaseBranch(this.cli, this.repository);
58
50
 
@@ -87,7 +79,7 @@ export default class PrepareSecurityRelease {
87
79
  if (!createPr) return;
88
80
 
89
81
  // create pr on the security-release repo
90
- return this.createPullRequest();
82
+ return this.createPullRequest(content);
91
83
  }
92
84
 
93
85
  promptCreatePR() {
@@ -143,11 +135,9 @@ export default class PrepareSecurityRelease {
143
135
  { defaultAnswer: true });
144
136
  }
145
137
 
146
- async buildIssue(releaseDate, securityReleasePRUrl = PLACEHOLDERS.vulnerabilitiesPRURL) {
138
+ async buildDescription() {
147
139
  const template = await this.getSecurityIssueTemplate();
148
- const content = template.replace(PLACEHOLDERS.releaseDate, releaseDate)
149
- .replace(PLACEHOLDERS.vulnerabilitiesPRURL, securityReleasePRUrl);
150
- return content;
140
+ return template;
151
141
  }
152
142
 
153
143
  async chooseReports() {
@@ -185,11 +175,11 @@ export default class PrepareSecurityRelease {
185
175
  return fullPath;
186
176
  }
187
177
 
188
- async createPullRequest() {
178
+ async createPullRequest(content) {
189
179
  const { owner, repo } = this.repository;
190
180
  const response = await this.req.createPullRequest(
191
181
  this.title,
192
- 'List of vulnerabilities to be included in the next security release',
182
+ content ?? 'List of vulnerabilities to be included in the next security release',
193
183
  {
194
184
  owner,
195
185
  repo,
@@ -53,7 +53,7 @@ export default class SecurityAnnouncement {
53
53
  };
54
54
 
55
55
  const { title, content } = this.createPreleaseAnnouncementIssue(releaseDate, 'build');
56
- await this.createIssue(title, content, repository);
56
+ await createIssue(title, content, repository, { cli: this.cli, req: this.req });
57
57
  }
58
58
 
59
59
  createPreleaseAnnouncementIssue(releaseDate, team) {
@@ -71,6 +71,6 @@ export default class SecurityAnnouncement {
71
71
  };
72
72
 
73
73
  const { title, content } = this.createPreleaseAnnouncementIssue(releaseDate, 'docker');
74
- await createIssue(title, content, repository, { cli: this.cli, repository: this.repository });
74
+ await createIssue(title, content, repository, { cli: this.cli, req: this.req });
75
75
  }
76
76
  }
@@ -8,7 +8,6 @@ import {
8
8
  checkoutOnSecurityReleaseBranch,
9
9
  NEXT_SECURITY_RELEASE_REPOSITORY,
10
10
  validateDate,
11
- getSummary,
12
11
  commitAndPushVulnerabilitiesJSON,
13
12
  NEXT_SECURITY_RELEASE_FOLDER
14
13
  } from './security-release/security-release.js';
@@ -84,6 +83,7 @@ export default class SecurityBlog {
84
83
  const releaseDate = new Date(content.releaseDate);
85
84
  const template = this.getSecurityPostReleaseTemplate();
86
85
  const data = {
86
+ // TODO: read from pre-sec-release
87
87
  annoucementDate: await this.getAnnouncementDate(cli),
88
88
  releaseDate: this.formatReleaseDate(releaseDate),
89
89
  affectedVersions: this.getAffectedVersions(content),
@@ -205,46 +205,25 @@ export default class SecurityBlog {
205
205
  const reports = content.reports;
206
206
  let template = '';
207
207
  for (const report of reports) {
208
- let cveId = report.cve_ids?.join(', ');
208
+ const cveId = report.cveIds?.join(', ');
209
209
  if (!cveId) {
210
- // ask for the CVE ID
211
- // it should have been created with the step `--request-cve`
212
- cveId = await this.cli.prompt(`What is the CVE ID for vulnerability https://hackerone.com/reports/${report.id} ${report.title}?`, {
213
- questionType: 'input',
214
- defaultAnswer: 'TBD'
215
- });
216
- report.cve_ids = [cveId];
217
- content[kChanged] = true;
210
+ this.cli.error(`CVE ID for vulnerability ${report.link} ${report.title} not found`);
211
+ process.exit(1);
218
212
  }
219
213
  template += `## ${report.title} (${cveId}) - (${report.severity.rating})\n\n`;
220
214
  if (!report.summary) {
221
- const fetchIt = await this.cli.prompt(`Summary missing for vulnerability https://hackerone.com/reports/${report.id} ${report.title}.\
222
- Do you want to try fetch it from HackerOne??`, {
223
- questionType: 'confirm',
224
- defaultAnswer: true
225
- });
226
-
227
- if (fetchIt) {
228
- report.summary = await getSummary(report.id, this.req);
229
- content[kChanged] = true;
230
- }
231
-
232
- if (!report.summary) {
233
- this.cli.error(`Summary missing for vulnerability https://hackerone.com/reports/${report.id} ${report.title}. Please create it before continuing.`);
234
- process.exit(1);
235
- }
215
+ this.cli.error(`Summary missing for vulnerability ${report.link} ` +
216
+ `${report.title}. Please create it before continuing.`);
217
+ process.exit(1);
236
218
  }
219
+
237
220
  template += `${report.summary}\n\n`;
238
221
  const releaseLines = report.affectedVersions.join(', ');
239
222
  template += `Impact:\n\n- This vulnerability affects all users\
240
223
  in active release lines: ${releaseLines}\n\n`;
241
224
  if (!report.patchAuthors) {
242
- const author = await this.cli.prompt(`Who fixed vulnerability https://hackerone.com/reports/${report.id} ${report.title}? If multiple use & as separator`, {
243
- questionType: 'input',
244
- defaultAnswer: 'TBD'
245
- });
246
- report.patchAuthors = author.split('&').map((p) => p.trim());
247
- content[kChanged] = true;
225
+ this.cli.error(`Missing patch author for vulnerability ${report.link} ${report.title}`);
226
+ process.exit(1);
248
227
  }
249
228
  template += `Thank you, to ${report.reporter} for reporting this vulnerability\
250
229
  and thank you ${report.patchAuthors.join(' and ')} for fixing it.\n\n`;
@@ -253,9 +232,10 @@ export default class SecurityBlog {
253
232
  }
254
233
 
255
234
  getDependencyUpdatesTemplate(dependencyUpdates) {
256
- if (!dependencyUpdates) return '';
257
- let template = 'This security release includes the following dependency' +
258
- ' updates to address public vulnerabilities:\n\n';
235
+ if (typeof dependencyUpdates !== 'object') return '';
236
+ if (Object.keys(dependencyUpdates).length === 0) return '';
237
+ let template = '\nThis security release includes the following dependency' +
238
+ ' updates to address public vulnerabilities:\n';
259
239
  for (const dependencyUpdate of Object.values(dependencyUpdates)) {
260
240
  for (const dependency of dependencyUpdate) {
261
241
  const title = dependency.title.substring(dependency.title.indexOf(':') + ':'.length).trim();
@@ -351,7 +331,12 @@ export default class SecurityBlog {
351
331
  affectedVersions.add(affectedVersion);
352
332
  }
353
333
  }
354
- return Array.from(affectedVersions).join(', ');
334
+ const parseToNumber = str => +(str.match(/[\d.]+/g)[0]);
335
+ return Array.from(affectedVersions)
336
+ .sort((a, b) => {
337
+ return parseToNumber(a) > parseToNumber(b) ? -1 : 1;
338
+ })
339
+ .join(', ');
355
340
  }
356
341
 
357
342
  getSecurityPreReleaseTemplate() {
@@ -1,7 +1,5 @@
1
1
  import path from 'node:path';
2
2
 
3
- import { Listr } from 'listr2';
4
-
5
3
  import {
6
4
  getNodeV8Version,
7
5
  filterForVersion,
@@ -19,10 +17,10 @@ const nodeChanges = [
19
17
  export default function applyNodeChanges() {
20
18
  return {
21
19
  title: 'Apply Node-specific changes',
22
- task: async(ctx) => {
20
+ task: async(ctx, task) => {
23
21
  const v8Version = await getNodeV8Version(ctx.nodeDir);
24
22
  const list = filterForVersion(nodeChanges, v8Version);
25
- return new Listr(list.map((change) => change.task()));
23
+ return task.newListr(list.map((change) => change.task()));
26
24
  }
27
25
  };
28
26
  }
@@ -4,7 +4,6 @@ import {
4
4
  } from 'node:fs';
5
5
 
6
6
  import inquirer from 'inquirer';
7
- import { Listr } from 'listr2';
8
7
  import { ListrEnquirerPromptAdapter } from '@listr2/prompt-adapter-enquirer';
9
8
 
10
9
  import { shortSha } from '../utils.js';
@@ -50,8 +49,8 @@ export function doBackport(options) {
50
49
 
51
50
  return {
52
51
  title: 'V8 commit backport',
53
- task: () => {
54
- return new Listr(todo);
52
+ task: (ctx, task) => {
53
+ return task.newListr(todo);
55
54
  }
56
55
  };
57
56
  };
@@ -164,8 +163,8 @@ function applyPatches() {
164
163
  function applyAndCommitPatches() {
165
164
  return {
166
165
  title: 'Apply and commit patches to deps/v8',
167
- task: (ctx) => {
168
- return new Listr(ctx.patches.map(applyPatchTask));
166
+ task: (ctx, task) => {
167
+ return task.newListr(ctx.patches.map(applyPatchTask));
169
168
  }
170
169
  };
171
170
  }
@@ -173,7 +172,7 @@ function applyAndCommitPatches() {
173
172
  function applyPatchTask(patch) {
174
173
  return {
175
174
  title: `Commit ${shortSha(patch.sha)}`,
176
- task: (ctx) => {
175
+ task: (ctx, task) => {
177
176
  const todo = [
178
177
  {
179
178
  title: 'Apply patch',
@@ -188,7 +187,7 @@ function applyPatchTask(patch) {
188
187
  }
189
188
  }
190
189
  todo.push(commitPatch(patch));
191
- return new Listr(todo);
190
+ return task.newListr(todo);
192
191
  }
193
192
  };
194
193
  }
@@ -1,8 +1,6 @@
1
1
  import path from 'node:path';
2
2
  import { promises as fs } from 'node:fs';
3
3
 
4
- import { Listr } from 'listr2';
5
-
6
4
  import { getCurrentV8Version } from './common.js';
7
5
  import {
8
6
  getNodeV8Version,
@@ -19,8 +17,8 @@ import { forceRunAsync } from '../run.js';
19
17
  export default function majorUpdate() {
20
18
  return {
21
19
  title: 'Major V8 update',
22
- task: () => {
23
- return new Listr([
20
+ task: (ctx, task) => {
21
+ return task.newListr([
24
22
  getCurrentV8Version(),
25
23
  checkoutBranch(),
26
24
  removeDepsV8(),
@@ -2,8 +2,6 @@ import { spawn } from 'node:child_process';
2
2
  import path from 'node:path';
3
3
  import { promises as fs } from 'node:fs';
4
4
 
5
- import { Listr } from 'listr2';
6
-
7
5
  import { getCurrentV8Version } from './common.js';
8
6
  import { isVersionString } from './util.js';
9
7
  import { forceRunAsync } from '../run.js';
@@ -11,8 +9,8 @@ import { forceRunAsync } from '../run.js';
11
9
  export default function minorUpdate() {
12
10
  return {
13
11
  title: 'Minor V8 update',
14
- task: () => {
15
- return new Listr([
12
+ task: (ctx, task) => {
13
+ return task.newListr([
16
14
  getCurrentV8Version(),
17
15
  getLatestV8Version(),
18
16
  doMinorUpdate()
@@ -1,15 +1,13 @@
1
1
  import { promises as fs } from 'node:fs';
2
2
 
3
- import { Listr } from 'listr2';
4
-
5
3
  import { v8Git } from './constants.js';
6
4
  import { forceRunAsync } from '../run.js';
7
5
 
8
6
  export default function updateV8Clone() {
9
7
  return {
10
8
  title: 'Update local V8 clone',
11
- task: () => {
12
- return new Listr([fetchOrigin(), createClone()]);
9
+ task: (ctx, task) => {
10
+ return task.newListr([fetchOrigin(), createClone()]);
13
11
  }
14
12
  };
15
13
  };
@@ -1,15 +1,13 @@
1
1
  import path from 'node:path';
2
2
  import { promises as fs } from 'node:fs';
3
3
 
4
- import { Listr } from 'listr2';
5
-
6
4
  import { getNodeV8Version } from './util.js';
7
5
 
8
6
  export default function updateVersionNumbers() {
9
7
  return {
10
8
  title: 'Update version numbers',
11
- task: () => {
12
- return new Listr([resetEmbedderString(), bumpNodeModule()]);
9
+ task: (ctx, task) => {
10
+ return task.newListr([resetEmbedderString(), bumpNodeModule()]);
13
11
  }
14
12
  };
15
13
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@node-core/utils",
3
- "version": "5.3.0",
3
+ "version": "5.4.0",
4
4
  "description": "Utilities for Node.js core collaborators",
5
5
  "type": "module",
6
6
  "engines": {
@@ -34,8 +34,8 @@
34
34
  ],
35
35
  "license": "MIT",
36
36
  "dependencies": {
37
- "@listr2/prompt-adapter-enquirer": "^2.0.8",
38
- "@node-core/caritat": "^1.3.1",
37
+ "@listr2/prompt-adapter-enquirer": "^2.0.10",
38
+ "@node-core/caritat": "^1.6.0",
39
39
  "@pkgjs/nv": "^0.2.2",
40
40
  "branch-diff": "^3.0.4",
41
41
  "chalk": "^5.3.0",
@@ -44,26 +44,26 @@
44
44
  "clipboardy": "^4.0.0",
45
45
  "core-validate-commit": "^4.0.0",
46
46
  "figures": "^6.1.0",
47
- "ghauth": "^6.0.4",
48
- "inquirer": "^9.2.22",
47
+ "ghauth": "^6.0.5",
48
+ "inquirer": "^9.3.2",
49
49
  "js-yaml": "^4.1.0",
50
- "listr2": "^8.2.1",
50
+ "listr2": "^8.2.3",
51
51
  "lodash": "^4.17.21",
52
52
  "log-symbols": "^6.0.0",
53
53
  "ora": "^8.0.1",
54
- "replace-in-file": "^7.1.0",
55
- "undici": "^6.18.0",
54
+ "replace-in-file": "^8.0.2",
55
+ "undici": "^6.19.2",
56
56
  "which": "^4.0.0",
57
57
  "yargs": "^17.7.2"
58
58
  },
59
59
  "devDependencies": {
60
60
  "@reporters/github": "^1.7.0",
61
- "c8": "^9.1.0",
61
+ "c8": "^10.1.2",
62
62
  "eslint": "^8.57.0",
63
63
  "eslint-config-standard": "^17.1.0",
64
64
  "eslint-plugin-import": "^2.29.1",
65
65
  "eslint-plugin-n": "^16.6.2",
66
- "eslint-plugin-promise": "^6.1.1",
66
+ "eslint-plugin-promise": "^6.4.0",
67
67
  "sinon": "^18.0.0"
68
68
  }
69
69
  }