@node-core/utils 6.3.3 → 7.0.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.
- package/README.md +2 -5
- package/bin/ncu-ci.js +3 -19
- package/components/git/benchmark.js +112 -0
- package/components/git/land.js +3 -3
- package/components/git/metadata.js +34 -1
- package/components/git/release.js +3 -5
- package/components/git/security.js +258 -27
- package/components/git/v8.js +6 -3
- package/components/metadata.js +75 -7
- package/lib/auth.js +3 -4
- package/lib/backport_session.js +3 -3
- package/lib/benchmark.js +243 -0
- package/lib/cherry_pick.js +32 -11
- package/lib/ci/failure_aggregator.js +16 -20
- package/lib/ci/run_ci.js +1 -1
- package/lib/cli.js +33 -1
- package/lib/config.js +7 -7
- package/lib/file.js +5 -5
- package/lib/landing_session.js +65 -24
- package/lib/links.js +4 -4
- package/lib/pr_checker.js +163 -31
- package/lib/pr_data.js +1 -1
- package/lib/pr_summary.js +1 -1
- package/lib/prepare_release.js +270 -85
- package/lib/prepare_security.js +367 -41
- package/lib/promote_release.js +19 -16
- package/lib/queries/ListSecurityReleasePRs.gql +19 -0
- package/lib/queries/PR.gql +4 -1
- package/lib/request.js +41 -0
- package/lib/run.js +5 -5
- package/lib/security-announcement.js +10 -5
- package/lib/security-release/security-release.js +160 -20
- package/lib/security_blog.js +130 -65
- package/lib/session.js +4 -3
- package/lib/team_info.js +1 -1
- package/lib/update-v8/applyNodeChanges.js +1 -3
- package/lib/update-v8/backport.js +6 -6
- package/lib/update-v8/commitUpdate.js +3 -6
- package/lib/update-v8/common.js +30 -6
- package/lib/update-v8/constants.js +22 -0
- package/lib/update-v8/deps.js +3 -5
- package/lib/update-v8/index.js +14 -5
- package/lib/update-v8/majorUpdate.js +5 -4
- package/lib/update-v8/minorUpdate.js +4 -4
- package/lib/update-v8/updateCrates.js +192 -0
- package/lib/update-v8/updateV8Clone.js +1 -1
- package/lib/update-v8/updateVersionNumbers.js +3 -6
- package/lib/update-v8/util.js +3 -3
- package/lib/update_security_release.js +347 -105
- package/lib/utils.js +5 -5
- package/lib/verbosity.js +3 -3
- package/lib/wpt/index.js +1 -23
- package/package.json +18 -22
- package/bin/get-metadata.js +0 -11
- package/lib/wpt/templates/README.md +0 -16
- package/npm-shrinkwrap.json +0 -20380
package/lib/request.js
CHANGED
|
@@ -100,6 +100,47 @@ export default class Request {
|
|
|
100
100
|
return this.json(prUrl);
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
+
async * getPullRequestFiles({ owner, repo, prid }) {
|
|
104
|
+
let page = 1;
|
|
105
|
+
for (;;) {
|
|
106
|
+
const url =
|
|
107
|
+
`/repos/${owner}/${repo}/pulls/${prid}/files?per_page=100&page=${page}`;
|
|
108
|
+
const batch = await this.json(url);
|
|
109
|
+
if (!Array.isArray(batch) || batch.length === 0) {
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
yield * batch;
|
|
113
|
+
if (batch.length < 100) {
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
page++;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async listDirectory({ owner, repo, path, ref }) {
|
|
121
|
+
let url = `/repos/${owner}/${repo}/contents/${path}`;
|
|
122
|
+
if (ref) {
|
|
123
|
+
url += `?ref=${encodeURIComponent(ref)}`;
|
|
124
|
+
}
|
|
125
|
+
return this.json(url);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async dispatchWorkflow(workflowId, { owner, repo, ref, inputs }) {
|
|
129
|
+
const url =
|
|
130
|
+
`/repos/${owner}/${repo}/actions/workflows/${workflowId}/dispatches`;
|
|
131
|
+
const res = await this.fetch(url, {
|
|
132
|
+
method: 'POST',
|
|
133
|
+
body: JSON.stringify({ ref, inputs })
|
|
134
|
+
});
|
|
135
|
+
if (!res.ok) {
|
|
136
|
+
const text = await res.text();
|
|
137
|
+
throw new Error(
|
|
138
|
+
`Failed to dispatch workflow ${workflowId} on ${owner}/${repo}: ` +
|
|
139
|
+
`${res.status} ${res.statusText}${text ? `\n${text}` : ''}`);
|
|
140
|
+
}
|
|
141
|
+
return res;
|
|
142
|
+
}
|
|
143
|
+
|
|
103
144
|
async createPullRequest(title, body, { owner, repo, head, base }) {
|
|
104
145
|
const url = `/repos/${owner}/${repo}/pulls`;
|
|
105
146
|
const options = {
|
package/lib/run.js
CHANGED
|
@@ -77,7 +77,7 @@ export function forceRunAsync(cmd, args, options) {
|
|
|
77
77
|
throw error;
|
|
78
78
|
}
|
|
79
79
|
});
|
|
80
|
-
}
|
|
80
|
+
}
|
|
81
81
|
|
|
82
82
|
export function runPromise(promise) {
|
|
83
83
|
return promise.catch((error) => {
|
|
@@ -86,11 +86,11 @@ export function runPromise(promise) {
|
|
|
86
86
|
}
|
|
87
87
|
exit();
|
|
88
88
|
});
|
|
89
|
-
}
|
|
89
|
+
}
|
|
90
90
|
|
|
91
91
|
export function runAsync(cmd, args, options) {
|
|
92
92
|
return runPromise(runAsyncBase(cmd, args, options));
|
|
93
|
-
}
|
|
93
|
+
}
|
|
94
94
|
|
|
95
95
|
export function runSync(cmd, args, options) {
|
|
96
96
|
if (cmd instanceof URL) {
|
|
@@ -111,8 +111,8 @@ export function runSync(cmd, args, options) {
|
|
|
111
111
|
} else {
|
|
112
112
|
return child.stdout.toString();
|
|
113
113
|
}
|
|
114
|
-
}
|
|
114
|
+
}
|
|
115
115
|
|
|
116
116
|
export function exit() {
|
|
117
117
|
process.exit(1);
|
|
118
|
-
}
|
|
118
|
+
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
1
|
import {
|
|
3
2
|
NEXT_SECURITY_RELEASE_REPOSITORY,
|
|
4
3
|
checkoutOnSecurityReleaseBranch,
|
|
@@ -7,7 +6,8 @@ import {
|
|
|
7
6
|
validateDate,
|
|
8
7
|
formatDateToYYYYMMDD,
|
|
9
8
|
commitAndPushVulnerabilitiesJSON,
|
|
10
|
-
createIssue
|
|
9
|
+
createIssue,
|
|
10
|
+
writeSecurityFile
|
|
11
11
|
} from './security-release/security-release.js';
|
|
12
12
|
import auth from './auth.js';
|
|
13
13
|
import Request from './request.js';
|
|
@@ -30,7 +30,7 @@ export default class SecurityAnnouncement {
|
|
|
30
30
|
this.req = new Request(credentials);
|
|
31
31
|
|
|
32
32
|
// checkout on security release branch
|
|
33
|
-
checkoutOnSecurityReleaseBranch(cli, this.repository);
|
|
33
|
+
await checkoutOnSecurityReleaseBranch(cli, this.repository);
|
|
34
34
|
// read vulnerabilities JSON file
|
|
35
35
|
const content = getVulnerabilitiesJSON(cli);
|
|
36
36
|
// validate the release date read from vulnerabilities JSON
|
|
@@ -52,9 +52,14 @@ export default class SecurityAnnouncement {
|
|
|
52
52
|
content.dockerIssue = dockerIssue;
|
|
53
53
|
|
|
54
54
|
const vulnerabilitiesJSONPath = getVulnerabilitiesJSONPath();
|
|
55
|
-
|
|
55
|
+
await writeSecurityFile(
|
|
56
|
+
cli,
|
|
57
|
+
vulnerabilitiesJSONPath,
|
|
58
|
+
JSON.stringify(content, null, 2),
|
|
59
|
+
'This records the build and docker tracking issue links in vulnerabilities.json.'
|
|
60
|
+
);
|
|
56
61
|
const commitMessage = 'chore: add build and docker issue link';
|
|
57
|
-
commitAndPushVulnerabilitiesJSON([vulnerabilitiesJSONPath],
|
|
62
|
+
await commitAndPushVulnerabilitiesJSON([vulnerabilitiesJSONPath],
|
|
58
63
|
commitMessage, { cli: this.cli, repository: this.repository });
|
|
59
64
|
|
|
60
65
|
this.cli.ok('Added docker and build issue in vulnerabilities.json');
|
|
@@ -11,6 +11,10 @@ export const NEXT_SECURITY_RELEASE_REPOSITORY = {
|
|
|
11
11
|
repo: 'security-release'
|
|
12
12
|
};
|
|
13
13
|
|
|
14
|
+
export const SEVERITY_RANKS = ['LOW', 'MEDIUM', 'HIGH', 'CRITICAL'];
|
|
15
|
+
const RELEASE_LINE_RE = /^v?(\d+)(?:\.x)?$/;
|
|
16
|
+
const SEMVER_RE = /^v?(\d+)\.\d+\.\d+/;
|
|
17
|
+
|
|
14
18
|
export const PLACEHOLDERS = {
|
|
15
19
|
releaseDate: '%RELEASE_DATE%',
|
|
16
20
|
vulnerabilitiesPRURL: '%VULNERABILITIES_PR_URL%',
|
|
@@ -28,7 +32,34 @@ export const PLACEHOLDERS = {
|
|
|
28
32
|
downloads: '%DOWNLOADS%'
|
|
29
33
|
};
|
|
30
34
|
|
|
31
|
-
|
|
35
|
+
function formatCommand(command, args) {
|
|
36
|
+
return [command, ...args].join(' ');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export async function confirmSecurityStep(cli, action, detail) {
|
|
40
|
+
let message = `Allow action: ${action}?`;
|
|
41
|
+
if (detail) {
|
|
42
|
+
message += `\n\n${detail}`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const allowed = await cli.prompt(message, { defaultAnswer: false });
|
|
46
|
+
if (!allowed) {
|
|
47
|
+
throw new Error(`Aborted: ${action}.`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export async function runSecurityGitCommand(cli, args, detail) {
|
|
52
|
+
const command = formatCommand('git', args);
|
|
53
|
+
await confirmSecurityStep(cli, `run \`${command}\``, detail);
|
|
54
|
+
return runSync('git', args);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export async function writeSecurityFile(cli, filePath, content, detail) {
|
|
58
|
+
await confirmSecurityStep(cli, `write \`${filePath}\``, detail);
|
|
59
|
+
return fs.writeFileSync(filePath, content);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export async function checkRemote(cli, repository) {
|
|
32
63
|
const remote = runSync('git', ['ls-remote', '--get-url', 'origin']).trim();
|
|
33
64
|
const { owner, repo } = repository;
|
|
34
65
|
const securityReleaseOrigin = [
|
|
@@ -42,23 +73,31 @@ export function checkRemote(cli, repository) {
|
|
|
42
73
|
}
|
|
43
74
|
}
|
|
44
75
|
|
|
45
|
-
export function checkoutOnSecurityReleaseBranch(cli, repository) {
|
|
46
|
-
checkRemote(cli, repository);
|
|
76
|
+
export async function checkoutOnSecurityReleaseBranch(cli, repository) {
|
|
77
|
+
await checkRemote(cli, repository);
|
|
47
78
|
const currentBranch = runSync('git', ['branch', '--show-current']).trim();
|
|
48
79
|
cli.info(`Current branch: ${currentBranch} `);
|
|
49
80
|
|
|
50
81
|
if (currentBranch !== NEXT_SECURITY_RELEASE_BRANCH) {
|
|
51
|
-
|
|
82
|
+
await runSecurityGitCommand(
|
|
83
|
+
cli,
|
|
84
|
+
['checkout', '-B', NEXT_SECURITY_RELEASE_BRANCH],
|
|
85
|
+
`This checks out or recreates the ${NEXT_SECURITY_RELEASE_BRANCH} branch locally.`
|
|
86
|
+
);
|
|
52
87
|
cli.ok(`Checkout on branch: ${NEXT_SECURITY_RELEASE_BRANCH} `);
|
|
53
|
-
}
|
|
88
|
+
}
|
|
54
89
|
}
|
|
55
90
|
|
|
56
|
-
export function commitAndPushVulnerabilitiesJSON(
|
|
57
|
-
|
|
91
|
+
export async function commitAndPushVulnerabilitiesJSON(
|
|
92
|
+
filePath,
|
|
93
|
+
commitMessage,
|
|
94
|
+
{ cli, repository }
|
|
95
|
+
) {
|
|
96
|
+
await checkRemote(cli, repository);
|
|
58
97
|
|
|
59
98
|
if (Array.isArray(filePath)) {
|
|
60
|
-
for (const
|
|
61
|
-
runSync('git', ['add',
|
|
99
|
+
for (const currentPath of filePath) {
|
|
100
|
+
runSync('git', ['add', currentPath]);
|
|
62
101
|
}
|
|
63
102
|
} else {
|
|
64
103
|
runSync('git', ['add', filePath]);
|
|
@@ -70,15 +109,31 @@ export function commitAndPushVulnerabilitiesJSON(filePath, commitMessage, { cli,
|
|
|
70
109
|
return;
|
|
71
110
|
}
|
|
72
111
|
|
|
73
|
-
|
|
112
|
+
await runSecurityGitCommand(
|
|
113
|
+
cli,
|
|
114
|
+
['commit', '-m', commitMessage],
|
|
115
|
+
`This creates a local commit with message: ${commitMessage}`
|
|
116
|
+
);
|
|
74
117
|
|
|
75
118
|
try {
|
|
76
|
-
|
|
77
|
-
|
|
119
|
+
await runSecurityGitCommand(
|
|
120
|
+
cli,
|
|
121
|
+
['push', '-u', 'origin', NEXT_SECURITY_RELEASE_BRANCH],
|
|
122
|
+
`This pushes the security release branch to origin/${NEXT_SECURITY_RELEASE_BRANCH}.`
|
|
123
|
+
);
|
|
124
|
+
} catch {
|
|
78
125
|
cli.warn('Rebasing...');
|
|
79
126
|
// try to pull rebase and push again
|
|
80
|
-
|
|
81
|
-
|
|
127
|
+
await runSecurityGitCommand(
|
|
128
|
+
cli,
|
|
129
|
+
['pull', 'origin', NEXT_SECURITY_RELEASE_BRANCH, '--rebase'],
|
|
130
|
+
`This rebases local changes on origin/${NEXT_SECURITY_RELEASE_BRANCH}.`
|
|
131
|
+
);
|
|
132
|
+
await runSecurityGitCommand(
|
|
133
|
+
cli,
|
|
134
|
+
['push', '-u', 'origin', NEXT_SECURITY_RELEASE_BRANCH],
|
|
135
|
+
`This retries pushing the security release branch to origin/${NEXT_SECURITY_RELEASE_BRANCH}.`
|
|
136
|
+
);
|
|
82
137
|
}
|
|
83
138
|
cli.ok(`Pushed commit: ${commitMessage} to ${NEXT_SECURITY_RELEASE_BRANCH}`);
|
|
84
139
|
}
|
|
@@ -130,6 +185,74 @@ export function formatDateToYYYYMMDD(date) {
|
|
|
130
185
|
return `${year}/${month}/${day}`;
|
|
131
186
|
}
|
|
132
187
|
|
|
188
|
+
export function getHighestSeverityAnnouncement(reports, releaseLine = 'this release') {
|
|
189
|
+
const highestSeverityIndex = Math.max(...reports.map(
|
|
190
|
+
r => SEVERITY_RANKS.indexOf(r.severity.rating.toUpperCase())
|
|
191
|
+
));
|
|
192
|
+
|
|
193
|
+
return `The highest severity issue fixed in ${releaseLine} is ${
|
|
194
|
+
SEVERITY_RANKS[highestSeverityIndex] ?? 'NONE'
|
|
195
|
+
}.`;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function normalizeReleaseLine(version) {
|
|
199
|
+
if (typeof version !== 'string') return;
|
|
200
|
+
|
|
201
|
+
const trimmed = version.trim();
|
|
202
|
+
let match = RELEASE_LINE_RE.exec(trimmed);
|
|
203
|
+
if (match) return `${match[1]}.x`;
|
|
204
|
+
|
|
205
|
+
match = SEMVER_RE.exec(trimmed);
|
|
206
|
+
if (match) return `${match[1]}.x`;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function uniqueReleaseLines(releaseLines) {
|
|
210
|
+
return [...new Set(releaseLines.filter(Boolean))];
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export function getAffectedVersionLines(affectedVersions) {
|
|
214
|
+
if (!affectedVersions) return [];
|
|
215
|
+
|
|
216
|
+
if (typeof affectedVersions === 'string') {
|
|
217
|
+
return uniqueReleaseLines(affectedVersions.split(',').map(normalizeReleaseLine));
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (Array.isArray(affectedVersions)) {
|
|
221
|
+
return uniqueReleaseLines(affectedVersions.flatMap((version) => {
|
|
222
|
+
if (typeof version === 'string') {
|
|
223
|
+
return normalizeReleaseLine(version);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (version && typeof version === 'object') {
|
|
227
|
+
return normalizeReleaseLine(version.version);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return [];
|
|
231
|
+
}));
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (typeof affectedVersions === 'object') {
|
|
235
|
+
const releaseLines = Object.keys(affectedVersions).map(normalizeReleaseLine);
|
|
236
|
+
if (releaseLines.some(Boolean)) {
|
|
237
|
+
return uniqueReleaseLines(releaseLines);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return uniqueReleaseLines(
|
|
241
|
+
Object.values(affectedVersions).flatMap(getAffectedVersionLines)
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return [];
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// `dependencies` maps a dependency name to its update, or to a list of updates
|
|
249
|
+
// when the release lines got different ones. Flatten it into a list of updates
|
|
250
|
+
// that each know their name.
|
|
251
|
+
export function getDependencyUpdates(dependencies) {
|
|
252
|
+
return Object.entries(dependencies ?? {}).flatMap(([name, update]) =>
|
|
253
|
+
(Array.isArray(update) ? update : [update]).map((entry) => ({ ...entry, name })));
|
|
254
|
+
}
|
|
255
|
+
|
|
133
256
|
export function promptDependencies(cli) {
|
|
134
257
|
return cli.prompt('Enter the link to the dependency update PR (leave empty to exit): ', {
|
|
135
258
|
defaultAnswer: '',
|
|
@@ -138,6 +261,11 @@ export function promptDependencies(cli) {
|
|
|
138
261
|
}
|
|
139
262
|
|
|
140
263
|
export async function createIssue(title, content, repository, { cli, req }) {
|
|
264
|
+
await confirmSecurityStep(
|
|
265
|
+
cli,
|
|
266
|
+
`create GitHub issue \`${repository.owner}/${repository.repo}: ${title}\``,
|
|
267
|
+
`This creates an issue in ${repository.owner}/${repository.repo}.`
|
|
268
|
+
);
|
|
141
269
|
const data = await req.createIssue(title, content, repository);
|
|
142
270
|
if (data.html_url) {
|
|
143
271
|
cli.ok(`Created: ${data.html_url}`);
|
|
@@ -183,7 +311,7 @@ export async function pickReport(report, { cli, req }) {
|
|
|
183
311
|
});
|
|
184
312
|
|
|
185
313
|
let prURL = '';
|
|
186
|
-
let patchAuthors
|
|
314
|
+
let patchAuthors;
|
|
187
315
|
if (custom_field_values.data.length) {
|
|
188
316
|
prURL = custom_field_values.data[0].attributes.value;
|
|
189
317
|
const { user } = await req.getPullRequest(prURL);
|
|
@@ -240,33 +368,45 @@ export class SecurityRelease {
|
|
|
240
368
|
NEXT_SECURITY_RELEASE_FOLDER, 'vulnerabilities.json');
|
|
241
369
|
}
|
|
242
370
|
|
|
243
|
-
updateReleaseFolder(releaseDate) {
|
|
371
|
+
async updateReleaseFolder(releaseDate) {
|
|
244
372
|
const folder = path.join(process.cwd(),
|
|
245
373
|
NEXT_SECURITY_RELEASE_FOLDER);
|
|
246
374
|
const newFolder = path.join(process.cwd(), 'security-release', releaseDate);
|
|
375
|
+
await confirmSecurityStep(
|
|
376
|
+
this.cli,
|
|
377
|
+
`rename \`${folder}\` to \`${newFolder}\``,
|
|
378
|
+
'This moves the next-security-release folder to the dated release folder.'
|
|
379
|
+
);
|
|
247
380
|
fs.renameSync(folder, newFolder);
|
|
248
381
|
return newFolder;
|
|
249
382
|
}
|
|
250
383
|
|
|
251
|
-
updateVulnerabilitiesJSON(content) {
|
|
384
|
+
async updateVulnerabilitiesJSON(content) {
|
|
252
385
|
try {
|
|
253
386
|
const vulnerabilitiesJSONPath = this.getVulnerabilitiesJSONPath();
|
|
254
387
|
this.cli.startSpinner(`Updating vulnerabilities.json from ${vulnerabilitiesJSONPath}...`);
|
|
255
|
-
|
|
256
|
-
|
|
388
|
+
await writeSecurityFile(
|
|
389
|
+
this.cli,
|
|
390
|
+
vulnerabilitiesJSONPath,
|
|
391
|
+
JSON.stringify(content, null, 2),
|
|
392
|
+
'This updates vulnerabilities.json with the latest security release data.'
|
|
393
|
+
);
|
|
394
|
+
await commitAndPushVulnerabilitiesJSON(vulnerabilitiesJSONPath,
|
|
257
395
|
'chore: updated vulnerabilities.json',
|
|
258
396
|
{ cli: this.cli, repository: this.repository });
|
|
259
397
|
this.cli.stopSpinner(`Done updating vulnerabilities.json from ${vulnerabilitiesJSONPath}`);
|
|
398
|
+
return true;
|
|
260
399
|
} catch (error) {
|
|
261
400
|
this.cli.error('Error updating vulnerabilities.json');
|
|
262
401
|
this.cli.error(error);
|
|
402
|
+
return false;
|
|
263
403
|
}
|
|
264
404
|
}
|
|
265
405
|
|
|
266
406
|
getAffectedVersions(content) {
|
|
267
407
|
const affectedVersions = new Set();
|
|
268
408
|
for (const report of Object.values(content.reports)) {
|
|
269
|
-
for (const affectedVersion of report.affectedVersions) {
|
|
409
|
+
for (const affectedVersion of getAffectedVersionLines(report.affectedVersions)) {
|
|
270
410
|
affectedVersions.add(affectedVersion);
|
|
271
411
|
}
|
|
272
412
|
}
|