@node-core/utils 4.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.
Files changed (98) hide show
  1. package/LICENSE +7 -0
  2. package/README.md +158 -0
  3. package/bin/get-metadata.js +11 -0
  4. package/bin/git-node.js +30 -0
  5. package/bin/ncu-ci.js +600 -0
  6. package/bin/ncu-config.js +101 -0
  7. package/bin/ncu-team.js +76 -0
  8. package/components/git/backport.js +70 -0
  9. package/components/git/epilogue.js +18 -0
  10. package/components/git/land.js +223 -0
  11. package/components/git/metadata.js +94 -0
  12. package/components/git/release.js +99 -0
  13. package/components/git/security.js +35 -0
  14. package/components/git/status.js +32 -0
  15. package/components/git/sync.js +24 -0
  16. package/components/git/v8.js +121 -0
  17. package/components/git/vote.js +84 -0
  18. package/components/git/wpt.js +87 -0
  19. package/components/metadata.js +49 -0
  20. package/lib/auth.js +133 -0
  21. package/lib/backport_session.js +302 -0
  22. package/lib/cache.js +107 -0
  23. package/lib/cherry_pick.js +304 -0
  24. package/lib/ci/build-types/benchmark_run.js +72 -0
  25. package/lib/ci/build-types/citgm_build.js +194 -0
  26. package/lib/ci/build-types/citgm_comparison_build.js +174 -0
  27. package/lib/ci/build-types/commit_build.js +112 -0
  28. package/lib/ci/build-types/daily_build.js +24 -0
  29. package/lib/ci/build-types/fanned_build.js +87 -0
  30. package/lib/ci/build-types/health_build.js +63 -0
  31. package/lib/ci/build-types/job.js +114 -0
  32. package/lib/ci/build-types/linter_build.js +35 -0
  33. package/lib/ci/build-types/normal_build.js +89 -0
  34. package/lib/ci/build-types/pr_build.js +101 -0
  35. package/lib/ci/build-types/test_build.js +186 -0
  36. package/lib/ci/build-types/test_run.js +41 -0
  37. package/lib/ci/ci_failure_parser.js +325 -0
  38. package/lib/ci/ci_type_parser.js +203 -0
  39. package/lib/ci/ci_utils.js +106 -0
  40. package/lib/ci/failure_aggregator.js +152 -0
  41. package/lib/ci/jenkins_constants.js +28 -0
  42. package/lib/ci/run_ci.js +120 -0
  43. package/lib/cli.js +192 -0
  44. package/lib/collaborators.js +140 -0
  45. package/lib/config.js +72 -0
  46. package/lib/figures.js +7 -0
  47. package/lib/file.js +43 -0
  48. package/lib/github/templates/next-security-release.md +97 -0
  49. package/lib/github/tree.js +162 -0
  50. package/lib/landing_session.js +506 -0
  51. package/lib/links.js +123 -0
  52. package/lib/mergeable_state.js +3 -0
  53. package/lib/metadata_gen.js +61 -0
  54. package/lib/pr_checker.js +605 -0
  55. package/lib/pr_data.js +115 -0
  56. package/lib/pr_summary.js +62 -0
  57. package/lib/prepare_release.js +772 -0
  58. package/lib/prepare_security.js +117 -0
  59. package/lib/proxy.js +21 -0
  60. package/lib/queries/DefaultBranchRef.gql +8 -0
  61. package/lib/queries/LastCommit.gql +16 -0
  62. package/lib/queries/PR.gql +37 -0
  63. package/lib/queries/PRComments.gql +27 -0
  64. package/lib/queries/PRCommits.gql +45 -0
  65. package/lib/queries/PRs.gql +25 -0
  66. package/lib/queries/Reviews.gql +23 -0
  67. package/lib/queries/SearchIssue.gql +51 -0
  68. package/lib/queries/Team.gql +22 -0
  69. package/lib/queries/TreeEntries.gql +12 -0
  70. package/lib/queries/VotePRInfo.gql +28 -0
  71. package/lib/release/utils.js +53 -0
  72. package/lib/request.js +185 -0
  73. package/lib/review_state.js +5 -0
  74. package/lib/reviews.js +178 -0
  75. package/lib/run.js +106 -0
  76. package/lib/session.js +415 -0
  77. package/lib/sync_session.js +15 -0
  78. package/lib/team_info.js +95 -0
  79. package/lib/update-v8/applyNodeChanges.js +49 -0
  80. package/lib/update-v8/backport.js +258 -0
  81. package/lib/update-v8/commitUpdate.js +26 -0
  82. package/lib/update-v8/common.js +35 -0
  83. package/lib/update-v8/constants.js +86 -0
  84. package/lib/update-v8/index.js +56 -0
  85. package/lib/update-v8/majorUpdate.js +171 -0
  86. package/lib/update-v8/minorUpdate.js +105 -0
  87. package/lib/update-v8/updateMaintainingDependencies.js +34 -0
  88. package/lib/update-v8/updateV8Clone.js +53 -0
  89. package/lib/update-v8/updateVersionNumbers.js +122 -0
  90. package/lib/update-v8/util.js +62 -0
  91. package/lib/user.js +4 -0
  92. package/lib/user_status.js +5 -0
  93. package/lib/utils.js +66 -0
  94. package/lib/verbosity.js +26 -0
  95. package/lib/voting_session.js +136 -0
  96. package/lib/wpt/index.js +243 -0
  97. package/lib/wpt/templates/README.md +16 -0
  98. package/package.json +69 -0
