@node-core/utils 5.3.1 → 5.5.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/components/git/release.js +2 -0
- package/components/git/security.js +17 -0
- package/lib/ci/build-types/citgm_comparison_build.js +15 -1
- package/lib/landing_session.js +1 -1
- package/lib/pr_checker.js +4 -3
- package/lib/prepare_release.js +133 -73
- package/lib/prepare_security.js +79 -25
- package/lib/request.js +59 -0
- package/lib/security-release/security-release.js +61 -0
- package/lib/security_blog.js +8 -40
- package/lib/update-v8/applyNodeChanges.js +2 -4
- package/lib/update-v8/backport.js +6 -7
- package/lib/update-v8/constants.js +12 -0
- package/lib/update-v8/majorUpdate.js +2 -4
- package/lib/update-v8/minorUpdate.js +2 -4
- package/lib/update-v8/updateV8Clone.js +2 -4
- package/lib/update-v8/updateVersionNumbers.js +2 -4
- package/lib/update_security_release.js +6 -41
- package/package.json +1 -1
@@ -78,6 +78,8 @@ async function main(state, argv, cli, dir) {
|
|
78
78
|
if (state === PREPARE) {
|
79
79
|
const prep = new ReleasePreparation(argv, cli, dir);
|
80
80
|
|
81
|
+
await prep.prepareLocalBranch();
|
82
|
+
|
81
83
|
if (prep.warnForWrongBranch()) return;
|
82
84
|
|
83
85
|
// If the new version was automatically calculated, confirm it.
|
@@ -43,6 +43,10 @@ const securityOptions = {
|
|
43
43
|
'post-release': {
|
44
44
|
describe: 'Create the post-release announcement',
|
45
45
|
type: 'boolean'
|
46
|
+
},
|
47
|
+
cleanup: {
|
48
|
+
describe: 'cleanup the security release.',
|
49
|
+
type: 'boolean'
|
46
50
|
}
|
47
51
|
};
|
48
52
|
|
@@ -81,6 +85,9 @@ export function builder(yargs) {
|
|
81
85
|
).example(
|
82
86
|
'git node security --post-release',
|
83
87
|
'Create the post-release announcement on the Nodejs.org repo'
|
88
|
+
).example(
|
89
|
+
'git node security --cleanup',
|
90
|
+
'Cleanup the security release. Merge the PR and close H1 reports'
|
84
91
|
);
|
85
92
|
}
|
86
93
|
|
@@ -112,6 +119,9 @@ export function handler(argv) {
|
|
112
119
|
if (argv['post-release']) {
|
113
120
|
return createPostRelease(argv);
|
114
121
|
}
|
122
|
+
if (argv.cleanup) {
|
123
|
+
return cleanupSecurityRelease(argv);
|
124
|
+
}
|
115
125
|
yargsInstance.showHelp();
|
116
126
|
}
|
117
127
|
|
@@ -167,6 +177,13 @@ async function startSecurityRelease() {
|
|
167
177
|
return release.start();
|
168
178
|
}
|
169
179
|
|
180
|
+
async function cleanupSecurityRelease() {
|
181
|
+
const logStream = process.stdout.isTTY ? process.stdout : process.stderr;
|
182
|
+
const cli = new CLI(logStream);
|
183
|
+
const release = new PrepareSecurityRelease(cli);
|
184
|
+
return release.cleanup();
|
185
|
+
}
|
186
|
+
|
170
187
|
async function syncSecurityRelease(argv) {
|
171
188
|
const logStream = process.stdout.isTTY ? process.stdout : process.stderr;
|
172
189
|
const cli = new CLI(logStream);
|
@@ -44,6 +44,7 @@ export class CITGMComparisonBuild {
|
|
44
44
|
const { failures: comparisonFailures } = comparisonBuild.results;
|
45
45
|
|
46
46
|
const failures = {};
|
47
|
+
let allPlatformFailures;
|
47
48
|
for (const platform in comparisonFailures) {
|
48
49
|
// Account for no failure on this platform, or different platform.
|
49
50
|
if (!Object.prototype.hasOwnProperty.call(baseFailures, platform)) {
|
@@ -66,11 +67,18 @@ export class CITGMComparisonBuild {
|
|
66
67
|
if (newFailures.length !== 0) {
|
67
68
|
result = statusType.FAILURE;
|
68
69
|
}
|
69
|
-
|
70
|
+
if (allPlatformFailures === undefined) {
|
71
|
+
allPlatformFailures = newFailures;
|
72
|
+
} else if (allPlatformFailures.length > 0) {
|
73
|
+
allPlatformFailures = allPlatformFailures.filter(f => {
|
74
|
+
return newFailures.includes(f);
|
75
|
+
});
|
76
|
+
}
|
70
77
|
failures[platform] = newFailures;
|
71
78
|
}
|
72
79
|
|
73
80
|
this.results.failures = failures;
|
81
|
+
this.results.allPlatformFailures = allPlatformFailures;
|
74
82
|
this.result = result;
|
75
83
|
|
76
84
|
return result;
|
@@ -124,6 +132,12 @@ export class CITGMComparisonBuild {
|
|
124
132
|
const str = `${totalFailures} failures in ${cID} not present in ${bID}`;
|
125
133
|
cli.log(`${statusType.FAILURE}: ${str}\n\n`);
|
126
134
|
console.table(output);
|
135
|
+
if (
|
136
|
+
results.allPlatformFailures &&
|
137
|
+
results.allPlatformFailures.length) {
|
138
|
+
const failures = results.allPlatformFailures.join(', ');
|
139
|
+
console.warn(`These modules failed in all platforms: ${failures}`);
|
140
|
+
}
|
127
141
|
}
|
128
142
|
|
129
143
|
formatAsJson() {
|
package/lib/landing_session.js
CHANGED
@@ -21,7 +21,7 @@ export default class LandingSession extends Session {
|
|
21
21
|
prid, backport, lint, autorebase, fixupAll,
|
22
22
|
checkCI, oneCommitMax, ...argv
|
23
23
|
} = {}) {
|
24
|
-
super(cli, dir, prid);
|
24
|
+
super(cli, dir, prid, argv);
|
25
25
|
this.req = req;
|
26
26
|
this.backport = backport;
|
27
27
|
this.lint = lint;
|
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
|
377
|
-
// Ignore Dependabot check suites.
|
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
|
|
package/lib/prepare_release.js
CHANGED
@@ -4,8 +4,7 @@ import { promises as fs } from 'node:fs';
|
|
4
4
|
import semver from 'semver';
|
5
5
|
import { replaceInFile } from 'replace-in-file';
|
6
6
|
|
7
|
-
import {
|
8
|
-
import { runAsync, runSync } from './run.js';
|
7
|
+
import { forceRunAsync, runAsync, runSync } from './run.js';
|
9
8
|
import { writeJson, readJson } from './file.js';
|
10
9
|
import Request from './request.js';
|
11
10
|
import auth from './auth.js';
|
@@ -15,58 +14,25 @@ import {
|
|
15
14
|
updateTestProcessRelease
|
16
15
|
} from './release/utils.js';
|
17
16
|
import CherryPick from './cherry_pick.js';
|
17
|
+
import Session from './session.js';
|
18
18
|
|
19
19
|
const isWindows = process.platform === 'win32';
|
20
20
|
|
21
|
-
export default class ReleasePreparation {
|
21
|
+
export default class ReleasePreparation extends Session {
|
22
22
|
constructor(argv, cli, dir) {
|
23
|
-
|
24
|
-
this.dir = dir;
|
23
|
+
super(cli, dir);
|
25
24
|
this.isSecurityRelease = argv.security;
|
26
25
|
this.isLTS = false;
|
27
26
|
this.isLTSTransition = argv.startLTS;
|
28
27
|
this.runBranchDiff = !argv.skipBranchDiff;
|
29
28
|
this.ltsCodename = '';
|
30
29
|
this.date = '';
|
31
|
-
this.config = getMergedConfig(this.dir);
|
32
30
|
this.filterLabels = argv.filterLabel && argv.filterLabel.split(',');
|
31
|
+
this.newVersion = argv.newVersion;
|
32
|
+
}
|
33
33
|
|
34
|
-
|
35
|
-
|
36
|
-
cli.error('Failed to begin the release preparation process.');
|
37
|
-
return;
|
38
|
-
}
|
39
|
-
|
40
|
-
// Allow passing optional new version.
|
41
|
-
if (argv.newVersion) {
|
42
|
-
const newVersion = semver.clean(argv.newVersion);
|
43
|
-
if (!semver.valid(newVersion)) {
|
44
|
-
cli.warn(`${newVersion} is not a valid semantic version.`);
|
45
|
-
return;
|
46
|
-
}
|
47
|
-
this.newVersion = newVersion;
|
48
|
-
} else {
|
49
|
-
this.newVersion = this.calculateNewVersion();
|
50
|
-
}
|
51
|
-
|
52
|
-
const { upstream, owner, repo, newVersion } = this;
|
53
|
-
|
54
|
-
this.versionComponents = {
|
55
|
-
major: semver.major(newVersion),
|
56
|
-
minor: semver.minor(newVersion),
|
57
|
-
patch: semver.patch(newVersion)
|
58
|
-
};
|
59
|
-
|
60
|
-
this.stagingBranch = `v${this.versionComponents.major}.x-staging`;
|
61
|
-
this.releaseBranch = `v${this.versionComponents.major}.x`;
|
62
|
-
|
63
|
-
const upstreamHref = runSync('git', [
|
64
|
-
'config', '--get',
|
65
|
-
`remote.${upstream}.url`]).trim();
|
66
|
-
if (!new RegExp(`${owner}/${repo}(?:.git)?$`).test(upstreamHref)) {
|
67
|
-
cli.warn('Remote repository URL does not point to the expected ' +
|
68
|
-
`repository ${owner}/${repo}`);
|
69
|
-
}
|
34
|
+
get branch() {
|
35
|
+
return this.stagingBranch;
|
70
36
|
}
|
71
37
|
|
72
38
|
warnForNonMergeablePR(pr) {
|
@@ -205,7 +171,7 @@ export default class ReleasePreparation {
|
|
205
171
|
// Check the branch diff to determine if the releaser
|
206
172
|
// wants to backport any more commits before proceeding.
|
207
173
|
cli.startSpinner('Fetching branch-diff');
|
208
|
-
const raw = this.getBranchDiff({
|
174
|
+
const raw = await this.getBranchDiff({
|
209
175
|
onlyNotableChanges: false,
|
210
176
|
comparisonBranch: newVersion
|
211
177
|
});
|
@@ -215,10 +181,9 @@ export default class ReleasePreparation {
|
|
215
181
|
|
216
182
|
const outstandingCommits = diff.length - 1;
|
217
183
|
if (outstandingCommits !== 0) {
|
218
|
-
const staging = `v${semver.major(newVersion)}.x-staging`;
|
219
184
|
const proceed = await cli.prompt(
|
220
185
|
`There are ${outstandingCommits} commits that may be ` +
|
221
|
-
`backported to ${
|
186
|
+
`backported to ${this.stagingBranch} - do you still want to proceed?`,
|
222
187
|
{ defaultAnswer: false });
|
223
188
|
|
224
189
|
if (!proceed) {
|
@@ -369,24 +334,19 @@ export default class ReleasePreparation {
|
|
369
334
|
return missing;
|
370
335
|
}
|
371
336
|
|
372
|
-
calculateNewVersion() {
|
373
|
-
|
374
|
-
|
375
|
-
const lastTagVersion = semver.clean(this.getLastRef());
|
376
|
-
const lastTag = {
|
377
|
-
major: semver.major(lastTagVersion),
|
378
|
-
minor: semver.minor(lastTagVersion),
|
379
|
-
patch: semver.patch(lastTagVersion)
|
380
|
-
};
|
381
|
-
|
382
|
-
const changelog = this.getChangelog();
|
337
|
+
async calculateNewVersion({ tagName, major, minor, patch }) {
|
338
|
+
const changelog = this.getChangelog(tagName);
|
383
339
|
|
340
|
+
const newVersion = { major, minor, patch };
|
384
341
|
if (changelog.includes('SEMVER-MAJOR')) {
|
385
|
-
newVersion
|
342
|
+
newVersion.major++;
|
343
|
+
newVersion.minor = 0;
|
344
|
+
newVersion.patch = 0;
|
386
345
|
} else if (changelog.includes('SEMVER-MINOR') || this.isLTSTransition) {
|
387
|
-
newVersion
|
346
|
+
newVersion.minor++;
|
347
|
+
newVersion.patch = 0;
|
388
348
|
} else {
|
389
|
-
newVersion
|
349
|
+
newVersion.patch++;
|
390
350
|
}
|
391
351
|
|
392
352
|
return newVersion;
|
@@ -396,11 +356,22 @@ export default class ReleasePreparation {
|
|
396
356
|
return runSync('git', ['rev-parse', '--abbrev-ref', 'HEAD']).trim();
|
397
357
|
}
|
398
358
|
|
399
|
-
getLastRef() {
|
400
|
-
|
359
|
+
getLastRef(tagName) {
|
360
|
+
if (!tagName) {
|
361
|
+
return runSync('git', ['describe', '--abbrev=0', '--tags']).trim();
|
362
|
+
}
|
363
|
+
|
364
|
+
try {
|
365
|
+
runSync('git', ['rev-parse', tagName]);
|
366
|
+
} catch {
|
367
|
+
this.cli.startSpinner(`Error parsing git ref ${tagName}, attempting fetching it as a tag`);
|
368
|
+
runSync('git', ['fetch', this.upstream, 'tag', '-n', tagName]);
|
369
|
+
this.cli.stopSpinner(`Tag fetched: ${tagName}`);
|
370
|
+
}
|
371
|
+
return tagName;
|
401
372
|
}
|
402
373
|
|
403
|
-
getChangelog() {
|
374
|
+
getChangelog(tagName) {
|
404
375
|
const changelogMaker = new URL(
|
405
376
|
'../node_modules/.bin/changelog-maker' + (isWindows ? '.cmd' : ''),
|
406
377
|
import.meta.url
|
@@ -411,7 +382,7 @@ export default class ReleasePreparation {
|
|
411
382
|
'--markdown',
|
412
383
|
'--filter-release',
|
413
384
|
'--start-ref',
|
414
|
-
this.getLastRef()
|
385
|
+
this.getLastRef(tagName)
|
415
386
|
]).trim();
|
416
387
|
}
|
417
388
|
|
@@ -496,7 +467,7 @@ export default class ReleasePreparation {
|
|
496
467
|
const data = await fs.readFile(majorChangelogPath, 'utf8');
|
497
468
|
const arr = data.split('\n');
|
498
469
|
const allCommits = this.getChangelog();
|
499
|
-
const notableChanges = this.getBranchDiff({ onlyNotableChanges: true });
|
470
|
+
const notableChanges = await this.getBranchDiff({ onlyNotableChanges: true });
|
500
471
|
let releaseHeader = `## ${date}, Version ${newVersion}` +
|
501
472
|
` ${releaseInfo}, @${username}\n`;
|
502
473
|
if (isSecurityRelease) {
|
@@ -550,14 +521,14 @@ export default class ReleasePreparation {
|
|
550
521
|
}
|
551
522
|
|
552
523
|
async createProposalBranch(base = this.stagingBranch) {
|
553
|
-
const {
|
524
|
+
const { newVersion } = this;
|
554
525
|
const proposalBranch = `v${newVersion}-proposal`;
|
555
526
|
|
556
527
|
await runAsync('git', [
|
557
528
|
'checkout',
|
558
529
|
'-b',
|
559
530
|
proposalBranch,
|
560
|
-
|
531
|
+
base
|
561
532
|
]);
|
562
533
|
return proposalBranch;
|
563
534
|
}
|
@@ -632,7 +603,7 @@ export default class ReleasePreparation {
|
|
632
603
|
messageBody.push('This is a security release.\n\n');
|
633
604
|
}
|
634
605
|
|
635
|
-
const notableChanges = this.getBranchDiff({
|
606
|
+
const notableChanges = await this.getBranchDiff({
|
636
607
|
onlyNotableChanges: true,
|
637
608
|
format: 'plaintext'
|
638
609
|
});
|
@@ -659,8 +630,9 @@ export default class ReleasePreparation {
|
|
659
630
|
return useMessage;
|
660
631
|
}
|
661
632
|
|
662
|
-
getBranchDiff(opts) {
|
633
|
+
async getBranchDiff(opts) {
|
663
634
|
const {
|
635
|
+
cli,
|
664
636
|
versionComponents = {},
|
665
637
|
upstream,
|
666
638
|
newVersion,
|
@@ -688,6 +660,10 @@ export default class ReleasePreparation {
|
|
688
660
|
'semver-minor'
|
689
661
|
];
|
690
662
|
|
663
|
+
await forceRunAsync('git', ['remote', 'set-branches', '--add', upstream, releaseBranch], {
|
664
|
+
ignoreFailures: false
|
665
|
+
});
|
666
|
+
await forceRunAsync('git', ['fetch', upstream, releaseBranch], { ignoreFailures: false });
|
691
667
|
branchDiffOptions = [
|
692
668
|
`${upstream}/${releaseBranch}`,
|
693
669
|
proposalBranch,
|
@@ -706,20 +682,43 @@ export default class ReleasePreparation {
|
|
706
682
|
'baking-for-lts'
|
707
683
|
];
|
708
684
|
|
709
|
-
let comparisonBranch = 'main';
|
685
|
+
let comparisonBranch = this.config.branch || 'main';
|
710
686
|
const isSemverMinor = versionComponents.patch === 0;
|
711
687
|
if (isLTS) {
|
688
|
+
const res = await fetch('https://nodejs.org/dist/index.json');
|
689
|
+
if (!res.ok) throw new Error('Failed to fetch', { cause: res });
|
690
|
+
const [latest] = await res.json();
|
712
691
|
// Assume Current branch matches tag with highest semver value.
|
713
|
-
|
714
|
-
['tag', '-l', '--sort', '-version:refname']).trim();
|
715
|
-
const highestVersionTag = tags.split('\n')[0];
|
716
|
-
comparisonBranch = `v${semver.coerce(highestVersionTag).major}.x`;
|
692
|
+
comparisonBranch = `v${semver.coerce(latest.version).major}.x`;
|
717
693
|
|
718
694
|
if (!isSemverMinor) {
|
719
695
|
excludeLabels.push('semver-minor');
|
720
696
|
}
|
721
697
|
}
|
722
698
|
|
699
|
+
await forceRunAsync('git', ['fetch', upstream, comparisonBranch], { ignoreFailures: false });
|
700
|
+
const commits = await forceRunAsync('git', ['rev-parse', 'FETCH_HEAD', comparisonBranch], {
|
701
|
+
captureStdout: 'lines',
|
702
|
+
ignoreFailures: true
|
703
|
+
});
|
704
|
+
if (commits == null) {
|
705
|
+
const shouldCreateCompareBranch = await cli.prompt(
|
706
|
+
`No local branch ${comparisonBranch}, do you want to create it?`);
|
707
|
+
if (shouldCreateCompareBranch) {
|
708
|
+
await forceRunAsync('git', ['branch', comparisonBranch, 'FETCH_HEAD'], {
|
709
|
+
ignoreFailures: false
|
710
|
+
});
|
711
|
+
}
|
712
|
+
} else if (commits[0] !== commits[1]) {
|
713
|
+
const shouldUpBranch = cli.prompt(`Local ${comparisonBranch} branch is not in sync with ${
|
714
|
+
upstream}/${comparisonBranch}, do you want to update it?`);
|
715
|
+
if (shouldUpBranch) {
|
716
|
+
await forceRunAsync('git', ['branch', '-f', comparisonBranch, 'FETCH_HEAD'], {
|
717
|
+
ignoreFailures: false
|
718
|
+
});
|
719
|
+
}
|
720
|
+
}
|
721
|
+
|
723
722
|
branchDiffOptions = [
|
724
723
|
stagingBranch,
|
725
724
|
comparisonBranch,
|
@@ -736,6 +735,67 @@ export default class ReleasePreparation {
|
|
736
735
|
return runSync(branchDiff, branchDiffOptions);
|
737
736
|
}
|
738
737
|
|
738
|
+
async getLastRelease(major) {
|
739
|
+
const { cli } = this;
|
740
|
+
|
741
|
+
cli.startSpinner(`Parsing CHANGELOG for most recent release of v${major}.x`);
|
742
|
+
const data = await fs.readFile(
|
743
|
+
path.resolve(`doc/changelogs/CHANGELOG_V${major}.md`),
|
744
|
+
'utf8'
|
745
|
+
);
|
746
|
+
const [,, minor, patch] = /<a href="#(\d+)\.(\d+)\.(\d+)">\1\.\2\.\3<\/a><br\/>/.exec(data);
|
747
|
+
this.isLTS = data.includes('<th>LTS ');
|
748
|
+
|
749
|
+
cli.stopSpinner(`Latest release on ${major}.x line is ${major}.${minor}.${patch}${
|
750
|
+
this.isLTS ? ' (LTS)' : ''
|
751
|
+
}`);
|
752
|
+
|
753
|
+
return {
|
754
|
+
tagName: await this.getLastRef(`v${major}.${minor}.${patch}`),
|
755
|
+
major, minor, patch
|
756
|
+
};
|
757
|
+
}
|
758
|
+
|
759
|
+
async prepareLocalBranch() {
|
760
|
+
const { cli } = this;
|
761
|
+
if (this.newVersion) {
|
762
|
+
// If the CLI asked for a specific version:
|
763
|
+
const newVersion = semver.parse(this.newVersion);
|
764
|
+
if (!newVersion) {
|
765
|
+
cli.warn(`${this.newVersion} is not a valid semantic version.`);
|
766
|
+
return;
|
767
|
+
}
|
768
|
+
this.newVersion = newVersion.version;
|
769
|
+
this.versionComponents = {
|
770
|
+
major: newVersion.major,
|
771
|
+
minor: newVersion.minor,
|
772
|
+
patch: newVersion.patch
|
773
|
+
};
|
774
|
+
this.stagingBranch = `v${newVersion.major}.x-staging`;
|
775
|
+
this.releaseBranch = `v${newVersion.major}.x`;
|
776
|
+
await this.tryResetBranch();
|
777
|
+
await this.getLastRelease(newVersion.major);
|
778
|
+
return;
|
779
|
+
}
|
780
|
+
|
781
|
+
// Otherwise, we need to figure out what's the next version number for the
|
782
|
+
// release line of the branch that's currently checked out.
|
783
|
+
const currentBranch = this.getCurrentBranch();
|
784
|
+
const match = /^v(\d+)\.x-staging$/.exec(currentBranch);
|
785
|
+
|
786
|
+
if (!match) {
|
787
|
+
cli.warn(`Cannot prepare a release from ${currentBranch
|
788
|
+
}. Switch to a staging branch before proceeding.`);
|
789
|
+
return;
|
790
|
+
}
|
791
|
+
this.stagingBranch = currentBranch;
|
792
|
+
await this.tryResetBranch();
|
793
|
+
this.versionComponents = await this.calculateNewVersion(await this.getLastRelease(match[1]));
|
794
|
+
const { major, minor, patch } = this.versionComponents;
|
795
|
+
this.newVersion = `${major}.${minor}.${patch}`;
|
796
|
+
this.releaseBranch = `v${major}.x`;
|
797
|
+
}
|
798
|
+
|
739
799
|
warnForWrongBranch() {
|
740
800
|
const {
|
741
801
|
cli,
|
package/lib/prepare_security.js
CHANGED
@@ -5,23 +5,18 @@ import Request from './request.js';
|
|
5
5
|
import {
|
6
6
|
NEXT_SECURITY_RELEASE_BRANCH,
|
7
7
|
NEXT_SECURITY_RELEASE_FOLDER,
|
8
|
-
NEXT_SECURITY_RELEASE_REPOSITORY,
|
9
|
-
PLACEHOLDERS,
|
10
8
|
checkoutOnSecurityReleaseBranch,
|
11
9
|
commitAndPushVulnerabilitiesJSON,
|
12
10
|
validateDate,
|
13
11
|
promptDependencies,
|
14
12
|
getSupportedVersions,
|
15
|
-
pickReport
|
13
|
+
pickReport,
|
14
|
+
SecurityRelease
|
16
15
|
} from './security-release/security-release.js';
|
17
16
|
import _ from 'lodash';
|
18
17
|
|
19
|
-
export default class PrepareSecurityRelease {
|
20
|
-
repository = NEXT_SECURITY_RELEASE_REPOSITORY;
|
18
|
+
export default class PrepareSecurityRelease extends SecurityRelease {
|
21
19
|
title = 'Next Security Release';
|
22
|
-
constructor(cli) {
|
23
|
-
this.cli = cli;
|
24
|
-
}
|
25
20
|
|
26
21
|
async start() {
|
27
22
|
const credentials = await auth({
|
@@ -37,22 +32,49 @@ export default class PrepareSecurityRelease {
|
|
37
32
|
const createVulnerabilitiesJSON = await this.promptVulnerabilitiesJSON();
|
38
33
|
|
39
34
|
let securityReleasePRUrl;
|
35
|
+
const content = await this.buildDescription(releaseDate, securityReleasePRUrl);
|
40
36
|
if (createVulnerabilitiesJSON) {
|
41
|
-
securityReleasePRUrl = await this.startVulnerabilitiesJSONCreation(releaseDate);
|
37
|
+
securityReleasePRUrl = await this.startVulnerabilitiesJSONCreation(releaseDate, content);
|
42
38
|
}
|
43
39
|
|
44
|
-
|
40
|
+
this.cli.ok('Done!');
|
41
|
+
}
|
45
42
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
};
|
43
|
+
async cleanup() {
|
44
|
+
const credentials = await auth({
|
45
|
+
github: true,
|
46
|
+
h1: true
|
47
|
+
});
|
48
|
+
|
49
|
+
this.req = new Request(credentials);
|
50
|
+
const vulnerabilityJSON = this.readVulnerabilitiesJSON();
|
51
|
+
this.cli.info('Closing and request disclosure to HackerOne reports');
|
52
|
+
await this.closeAndRequestDisclosure(vulnerabilityJSON.reports);
|
53
|
+
|
54
|
+
this.cli.info('Closing pull requests');
|
55
|
+
// For now, close the ones with vN.x label
|
56
|
+
await this.closePRWithLabel(this.getAffectedVersions(vulnerabilityJSON));
|
51
57
|
|
58
|
+
const updateFolder = this.cli.prompt(
|
59
|
+
// eslint-disable-next-line max-len
|
60
|
+
`Would you like to update the next-security-release folder to ${vulnerabilityJSON.releaseDate}?`,
|
61
|
+
{ defaultAnswer: true });
|
62
|
+
if (updateFolder) {
|
63
|
+
const newFolder = this.updateReleaseFolder(vulnerabilityJSON.releaseDate);
|
64
|
+
commitAndPushVulnerabilitiesJSON(
|
65
|
+
newFolder,
|
66
|
+
'chore: change next-security-release folder',
|
67
|
+
{ cli: this.cli, repository: this.repository }
|
68
|
+
);
|
69
|
+
}
|
70
|
+
this.cli.info(`Merge pull request with:
|
71
|
+
- git checkout main
|
72
|
+
- git merge --squash ${NEXT_SECURITY_RELEASE_BRANCH}
|
73
|
+
- git push origin main`);
|
52
74
|
this.cli.ok('Done!');
|
53
75
|
}
|
54
76
|
|
55
|
-
async startVulnerabilitiesJSONCreation(releaseDate) {
|
77
|
+
async startVulnerabilitiesJSONCreation(releaseDate, content) {
|
56
78
|
// checkout on the next-security-release branch
|
57
79
|
checkoutOnSecurityReleaseBranch(this.cli, this.repository);
|
58
80
|
|
@@ -87,7 +109,7 @@ export default class PrepareSecurityRelease {
|
|
87
109
|
if (!createPr) return;
|
88
110
|
|
89
111
|
// create pr on the security-release repo
|
90
|
-
return this.createPullRequest();
|
112
|
+
return this.createPullRequest(content);
|
91
113
|
}
|
92
114
|
|
93
115
|
promptCreatePR() {
|
@@ -143,11 +165,9 @@ export default class PrepareSecurityRelease {
|
|
143
165
|
{ defaultAnswer: true });
|
144
166
|
}
|
145
167
|
|
146
|
-
async
|
168
|
+
async buildDescription() {
|
147
169
|
const template = await this.getSecurityIssueTemplate();
|
148
|
-
|
149
|
-
.replace(PLACEHOLDERS.vulnerabilitiesPRURL, securityReleasePRUrl);
|
150
|
-
return content;
|
170
|
+
return template;
|
151
171
|
}
|
152
172
|
|
153
173
|
async chooseReports() {
|
@@ -173,9 +193,9 @@ export default class PrepareSecurityRelease {
|
|
173
193
|
|
174
194
|
const folderPath = path.join(process.cwd(), NEXT_SECURITY_RELEASE_FOLDER);
|
175
195
|
try {
|
176
|
-
|
196
|
+
fs.accessSync(folderPath);
|
177
197
|
} catch (error) {
|
178
|
-
|
198
|
+
fs.mkdirSync(folderPath, { recursive: true });
|
179
199
|
}
|
180
200
|
|
181
201
|
const fullPath = path.join(folderPath, 'vulnerabilities.json');
|
@@ -185,11 +205,11 @@ export default class PrepareSecurityRelease {
|
|
185
205
|
return fullPath;
|
186
206
|
}
|
187
207
|
|
188
|
-
async createPullRequest() {
|
208
|
+
async createPullRequest(content) {
|
189
209
|
const { owner, repo } = this.repository;
|
190
210
|
const response = await this.req.createPullRequest(
|
191
211
|
this.title,
|
192
|
-
'List of vulnerabilities to be included in the next security release',
|
212
|
+
content ?? 'List of vulnerabilities to be included in the next security release',
|
193
213
|
{
|
194
214
|
owner,
|
195
215
|
repo,
|
@@ -264,4 +284,38 @@ export default class PrepareSecurityRelease {
|
|
264
284
|
}
|
265
285
|
return deps;
|
266
286
|
}
|
287
|
+
|
288
|
+
async closeAndRequestDisclosure(jsonReports) {
|
289
|
+
this.cli.startSpinner('Closing HackerOne reports');
|
290
|
+
for (const report of jsonReports) {
|
291
|
+
this.cli.updateSpinner(`Closing report ${report.id}...`);
|
292
|
+
await this.req.updateReportState(
|
293
|
+
report.id,
|
294
|
+
'resolved',
|
295
|
+
'Closing as resolved'
|
296
|
+
);
|
297
|
+
|
298
|
+
this.cli.updateSpinner(`Requesting disclosure to report ${report.id}...`);
|
299
|
+
await this.req.requestDisclosure(report.id);
|
300
|
+
}
|
301
|
+
this.cli.stopSpinner('Done closing H1 Reports and requesting disclosure');
|
302
|
+
}
|
303
|
+
|
304
|
+
async closePRWithLabel(labels) {
|
305
|
+
if (typeof labels === 'string') {
|
306
|
+
labels = [labels];
|
307
|
+
}
|
308
|
+
|
309
|
+
const url = 'https://github.com/nodejs-private/node-private/pulls';
|
310
|
+
this.cli.startSpinner('Closing GitHub Pull Requests...');
|
311
|
+
// At this point, GitHub does not provide filters through their REST API
|
312
|
+
const prs = this.req.getPullRequest(url);
|
313
|
+
for (const pr of prs) {
|
314
|
+
if (pr.labels.some((l) => labels.includes(l))) {
|
315
|
+
this.cli.updateSpinner(`Closing Pull Request: ${pr.id}`);
|
316
|
+
await this.req.closePullRequest(pr.id);
|
317
|
+
}
|
318
|
+
}
|
319
|
+
this.cli.startSpinner('Closed GitHub Pull Requests.');
|
320
|
+
}
|
267
321
|
}
|
package/lib/request.js
CHANGED
@@ -109,6 +109,22 @@ export default class Request {
|
|
109
109
|
return this.json(url, options);
|
110
110
|
}
|
111
111
|
|
112
|
+
async closePullRequest({ owner, repo }) {
|
113
|
+
const url = `https://api.github.com/repos/${owner}/${repo}/pulls`;
|
114
|
+
const options = {
|
115
|
+
method: 'POST',
|
116
|
+
headers: {
|
117
|
+
Authorization: `Basic ${this.credentials.github}`,
|
118
|
+
'User-Agent': 'node-core-utils',
|
119
|
+
Accept: 'application/vnd.github+json'
|
120
|
+
},
|
121
|
+
body: JSON.stringify({
|
122
|
+
state: 'closed'
|
123
|
+
})
|
124
|
+
};
|
125
|
+
return this.json(url, options);
|
126
|
+
}
|
127
|
+
|
112
128
|
async gql(name, variables, path) {
|
113
129
|
const query = this.loadQuery(name);
|
114
130
|
if (path) {
|
@@ -201,6 +217,49 @@ export default class Request {
|
|
201
217
|
return this.json(url, options);
|
202
218
|
}
|
203
219
|
|
220
|
+
async updateReportState(reportId, state, message) {
|
221
|
+
const url = `https://api.hackerone.com/v1/reports/${reportId}/state_changes`;
|
222
|
+
const options = {
|
223
|
+
method: 'POST',
|
224
|
+
headers: {
|
225
|
+
Authorization: `Basic ${this.credentials.h1}`,
|
226
|
+
'User-Agent': 'node-core-utils',
|
227
|
+
Accept: 'application/json'
|
228
|
+
},
|
229
|
+
body: JSON.stringify({
|
230
|
+
data: {
|
231
|
+
type: 'state-change',
|
232
|
+
attributes: {
|
233
|
+
message,
|
234
|
+
state
|
235
|
+
}
|
236
|
+
}
|
237
|
+
})
|
238
|
+
};
|
239
|
+
return this.json(url, options);
|
240
|
+
}
|
241
|
+
|
242
|
+
async requestDisclosure(reportId) {
|
243
|
+
const url = `https://api.hackerone.com/v1/reports/${reportId}/disclosure_requests`;
|
244
|
+
const options = {
|
245
|
+
method: 'POST',
|
246
|
+
headers: {
|
247
|
+
Authorization: `Basic ${this.credentials.h1}`,
|
248
|
+
'User-Agent': 'node-core-utils',
|
249
|
+
Accept: 'application/json'
|
250
|
+
},
|
251
|
+
body: JSON.stringify({
|
252
|
+
data: {
|
253
|
+
attributes: {
|
254
|
+
// default to limited version
|
255
|
+
substate: 'no-content'
|
256
|
+
}
|
257
|
+
}
|
258
|
+
})
|
259
|
+
};
|
260
|
+
return this.json(url, options);
|
261
|
+
}
|
262
|
+
|
204
263
|
// This is for github v4 API queries, for other types of queries
|
205
264
|
// use .text or .json
|
206
265
|
async query(query, variables) {
|
@@ -210,3 +210,64 @@ export async function pickReport(report, { cli, req }) {
|
|
210
210
|
reporter: reporter.data.attributes.username
|
211
211
|
};
|
212
212
|
}
|
213
|
+
|
214
|
+
export class SecurityRelease {
|
215
|
+
constructor(cli, repository = NEXT_SECURITY_RELEASE_REPOSITORY) {
|
216
|
+
this.cli = cli;
|
217
|
+
this.repository = repository;
|
218
|
+
}
|
219
|
+
|
220
|
+
readVulnerabilitiesJSON(vulnerabilitiesJSONPath = this.getVulnerabilitiesJSONPath()) {
|
221
|
+
const exists = fs.existsSync(vulnerabilitiesJSONPath);
|
222
|
+
|
223
|
+
if (!exists) {
|
224
|
+
this.cli.error(`The file vulnerabilities.json does not exist at ${vulnerabilitiesJSONPath}`);
|
225
|
+
process.exit(1);
|
226
|
+
}
|
227
|
+
|
228
|
+
return JSON.parse(fs.readFileSync(vulnerabilitiesJSONPath, 'utf8'));
|
229
|
+
}
|
230
|
+
|
231
|
+
getVulnerabilitiesJSONPath() {
|
232
|
+
return path.join(process.cwd(),
|
233
|
+
NEXT_SECURITY_RELEASE_FOLDER, 'vulnerabilities.json');
|
234
|
+
}
|
235
|
+
|
236
|
+
updateReleaseFolder(releaseDate) {
|
237
|
+
const folder = path.join(process.cwd(),
|
238
|
+
NEXT_SECURITY_RELEASE_FOLDER);
|
239
|
+
const newFolder = path.join(process.cwd(), releaseDate);
|
240
|
+
fs.renameSync(folder, newFolder);
|
241
|
+
return newFolder;
|
242
|
+
}
|
243
|
+
|
244
|
+
updateVulnerabilitiesJSON(content) {
|
245
|
+
try {
|
246
|
+
const vulnerabilitiesJSONPath = this.getVulnerabilitiesJSONPath();
|
247
|
+
this.cli.startSpinner(`Updating vulnerabilities.json from ${vulnerabilitiesJSONPath}...`);
|
248
|
+
fs.writeFileSync(vulnerabilitiesJSONPath, JSON.stringify(content, null, 2));
|
249
|
+
commitAndPushVulnerabilitiesJSON(vulnerabilitiesJSONPath,
|
250
|
+
'chore: updated vulnerabilities.json',
|
251
|
+
{ cli: this.cli, repository: this.repository });
|
252
|
+
this.cli.stopSpinner(`Done updating vulnerabilities.json from ${vulnerabilitiesJSONPath}`);
|
253
|
+
} catch (error) {
|
254
|
+
this.cli.error('Error updating vulnerabilities.json');
|
255
|
+
this.cli.error(error);
|
256
|
+
}
|
257
|
+
}
|
258
|
+
|
259
|
+
getAffectedVersions(content) {
|
260
|
+
const affectedVersions = new Set();
|
261
|
+
for (const report of Object.values(content.reports)) {
|
262
|
+
for (const affectedVersion of report.affectedVersions) {
|
263
|
+
affectedVersions.add(affectedVersion);
|
264
|
+
}
|
265
|
+
}
|
266
|
+
const parseToNumber = str => +(str.match(/[\d.]+/g)[0]);
|
267
|
+
return Array.from(affectedVersions)
|
268
|
+
.sort((a, b) => {
|
269
|
+
return parseToNumber(a) > parseToNumber(b) ? -1 : 1;
|
270
|
+
})
|
271
|
+
.join(', ');
|
272
|
+
}
|
273
|
+
}
|
package/lib/security_blog.js
CHANGED
@@ -4,24 +4,17 @@ import _ from 'lodash';
|
|
4
4
|
import nv from '@pkgjs/nv';
|
5
5
|
import {
|
6
6
|
PLACEHOLDERS,
|
7
|
-
getVulnerabilitiesJSON,
|
8
7
|
checkoutOnSecurityReleaseBranch,
|
9
|
-
NEXT_SECURITY_RELEASE_REPOSITORY,
|
10
8
|
validateDate,
|
11
|
-
|
12
|
-
NEXT_SECURITY_RELEASE_FOLDER
|
9
|
+
SecurityRelease
|
13
10
|
} from './security-release/security-release.js';
|
14
11
|
import auth from './auth.js';
|
15
12
|
import Request from './request.js';
|
16
13
|
|
17
14
|
const kChanged = Symbol('changed');
|
18
15
|
|
19
|
-
export default class SecurityBlog {
|
20
|
-
repository = NEXT_SECURITY_RELEASE_REPOSITORY;
|
16
|
+
export default class SecurityBlog extends SecurityRelease {
|
21
17
|
req;
|
22
|
-
constructor(cli) {
|
23
|
-
this.cli = cli;
|
24
|
-
}
|
25
18
|
|
26
19
|
async createPreRelease() {
|
27
20
|
const { cli } = this;
|
@@ -30,7 +23,7 @@ export default class SecurityBlog {
|
|
30
23
|
checkoutOnSecurityReleaseBranch(cli, this.repository);
|
31
24
|
|
32
25
|
// read vulnerabilities JSON file
|
33
|
-
const content =
|
26
|
+
const content = this.readVulnerabilitiesJSON();
|
34
27
|
// validate the release date read from vulnerabilities JSON
|
35
28
|
if (!content.releaseDate) {
|
36
29
|
cli.error('Release date is not set in vulnerabilities.json,' +
|
@@ -72,7 +65,7 @@ export default class SecurityBlog {
|
|
72
65
|
checkoutOnSecurityReleaseBranch(cli, this.repository);
|
73
66
|
|
74
67
|
// read vulnerabilities JSON file
|
75
|
-
const content =
|
68
|
+
const content = this.readVulnerabilitiesJSON(cli);
|
76
69
|
if (!content.releaseDate) {
|
77
70
|
cli.error('Release date is not set in vulnerabilities.json,' +
|
78
71
|
' run `git node security --update-date=YYYY/MM/DD` to set the release date.');
|
@@ -113,22 +106,6 @@ export default class SecurityBlog {
|
|
113
106
|
this.updateVulnerabilitiesJSON(content);
|
114
107
|
}
|
115
108
|
|
116
|
-
updateVulnerabilitiesJSON(content) {
|
117
|
-
try {
|
118
|
-
this.cli.info('Updating vulnerabilities.json');
|
119
|
-
const vulnerabilitiesJSONPath = path.join(process.cwd(),
|
120
|
-
NEXT_SECURITY_RELEASE_FOLDER, 'vulnerabilities.json');
|
121
|
-
fs.writeFileSync(vulnerabilitiesJSONPath, JSON.stringify(content, null, 2));
|
122
|
-
const commitMessage = 'chore: updated vulnerabilities.json';
|
123
|
-
commitAndPushVulnerabilitiesJSON(vulnerabilitiesJSONPath,
|
124
|
-
commitMessage,
|
125
|
-
{ cli: this.cli, repository: this.repository });
|
126
|
-
} catch (error) {
|
127
|
-
this.cli.error('Error updating vulnerabilities.json');
|
128
|
-
this.cli.error(error);
|
129
|
-
}
|
130
|
-
}
|
131
|
-
|
132
109
|
async promptExistingPreRelease(cli) {
|
133
110
|
const pathPreRelease = await cli.prompt(
|
134
111
|
'Please provide the path of the existing pre-release announcement:', {
|
@@ -232,9 +209,10 @@ export default class SecurityBlog {
|
|
232
209
|
}
|
233
210
|
|
234
211
|
getDependencyUpdatesTemplate(dependencyUpdates) {
|
235
|
-
if (
|
236
|
-
|
237
|
-
|
212
|
+
if (typeof dependencyUpdates !== 'object') return '';
|
213
|
+
if (Object.keys(dependencyUpdates).length === 0) return '';
|
214
|
+
let template = '\nThis security release includes the following dependency' +
|
215
|
+
' updates to address public vulnerabilities:\n';
|
238
216
|
for (const dependencyUpdate of Object.values(dependencyUpdates)) {
|
239
217
|
for (const dependency of dependencyUpdate) {
|
240
218
|
const title = dependency.title.substring(dependency.title.indexOf(':') + ':'.length).trim();
|
@@ -323,16 +301,6 @@ export default class SecurityBlog {
|
|
323
301
|
return text.join('\n');
|
324
302
|
}
|
325
303
|
|
326
|
-
getAffectedVersions(content) {
|
327
|
-
const affectedVersions = new Set();
|
328
|
-
for (const report of Object.values(content.reports)) {
|
329
|
-
for (const affectedVersion of report.affectedVersions) {
|
330
|
-
affectedVersions.add(affectedVersion);
|
331
|
-
}
|
332
|
-
}
|
333
|
-
return Array.from(affectedVersions).join(', ');
|
334
|
-
}
|
335
|
-
|
336
304
|
getSecurityPreReleaseTemplate() {
|
337
305
|
return fs.readFileSync(
|
338
306
|
new URL(
|
@@ -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
|
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
|
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
|
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
|
190
|
+
return task.newListr(todo);
|
192
191
|
}
|
193
192
|
};
|
194
193
|
}
|
@@ -38,6 +38,9 @@ const fp16Ignore = `!/third_party/fp16
|
|
38
38
|
/third_party/fp16/src/*
|
39
39
|
!/third_party/fp16/src/include`;
|
40
40
|
|
41
|
+
const fastFloatReplace = `/third_party/fast_float/src/*
|
42
|
+
!/third_party/fast_float/src/include`;
|
43
|
+
|
41
44
|
export const v8Deps = [
|
42
45
|
{
|
43
46
|
name: 'trace_event',
|
@@ -103,5 +106,14 @@ export const v8Deps = [
|
|
103
106
|
repo: 'third_party/fp16/src',
|
104
107
|
gitignore: fp16Ignore,
|
105
108
|
since: 124
|
109
|
+
},
|
110
|
+
{
|
111
|
+
name: 'fast_float',
|
112
|
+
repo: 'third_party/fast_float/src',
|
113
|
+
gitignore: {
|
114
|
+
match: '/third_party/fast_float/src',
|
115
|
+
replace: fastFloatReplace
|
116
|
+
},
|
117
|
+
since: 130
|
106
118
|
}
|
107
119
|
];
|
@@ -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
|
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
|
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
|
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
|
9
|
+
task: (ctx, task) => {
|
10
|
+
return task.newListr([resetEmbedderString(), bumpNodeModule()]);
|
13
11
|
}
|
14
12
|
};
|
15
13
|
};
|
@@ -1,31 +1,23 @@
|
|
1
1
|
import {
|
2
|
-
NEXT_SECURITY_RELEASE_FOLDER,
|
3
|
-
NEXT_SECURITY_RELEASE_REPOSITORY,
|
4
2
|
checkoutOnSecurityReleaseBranch,
|
5
3
|
checkRemote,
|
6
4
|
commitAndPushVulnerabilitiesJSON,
|
7
5
|
validateDate,
|
8
6
|
pickReport,
|
9
7
|
getReportSeverity,
|
10
|
-
getSummary
|
8
|
+
getSummary,
|
9
|
+
SecurityRelease
|
11
10
|
} from './security-release/security-release.js';
|
12
11
|
import fs from 'node:fs';
|
13
|
-
import path from 'node:path';
|
14
12
|
import auth from './auth.js';
|
15
13
|
import Request from './request.js';
|
16
14
|
import nv from '@pkgjs/nv';
|
17
15
|
|
18
|
-
export default class UpdateSecurityRelease {
|
19
|
-
repository = NEXT_SECURITY_RELEASE_REPOSITORY;
|
20
|
-
constructor(cli) {
|
21
|
-
this.cli = cli;
|
22
|
-
}
|
23
|
-
|
16
|
+
export default class UpdateSecurityRelease extends SecurityRelease {
|
24
17
|
async sync() {
|
25
18
|
checkRemote(this.cli, this.repository);
|
26
19
|
|
27
|
-
const
|
28
|
-
const content = this.readVulnerabilitiesJSON(vulnerabilitiesJSONPath);
|
20
|
+
const content = this.readVulnerabilitiesJSON();
|
29
21
|
const credentials = await auth({
|
30
22
|
github: true,
|
31
23
|
h1: true
|
@@ -52,6 +44,7 @@ export default class UpdateSecurityRelease {
|
|
52
44
|
prURL
|
53
45
|
};
|
54
46
|
}
|
47
|
+
const vulnerabilitiesJSONPath = this.getVulnerabilitiesJSONPath();
|
55
48
|
fs.writeFileSync(vulnerabilitiesJSONPath, JSON.stringify(content, null, 2));
|
56
49
|
this.cli.ok('Synced vulnerabilities.json with HackerOne');
|
57
50
|
}
|
@@ -78,22 +71,6 @@ export default class UpdateSecurityRelease {
|
|
78
71
|
cli.ok('Done!');
|
79
72
|
}
|
80
73
|
|
81
|
-
readVulnerabilitiesJSON(vulnerabilitiesJSONPath) {
|
82
|
-
const exists = fs.existsSync(vulnerabilitiesJSONPath);
|
83
|
-
|
84
|
-
if (!exists) {
|
85
|
-
this.cli.error(`The file vulnerabilities.json does not exist at ${vulnerabilitiesJSONPath}`);
|
86
|
-
process.exit(1);
|
87
|
-
}
|
88
|
-
|
89
|
-
return JSON.parse(fs.readFileSync(vulnerabilitiesJSONPath, 'utf8'));
|
90
|
-
}
|
91
|
-
|
92
|
-
getVulnerabilitiesJSONPath() {
|
93
|
-
return path.join(process.cwd(),
|
94
|
-
NEXT_SECURITY_RELEASE_FOLDER, 'vulnerabilities.json');
|
95
|
-
}
|
96
|
-
|
97
74
|
async updateJSONReleaseDate(releaseDate) {
|
98
75
|
const vulnerabilitiesJSONPath = this.getVulnerabilitiesJSONPath();
|
99
76
|
const content = this.readVulnerabilitiesJSON(vulnerabilitiesJSONPath);
|
@@ -163,7 +140,7 @@ export default class UpdateSecurityRelease {
|
|
163
140
|
const programId = await this.getNodeProgramId(req);
|
164
141
|
const cves = await this.promptCVECreation(req, reports, programId);
|
165
142
|
this.assignCVEtoReport(cves, reports);
|
166
|
-
this.updateVulnerabilitiesJSON(content
|
143
|
+
this.updateVulnerabilitiesJSON(content);
|
167
144
|
this.updateHackonerReportCve(req, reports);
|
168
145
|
}
|
169
146
|
|
@@ -195,18 +172,6 @@ export default class UpdateSecurityRelease {
|
|
195
172
|
}
|
196
173
|
}
|
197
174
|
|
198
|
-
updateVulnerabilitiesJSON(content, vulnerabilitiesJSONPath) {
|
199
|
-
this.cli.startSpinner(`Updating vulnerabilities.json from\
|
200
|
-
${vulnerabilitiesJSONPath}..`);
|
201
|
-
const filePath = path.resolve(vulnerabilitiesJSONPath);
|
202
|
-
fs.writeFileSync(filePath, JSON.stringify(content, null, 2));
|
203
|
-
// push the changes to the repository
|
204
|
-
commitAndPushVulnerabilitiesJSON(filePath,
|
205
|
-
'chore: updated vulnerabilities.json with CVEs',
|
206
|
-
{ cli: this.cli, repository: this.repository });
|
207
|
-
this.cli.stopSpinner(`Done updating vulnerabilities.json from ${filePath}`);
|
208
|
-
}
|
209
|
-
|
210
175
|
async promptCVECreation(req, reports, programId) {
|
211
176
|
const supportedVersions = (await nv('supported'));
|
212
177
|
const cves = [];
|