@@ -0,0 +1,136 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { once } from 'node:events';
3
+ import { env } from 'node:process';
4
+
5
+ import {
6
+ runAsync
7
+ } from './run.js';
8
+ import Session from './session.js';
9
+ import {
10
+ getEditor, isGhAvailable
11
+ } from './utils.js';
12
+
13
+ import voteUsingGit from '@node-core/caritat/voteUsingGit';
14
+ import * as yaml from 'js-yaml';
15
+
16
+ function getHTTPRepoURL(repoURL, login) {
17
+ const url = new URL(repoURL + '.git');
18
+ url.username = login;
19
+ return url.toString();
20
+ }
21
+
22
+ export default class VotingSession extends Session {
23
+ constructor(cli, req, dir, {
24
+ prid, abstain, ...argv
25
+ } = {}) {
26
+ super(cli, dir, prid, argv, false);
27
+ this.req = req;
28
+ this.abstain = abstain;
29
+ this.closeVote = argv['decrypt-key-part'];
30
+ this.postComment = argv['post-comment'];
31
+ this.gpgSign = argv['gpg-sign'];
32
+ }
33
+
34
+ get argv() {
35
+ const args = super.argv;
36
+ args.decryptKeyPart = this.closeVote;
37
+ return args;
38
+ }
39
+
40
+ async start(metadata) {
41
+ const { repository, viewer } = await this.req.gql('VotePRInfo',
42
+ { owner: this.owner, repo: this.repo, prid: this.prid });
43
+ if (repository.pullRequest.merged) {
44
+ this.cli.warn('The pull request appears to have been merged already.');
45
+ } else if (repository.pullRequest.closed) {
46
+ this.cli.warn('The pull request appears to have been closed already.');
47
+ }
48
+ if (this.closeVote) return this.decryptKeyPart(repository.pullRequest);
49
+ // @see https://git-scm.com/book/en/v2/Git-Internals-Environment-Variables#_committing
50
+ const username = process.env.GIT_AUTHOR_NAME || (await runAsync(
51
+ 'git', ['config', '--get', 'user.name'], { captureStdout: true })).trim();
52
+ const emailAddress = process.env.GIT_AUTHOR_EMAIL || (await runAsync(
53
+ 'git', ['config', '--get', 'user.email'], { captureStdout: true })).trim();
54
+ const { headRef } = repository.pullRequest;
55
+ await voteUsingGit({
56
+ GIT_BIN: 'git',
57
+ abstain: this.abstain,
58
+ EDITOR: await getEditor({ git: true }),
59
+ handle: viewer.login,
60
+ username,
61
+ emailAddress,
62
+ gpgSign: this.gpgSign,
63
+ repoURL: viewer.publicKeys.totalCount
64
+ ? headRef.repository.sshUrl
65
+ : getHTTPRepoURL(headRef.repository.url, viewer.login),
66
+ branch: headRef.name,
67
+ subPath: headRef.name
68
+ });
69
+ }
70
+
71
+ async decryptKeyPart(prInfo) {
72
+ const subPath = `${prInfo.headRef.name}/vote.yml`;
73
+ this.cli.startSpinner('Downloading vote file from remote...');
74
+ const yamlString = await this.req.text(
75
+ `https://api.github.com/repos/${this.owner}/${this.repo}/contents/${encodeURIComponent(subPath)}?ref=${prInfo.commits.nodes[0].commit.oid}`, {
76
+ agent: this.req.proxyAgent,
77
+ headers: {
78
+ Authorization: `Basic ${this.req.credentials.github}`,
79
+ 'User-Agent': 'node-core-utils',
80
+ Accept: 'application/vnd.github.raw'
81
+ }
82
+ });
83
+ this.cli.stopSpinner('Download complete');
84
+
85
+ const { shares } = yaml.load(yamlString);
86
+ const ac = new AbortController();
87
+ this.cli.startSpinner('Decrypt key part...');
88
+ const out = await Promise.any(
89
+ shares.map(async(share) => {
90
+ const cp = spawn(env.GPG_BIN || 'gpg', ['-d'], {
91
+ stdio: ['pipe', 'pipe', 'inherit'],
92
+ signal: ac.signal
93
+ });
94
+ // @ts-ignore toArray exists
95
+ const stdout = cp.stdout.toArray();
96
+ stdout.catch(Function.prototype); // ignore errors.
97
+ cp.stdin.end(share);
98
+ const [code] = await Promise.race([
99
+ once(cp, 'exit'),
100
+ once(cp, 'error').then((er) => Promise.reject(er))
101
+ ]);
102
+ if (code !== 0) throw new Error('failed', { cause: code });
103
+ return Buffer.concat(await stdout);
104
+ })
105
+ );
106
+ ac.abort();
107
+ this.cli.stopSpinner('Found one key part.');
108
+
109
+ const keyPart = '-----BEGIN SHAMIR KEY PART-----\n' +
110
+ out.toString('base64') +
111
+ '\n-----END SHAMIR KEY PART-----';
112
+ this.cli.log('Your key part is:');
113
+ this.cli.log(keyPart);
114
+ const body = 'I would like to close this vote, and for this effect, I\'m revealing my ' +
115
+ `key part:\n\n${'```'}\n${keyPart}\n${'```'}\n`;
116
+ if (this.postComment) {
117
+ const { html_url } = await this.req.json(`https://api.github.com/repos/${this.owner}/${this.repo}/issues/${this.prid}/comments`, {
118
+ agent: this.req.proxyAgent,
119
+ method: 'POST',
120
+ headers: {
121
+ Authorization: `Basic ${this.req.credentials.github}`,
122
+ 'User-Agent': 'node-core-utils',
123
+ Accept: 'application/vnd.github.antiope-preview+json'
124
+ },
125
+ body: JSON.stringify({ body })
126
+ });
127
+ this.cli.log('Comment posted at:', html_url);
128
+ } else if (isGhAvailable()) {
129
+ this.cli.log('\nRun the following command to post the comment:\n');
130
+ this.cli.log(
131
+ `gh pr comment ${this.prid} --repo ${this.owner}/${this.repo} ` +
132
+ `--body-file - <<'EOF'\n${body}\nEOF`
133
+ );
134
+ }
135
+ }
136
+ }
@@ -0,0 +1,243 @@
1
+ import path from 'node:path';
2
+
3
+ import _ from 'lodash';
4
+
5
+ import GitHubTree from '../github/tree.js';
6
+ import {
7
+ writeFile, readJson, writeJson, readFile, removeDirectory
8
+ } from '../file.js';
9
+ import {
10
+ shortSha
11
+ } from '../utils.js';
12
+
13
+ export class WPTUpdater {
14
+ constructor(path, cli, request, nodedir, commit) {
15
+ this.path = path;
16
+ this.nodedir = nodedir;
17
+
18
+ this.cli = cli;
19
+ this.request = request;
20
+ this.treeParams = {
21
+ owner: 'web-platform-tests',
22
+ repo: 'wpt',
23
+ branch: 'master',
24
+ path,
25
+ commit
26
+ };
27
+ this.tree = new GitHubTree(cli, request, this.treeParams);
28
+ this.assets = [];
29
+ }
30
+
31
+ templates(...args) {
32
+ const file = path.posix.join('templates', ...args);
33
+ return _.template(readFile(new URL(file, import.meta.url)));
34
+ }
35
+
36
+ fixtures(...args) {
37
+ return path.join(this.nodedir, 'test', 'fixtures', 'wpt', ...args);
38
+ }
39
+
40
+ get versionsPath() {
41
+ return this.fixtures('versions.json');
42
+ }
43
+
44
+ get readmePath() {
45
+ return this.fixtures('README.md');
46
+ }
47
+
48
+ getLocalVersions() {
49
+ return readJson(this.versionsPath);
50
+ }
51
+
52
+ // If filepath starts with '/', the path is relative to WPT project root,
53
+ // otherwise it's relative to the path of this updater
54
+ async pullTextFile(dest, filepath) {
55
+ const content = await this.tree.buffer(filepath);
56
+ const filename = path.join(dest, filepath);
57
+ writeFile(filename, content);
58
+ this.cli.updateSpinner(`Downloaded ${filename}`);
59
+ }
60
+
61
+ async getAssetList() {
62
+ this.cli.startSpinner(`Querying asset list for ${this.path}...`);
63
+ const assets = this.assets = await this.tree.getFiles();
64
+ this.cli.stopSpinner(
65
+ `Read asset list, ${assets.length} files in total.`
66
+ );
67
+ return assets;
68
+ }
69
+
70
+ async pullAllAssets(assets) {
71
+ const fixtures = this.fixtures(this.path);
72
+ this.cli.separator(`Writing assets to ${fixtures}...`);
73
+
74
+ if (!assets) {
75
+ assets = await this.getAssetList();
76
+ }
77
+
78
+ this.cli.startSpinner('Removing stale assets...');
79
+ await removeDirectory(this.fixtures(this.path));
80
+
81
+ this.cli.startSpinner('Pulling assets...');
82
+ await Promise.all(assets.map(
83
+ (asset) => this.pullTextFile(fixtures, asset.name)
84
+ ));
85
+ this.cli.stopSpinner(`Downloaded ${assets.length} assets.`);
86
+
87
+ return assets;
88
+ }
89
+
90
+ getTreeUrl(path, commit) {
91
+ const params = Object.assign({}, this.treeParams, { commit, path });
92
+ const tree = new GitHubTree(this.cli, this.request, params);
93
+ return tree.getPermanentUrl();
94
+ }
95
+
96
+ /**
97
+ * @param {string} nodedir
98
+ * @param {Object<string, {commit: string, path: string}>} updated
99
+ */
100
+ async updateVersions(updated) {
101
+ const versionsPath = this.versionsPath;
102
+ const readmePath = this.readmePath;
103
+ let versions = this.getLocalVersions();
104
+
105
+ this.cli.startSpinner('Updating versions.json ...');
106
+ Object.assign(versions, updated);
107
+ // Reorder keys alphabetically
108
+ versions = Object.fromEntries(
109
+ Object.entries(versions).sort(([key1], [key2]) =>
110
+ key1.localeCompare(key2)
111
+ )
112
+ );
113
+ writeJson(versionsPath, versions);
114
+ this.cli.stopSpinner(`Updated ${versionsPath}`);
115
+
116
+ const urlMap = Object.keys(versions).map(
117
+ (key) => [key, this.getTreeUrl(versions[key].path, versions[key].commit)]
118
+ );
119
+
120
+ this.cli.startSpinner('Updating README ...');
121
+ const readme = this.templates('README.md')({ map: urlMap });
122
+ writeFile(readmePath, readme);
123
+ this.cli.stopSpinner(`Updated ${readmePath}`);
124
+ }
125
+
126
+ async updateLicense() {
127
+ this.cli.startSpinner('Updating license...');
128
+ await this.pullTextFile(this.fixtures(), '/LICENSE.md');
129
+ this.cli.stopSpinner(`Updated ${this.fixtures('LICENSE.md')}.`);
130
+ }
131
+
132
+ async checkNeedsUpdate(key = this.path) {
133
+ this.cli.separator(`Checking updates for ${key}...`);
134
+
135
+ const versions = this.getLocalVersions();
136
+ const localCommit = versions[key] && versions[key].commit;
137
+ if (!localCommit) {
138
+ this.cli.log(`No records for ${key} in local ${this.versionsPath}`);
139
+ this.cli.log('pulling files from scratch...');
140
+ return true;
141
+ }
142
+
143
+ const rev = shortSha(localCommit);
144
+ this.cli.log(`Last local update for ${key} is ${rev}`);
145
+ this.cli.startSpinner('checking updates...');
146
+ const lastCommit = await this.tree.getLastCommit();
147
+
148
+ if (localCommit === lastCommit) {
149
+ this.cli.stopSpinner(
150
+ `${key} is up to date with upstream (${rev})`
151
+ );
152
+ return false;
153
+ }
154
+
155
+ const upstreamRev = shortSha(lastCommit);
156
+ this.cli.stopSpinner(`Last update in upstream is ${upstreamRev}`);
157
+ return true;
158
+ }
159
+
160
+ async update() {
161
+ const needsUpdate = await this.checkNeedsUpdate();
162
+ if (!needsUpdate) {
163
+ return;
164
+ }
165
+ await this.pullAllAssets();
166
+
167
+ const lastCommit = await this.tree.getLastCommit();
168
+ await this.updateVersions({
169
+ [this.path]: {
170
+ commit: lastCommit,
171
+ path: this.path
172
+ }
173
+ });
174
+ }
175
+ }
176
+
177
+ export class ResourcesUpdater extends WPTUpdater {
178
+ constructor(cli, request, nodedir, commit) {
179
+ super('resources', cli, request, nodedir, commit);
180
+ }
181
+
182
+ async update() { // override
183
+ const needsUpdate = await this.checkNeedsUpdate();
184
+ if (!needsUpdate) {
185
+ return;
186
+ }
187
+
188
+ const assets = await this.getAssetList();
189
+ const excludes = ['test/', 'chromium/', 'webidl2/test'];
190
+ const toDownload = assets.filter(asset =>
191
+ !excludes.find(prefix => asset.name.startsWith(prefix))
192
+ );
193
+ const excluded = assets.length - toDownload.length;
194
+ this.cli.log(`Excluded ${excluded} files, ` +
195
+ `${toDownload.length} files to write.`);
196
+
197
+ await this.pullAllAssets(toDownload);
198
+
199
+ const lastCommit = this.tree.lastCommit;
200
+ await this.updateVersions({
201
+ [this.path]: {
202
+ commit: lastCommit,
203
+ path: this.path
204
+ }
205
+ });
206
+ }
207
+ }
208
+
209
+ export class InterfacesUpdater extends WPTUpdater {
210
+ constructor(cli, request, nodedir, commit, supported) {
211
+ super('interfaces', cli, request, nodedir, commit);
212
+ this.supported = supported;
213
+ }
214
+
215
+ async update() { // override
216
+ const needsUpdate = await this.checkNeedsUpdate();
217
+ if (!needsUpdate) {
218
+ return;
219
+ }
220
+ const supported = this.supported;
221
+ const assets = await this.getAssetList();
222
+ const found = [];
223
+ for (const mod of supported) {
224
+ const idl = `${mod}.idl`;
225
+ const asset = assets.find(asset => asset.name === idl);
226
+ if (asset) {
227
+ found.push(asset);
228
+ } else {
229
+ this.cli.warn(`Couldn't find ${idl} in the upstream`);
230
+ }
231
+ }
232
+
233
+ await this.pullAllAssets(found);
234
+
235
+ const lastCommit = this.tree.lastCommit;
236
+ await this.updateVersions({
237
+ [this.path]: {
238
+ commit: lastCommit,
239
+ path: this.path
240
+ }
241
+ });
242
+ }
243
+ }
@@ -0,0 +1,16 @@
1
+ # Web Platform Test Fixtures
2
+
3
+ The files in this folder, including this document,
4
+ are generated by [`git node wpt`][].
5
+
6
+ This folder contains a subset of the [Web Platform Tests][] for the
7
+ implementation of Web APIs in Node.js.
8
+
9
+ See [test/wpt](../../wpt/README.md) for information on how these tests are run.
10
+
11
+ Last update:
12
+ <% for (const [path, url] of map) { %>
13
+ - <%= path %>: <%= url %><% } %>
14
+
15
+ [Web Platform Tests]: https://github.com/web-platform-tests/wpt
16
+ [`git node wpt`]: https://github.com/nodejs/node-core-utils/blob/main/docs/git-node.md#git-node-wpt
package/package.json ADDED
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "@node-core/utils",
3
+ "version": "4.0.0",
4
+ "description": "Utilities for Node.js core collaborators",
5
+ "type": "module",
6
+ "engines": {
7
+ "node": "^18.18.0 || >=20.0.0"
8
+ },
9
+ "bin": {
10
+ "get-metadata": "./bin/get-metadata.js",
11
+ "git-node": "./bin/git-node.js",
12
+ "ncu-config": "./bin/ncu-config.js",
13
+ "ncu-team": "./bin/ncu-team.js",
14
+ "ncu-ci": "./bin/ncu-ci.js"
15
+ },
16
+ "scripts": {
17
+ "test": "npm run test-unit && npm run lint",
18
+ "test-unit": "node --test test/unit",
19
+ "test:reporters": "node --test --test-reporter=spec --test-reporter-destination=stdout --test-reporter=@reporters/github --test-reporter-destination=stdout test/unit",
20
+ "coverage": "c8 --reporter=html --reporter=text --reporter=text-summary npm test",
21
+ "coverage:ci": "c8 --reporter=lcov --reporter=text --reporter=text-summary npm run test:reporters",
22
+ "lint": "eslint . --cache",
23
+ "lint-fix": "eslint . --fix"
24
+ },
25
+ "author": "Joyee Cheung <joyeec9h3@gmail.com>",
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "git+ssh://git@github.com:nodejs/node-core-utils.git"
29
+ },
30
+ "files": [
31
+ "lib/",
32
+ "bin/",
33
+ "components/"
34
+ ],
35
+ "license": "MIT",
36
+ "dependencies": {
37
+ "@node-core/caritat": "^1.2.0",
38
+ "branch-diff": "^2.1.4",
39
+ "chalk": "^5.3.0",
40
+ "changelog-maker": "^3.2.4",
41
+ "@pkgjs/nv": "^0.2.1",
42
+ "cheerio": "^1.0.0-rc.12",
43
+ "clipboardy": "^3.0.0",
44
+ "core-validate-commit": "^4.0.0",
45
+ "enquirer": "^2.4.1",
46
+ "figures": "^5.0.0",
47
+ "ghauth": "^5.0.1",
48
+ "inquirer": "^9.2.10",
49
+ "js-yaml": "^4.1.0",
50
+ "listr2": "^6.6.1",
51
+ "lodash": "^4.17.21",
52
+ "log-symbols": "^5.1.0",
53
+ "ora": "^7.0.1",
54
+ "replace-in-file": "^7.0.1",
55
+ "undici": "^5.23.0",
56
+ "which": "^3.0.1",
57
+ "yargs": "^17.7.2"
58
+ },
59
+ "devDependencies": {
60
+ "@reporters/github": "^1.5.2",
61
+ "c8": "^8.0.1",
62
+ "eslint": "^8.47.0",
63
+ "eslint-config-standard": "^17.1.0",
64
+ "eslint-plugin-import": "^2.28.1",
65
+ "eslint-plugin-n": "^16.0.2",
66
+ "eslint-plugin-promise": "^6.1.1",
67
+ "sinon": "^15.2.0"
68
+ }
69
+ }