@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
package/bin/ncu-ci.js ADDED
@@ -0,0 +1,600 @@
1
+ #!/usr/bin/env node
2
+
3
+ import yargs from 'yargs';
4
+ import clipboardy from 'clipboardy';
5
+
6
+ import {
7
+ JobParser,
8
+ parseJobFromURL,
9
+ CI_TYPES_KEYS
10
+ } from '../lib/ci/ci_type_parser.js';
11
+ import { setVerbosityFromEnv } from '../lib/verbosity.js';
12
+ import { listBuilds } from '../lib/ci/ci_utils.js';
13
+ import { jobCache } from '../lib/ci/build-types/job.js';
14
+ import { PRBuild } from '../lib/ci/build-types/pr_build.js';
15
+ import { CommitBuild } from '../lib/ci/build-types/commit_build.js';
16
+ import { DailyBuild } from '../lib/ci/build-types/daily_build.js';
17
+ import { FailureAggregator } from '../lib/ci/failure_aggregator.js';
18
+ import { BenchmarkRun } from '../lib/ci/build-types/benchmark_run.js';
19
+ import { HealthBuild } from '../lib/ci/build-types/health_build.js';
20
+ import { CITGMBuild } from '../lib/ci/build-types/citgm_build.js';
21
+ import {
22
+ CITGMComparisonBuild
23
+ } from '../lib/ci/build-types/citgm_comparison_build.js';
24
+ import {
25
+ RunPRJob
26
+ } from '../lib/ci/run_ci.js';
27
+ import { writeJson, writeFile } from '../lib/file.js';
28
+ import { getMergedConfig } from '../lib/config.js';
29
+ import { runPromise } from '../lib/run.js';
30
+ import auth from '../lib/auth.js';
31
+ import Request from '../lib/request.js';
32
+ import CLI from '../lib/cli.js';
33
+ import { hideBin } from 'yargs/helpers';
34
+
35
+ setVerbosityFromEnv();
36
+
37
+ const {
38
+ PR,
39
+ COMMIT,
40
+ BENCHMARK,
41
+ CITGM,
42
+ CITGM_NOBUILD,
43
+ DAILY_MASTER
44
+ } = CI_TYPES_KEYS;
45
+
46
+ const commandKeys = [
47
+ 'rate',
48
+ 'walk',
49
+ 'url',
50
+ 'pr',
51
+ 'commit',
52
+ 'citgm'
53
+ ];
54
+
55
+ const args = yargs(hideBin(process.argv))
56
+ .completion('completion')
57
+ .command({
58
+ command: 'rate <type>',
59
+ desc: 'Calculate the green rate of a CI job in the last 100 runs',
60
+ builder: (yargs) => {
61
+ yargs
62
+ .positional('type', {
63
+ describe: 'type of CI',
64
+ choices: ['commit', 'pr']
65
+ });
66
+ },
67
+ handler
68
+ })
69
+ .command({
70
+ command: 'walk <type>',
71
+ desc: 'Walk the CI and display the failures',
72
+ builder: (yargs) => {
73
+ yargs
74
+ .positional('type', {
75
+ describe: 'type of CI',
76
+ choices: ['commit', 'pr']
77
+ })
78
+ .option('stats', {
79
+ default: false,
80
+ describe: 'Aggregate the results'
81
+ })
82
+ .option('cache', {
83
+ default: false,
84
+ describe: 'Cache the responses from Jenkins in .ncu/cache/ under' +
85
+ ' the node-core-utils installation directory'
86
+ })
87
+ .option('limit', {
88
+ default: 99,
89
+ describe: 'Maximum number of CIs to get data from'
90
+ })
91
+ .option('since <date>', {
92
+ type: 'string',
93
+ describe: 'Time since when the CI results should be queried'
94
+ }).check(argv => {
95
+ try {
96
+ // eslint-disable-next-line no-new
97
+ new Date(argv.since);
98
+ } catch {
99
+ throw new Error('--since <date> should be string that can ' +
100
+ 'be parsed by new Date()');
101
+ }
102
+ return true;
103
+ });
104
+ },
105
+ handler
106
+ })
107
+ .command({
108
+ command: 'run <prid>',
109
+ desc: 'Run CI for given PR',
110
+ builder: (yargs) => {
111
+ yargs
112
+ .positional('prid', {
113
+ describe: 'ID of the PR',
114
+ type: 'number'
115
+ })
116
+ .option('owner', {
117
+ default: '',
118
+ describe: 'GitHub repository owner'
119
+ })
120
+ .option('repo', {
121
+ default: '',
122
+ describe: 'GitHub repository name'
123
+ });
124
+ },
125
+ handler
126
+ })
127
+ .command({
128
+ command: 'url <url>',
129
+ desc: 'Automatically detect CI type and show results',
130
+ builder: (yargs) => {
131
+ yargs
132
+ .positional('url', {
133
+ describe: 'URL of the PR or the CI',
134
+ type: 'string'
135
+ });
136
+ },
137
+ handler
138
+ })
139
+ .command({
140
+ command: 'pr <jobid>',
141
+ desc: 'Show results of a node-test-pull-request CI job',
142
+ builder: (yargs) => {
143
+ yargs
144
+ .positional('jobid', {
145
+ describe: 'id of the job',
146
+ type: 'number'
147
+ });
148
+ },
149
+ handler
150
+ })
151
+ .command({
152
+ command: 'commit <jobid>',
153
+ desc: 'Show results of a node-test-commit CI job',
154
+ builder: (yargs) => {
155
+ yargs
156
+ .positional('jobid', {
157
+ describe: 'id of the first job',
158
+ type: 'number'
159
+ });
160
+ },
161
+ handler
162
+ })
163
+ .command({
164
+ command: 'benchmark <jobid>',
165
+ desc: 'Show results of a benchmark-node-micro-benchmarks CI job',
166
+ builder: (yargs) => {
167
+ yargs
168
+ .positional('jobid', {
169
+ describe: 'id of the job',
170
+ type: 'number'
171
+ });
172
+ },
173
+ handler
174
+ })
175
+ .command({
176
+ command: 'citgm <jobid> [jobid2]',
177
+ desc: 'Show results of a citgm-smoker CI job',
178
+ builder: (yargs) => {
179
+ yargs
180
+ .positional('jobid', {
181
+ describe: 'id of the job',
182
+ type: 'number'
183
+ })
184
+ .positional('jobid2', {
185
+ describe: 'id of the second job, if doing a comparison',
186
+ type: 'number'
187
+ });
188
+ },
189
+ handler
190
+ })
191
+ .command({
192
+ command: 'daily',
193
+ desc: 'Show recent results of node-daily-master',
194
+ builder: (yargs) => {
195
+ yargs
196
+ .option('stats', {
197
+ default: false,
198
+ describe: 'Aggregate the results'
199
+ })
200
+ .option('cache', {
201
+ default: false,
202
+ describe: 'Cache the responses from Jenkins in .ncu/cache/ under' +
203
+ ' the node-core-utils installation directory'
204
+ })
205
+ .option('limit', {
206
+ default: 15,
207
+ describe: 'Maximum number of CIs to get data from'
208
+ });
209
+ },
210
+ handler
211
+ })
212
+ .demandCommand(1, 'must provide a valid command')
213
+ .option('copy', {
214
+ describe: 'Write the results as markdown to clipboard',
215
+ default: false,
216
+ type: 'boolean'
217
+ })
218
+ .option('nobuild', {
219
+ describe: 'If running cigtm, whether or not jobid is citgm-nobuild.',
220
+ type: 'boolean',
221
+ default: false
222
+ })
223
+ .option('json <path>', {
224
+ type: 'string',
225
+ describe: 'Write the results as json to <path>'
226
+ })
227
+ .option('markdown <path>', {
228
+ type: 'string',
229
+ describe: 'Write the results as markdown to <path>'
230
+ }).check(argv => {
231
+ if (argv.markdown && commandKeys.includes(argv.markdown)) {
232
+ throw new Error('--markdown <path> did not specify a valid path');
233
+ } else if (argv.json && commandKeys.includes(argv.json)) {
234
+ throw new Error('--json <path> did not specify a valid path');
235
+ }
236
+ return true;
237
+ })
238
+ .help();
239
+
240
+ args.parse();
241
+
242
+ const commandToType = {
243
+ commit: COMMIT,
244
+ pr: PR,
245
+ benchmark: BENCHMARK,
246
+ citgm: CITGM
247
+ };
248
+
249
+ class RunPRJobCommand {
250
+ constructor(cli, request, argv) {
251
+ this.cli = cli;
252
+ this.request = request;
253
+ this.dir = process.cwd();
254
+ this.argv = argv;
255
+ this.config = getMergedConfig(this.dir);
256
+ }
257
+
258
+ get owner() {
259
+ return this.argv.owner || this.config.owner;
260
+ }
261
+
262
+ get repo() {
263
+ return this.argv.repo || this.config.repo;
264
+ }
265
+
266
+ get prid() {
267
+ return this.argv.prid;
268
+ }
269
+
270
+ async start() {
271
+ const {
272
+ cli, request, prid, repo, owner
273
+ } = this;
274
+ let validArgs = true;
275
+ if (!repo) {
276
+ validArgs = false;
277
+ cli.error('GitHub repository is missing, please set it via ncu-config ' +
278
+ 'or pass it via the --repo option');
279
+ }
280
+ if (!owner) {
281
+ cli.error('GitHub owner is missing, please set it via ncu-config ' +
282
+ 'or pass it via the --owner option');
283
+ validArgs = false;
284
+ }
285
+ if (!validArgs) {
286
+ this.cli.setExitCode(1);
287
+ return;
288
+ }
289
+ const jobRunner = new RunPRJob(cli, request, owner, repo, prid);
290
+ if (!(await jobRunner.start())) {
291
+ this.cli.setExitCode(1);
292
+ process.exitCode = 1;
293
+ }
294
+ }
295
+ }
296
+
297
+ class CICommand {
298
+ constructor(cli, request, argv) {
299
+ this.cli = cli;
300
+ this.request = request;
301
+ this.argv = argv;
302
+ this.queue = [];
303
+ this.json = [];
304
+ this.markdown = '';
305
+ }
306
+
307
+ async drain() {
308
+ if (this.queue.length === 0) {
309
+ return;
310
+ }
311
+
312
+ const { cli, queue, argv, request } = this;
313
+
314
+ for (let i = 0; i < queue.length; ++i) {
315
+ const job = queue[i];
316
+ cli.separator('');
317
+ const progress = `[${i + 1}/${queue.length}]`;
318
+ if (job.link) {
319
+ cli.log(`${progress} Running ${job.link}`);
320
+ } else if (job.jobid) {
321
+ cli.log(`${progress} Running ${job.type}: ${job.jobid}`);
322
+ } else {
323
+ cli.log(`${progress} Running ${job.type}`);
324
+ }
325
+ cli.separator('');
326
+
327
+ let build;
328
+ switch (job.type) {
329
+ case 'health':
330
+ build = new HealthBuild(cli, request, job.ciType, job.builds);
331
+ break;
332
+ case PR:
333
+ build = new PRBuild(cli, request, job.jobid);
334
+ break;
335
+ case COMMIT:
336
+ build = new CommitBuild(cli, request, job.jobid);
337
+ break;
338
+ case CITGM:
339
+ case CITGM_NOBUILD:
340
+ if (job.jobid2) {
341
+ build = new CITGMComparisonBuild(cli, request, job);
342
+ } else {
343
+ build = new CITGMBuild(cli, request, job);
344
+ }
345
+ break;
346
+ case BENCHMARK:
347
+ build = new BenchmarkRun(cli, request, job.jobid);
348
+ break;
349
+ case DAILY_MASTER: {
350
+ const daily = new DailyBuild(cli, request, job.jobid);
351
+ const data = await daily.getBuildData();
352
+ const testCommitBuild = data.subBuilds.filter(subBuild => {
353
+ return subBuild.jobName === 'node-test-commit';
354
+ })[0];
355
+ if (testCommitBuild) {
356
+ build = new CommitBuild(cli, request, testCommitBuild.buildNumber);
357
+ break;
358
+ } else {
359
+ throw new Error('Could not find \'node-test-commit\' job');
360
+ }
361
+ }
362
+ default:
363
+ throw new Error(`Unknown job type ${job.type}`);
364
+ }
365
+
366
+ await build.getResults();
367
+ build.display();
368
+
369
+ // Set this.json regardless of whether the user has passed
370
+ // --json - it's needed for failure aggregation.
371
+ const json = build.formatAsJson();
372
+ if (json !== undefined) {
373
+ this.json = this.json.concat(json);
374
+ }
375
+
376
+ if ((argv.copy || argv.markdown) && !argv.stats) {
377
+ this.markdown += build.formatAsMarkdown();
378
+ }
379
+ }
380
+ }
381
+
382
+ async aggregate() {} // noop
383
+
384
+ async serialize() {
385
+ const { argv, cli } = this;
386
+
387
+ if (argv.copy) {
388
+ if (this.markdown) {
389
+ clipboardy.writeSync(this.markdown);
390
+ cli.separator('');
391
+ cli.log('Written markdown to clipboard');
392
+ } else {
393
+ cli.error('No markdown generated');
394
+ }
395
+ }
396
+
397
+ if (argv.markdown) {
398
+ if (this.markdown) {
399
+ writeFile(argv.markdown, this.markdown);
400
+ cli.separator('');
401
+ cli.log(`Written markdown to ${argv.markdown}`);
402
+ } else {
403
+ cli.error('No markdown generated');
404
+ }
405
+ }
406
+
407
+ if (argv.json) {
408
+ if (this.json.length) {
409
+ writeJson(argv.json, this.json);
410
+ cli.separator('');
411
+ cli.log(`Wrote JSON to ${argv.json}`);
412
+ } else {
413
+ cli.error('No JSON generated');
414
+ }
415
+ }
416
+ }
417
+ }
418
+
419
+ class RateCommand extends CICommand {
420
+ async initialize() {
421
+ this.queue.push({
422
+ type: 'health',
423
+ ciType: commandToType[this.argv.type]
424
+ });
425
+ }
426
+ }
427
+
428
+ class WalkCommand extends CICommand {
429
+ constructor(cli, request, argv) {
430
+ super(cli, request, argv);
431
+ if (argv.cache) {
432
+ jobCache.enable();
433
+ }
434
+ }
435
+
436
+ async initialize() {
437
+ const ciType = commandToType[this.argv.type];
438
+ const since = this.argv.since ? new Date(this.argv.since) : undefined;
439
+ const builds = await listBuilds(this.cli, this.request, ciType, since);
440
+ if (builds.count === 0) {
441
+ this.cli.log('No applicable builds found.');
442
+ return;
443
+ }
444
+ this.queue.push({ type: 'health', ciType, builds });
445
+ for (const build of builds.failed.slice(0, this.argv.limit)) {
446
+ this.queue.push(build);
447
+ }
448
+ }
449
+
450
+ async aggregate() {
451
+ const { argv, cli } = this;
452
+ if (this.queue.length === 0) {
453
+ return;
454
+ }
455
+ const aggregator = new FailureAggregator(cli, this.json);
456
+ this.json = aggregator.aggregate();
457
+ cli.log('');
458
+ cli.separator('Stats');
459
+ cli.log('');
460
+ aggregator.display();
461
+
462
+ if (argv.markdown || argv.copy) {
463
+ this.markdown = aggregator.formatAsMarkdown();
464
+ }
465
+ }
466
+ }
467
+
468
+ class JobCommand extends CICommand {
469
+ constructor(cli, request, argv, command) {
470
+ super(cli, request, argv);
471
+ this.command = command;
472
+ }
473
+
474
+ async initialize() {
475
+ const { queue, argv } = this;
476
+
477
+ queue.push({
478
+ type: commandToType[this.command],
479
+ jobid: argv.jobid,
480
+ jobid2: argv.jobid2,
481
+ noBuild: this.argv.nobuild
482
+ });
483
+ }
484
+ }
485
+
486
+ class URLCommand extends CICommand {
487
+ async initialize() {
488
+ const { argv, cli, request, queue } = this;
489
+ const parsed = parseJobFromURL(argv.url);
490
+ if (parsed) {
491
+ queue.push({
492
+ type: parsed.type,
493
+ jobid: parsed.jobid
494
+ });
495
+ return;
496
+ }
497
+
498
+ // Parse CI links from PR.
499
+ const parser = await JobParser.fromPR(argv.url, cli, request);
500
+ if (!parser) { // Not a valid PR URL
501
+ cli.error(`${argv.url} is not a valid PR URL`);
502
+ return;
503
+ }
504
+ const ciMap = parser.parse();
505
+ if (ciMap.size === 0) {
506
+ cli.info(`No CI run detected from ${argv.url}`);
507
+ }
508
+ for (const [type, ci] of ciMap) {
509
+ queue.push({
510
+ type,
511
+ jobid: ci.jobid
512
+ });
513
+ }
514
+ }
515
+ }
516
+
517
+ class DailyCommand extends CICommand {
518
+ constructor(cli, request, argv) {
519
+ super(cli, request, argv);
520
+ if (argv.cache) {
521
+ jobCache.enable();
522
+ }
523
+ }
524
+
525
+ async initialize() {
526
+ const ciType = DAILY_MASTER;
527
+ const builds = await listBuilds(this.cli, this.request, ciType);
528
+ this.queue.push({ type: 'health', ciType, builds });
529
+ for (const build of builds.failed.slice(0, this.argv.limit)) {
530
+ this.queue.push(build);
531
+ }
532
+ }
533
+
534
+ async aggregate() {
535
+ const { argv, cli } = this;
536
+ const aggregator = new FailureAggregator(cli, this.json);
537
+ this.json = aggregator.aggregate();
538
+ cli.log('');
539
+ cli.separator('Stats');
540
+ cli.log('');
541
+ aggregator.display();
542
+
543
+ if (argv.markdown || argv.copy) {
544
+ this.markdown = aggregator.formatAsMarkdown();
545
+ }
546
+ }
547
+ }
548
+
549
+ async function main(command, argv) {
550
+ const cli = new CLI();
551
+ const credentials = await auth({
552
+ github: true,
553
+ jenkins: true
554
+ });
555
+ const request = new Request(credentials);
556
+
557
+ let commandHandler;
558
+ // Prepare queue.
559
+ switch (command) {
560
+ case 'run': {
561
+ const jobRunner = new RunPRJobCommand(cli, request, argv);
562
+ return jobRunner.start();
563
+ }
564
+ case 'rate': {
565
+ commandHandler = new RateCommand(cli, request, argv);
566
+ break;
567
+ }
568
+ case 'walk': {
569
+ commandHandler = new WalkCommand(cli, request, argv);
570
+ break;
571
+ }
572
+ case 'url': {
573
+ commandHandler = new URLCommand(cli, request, argv);
574
+ break;
575
+ }
576
+ case 'pr':
577
+ case 'commit':
578
+ case 'citgm':
579
+ case 'benchmark': {
580
+ commandHandler = new JobCommand(cli, request, argv, command);
581
+ break;
582
+ }
583
+ case 'daily': {
584
+ commandHandler = new DailyCommand(cli, request, argv, command);
585
+ break;
586
+ }
587
+ default:
588
+ return args.showHelp();
589
+ }
590
+
591
+ await commandHandler.initialize();
592
+ await commandHandler.drain();
593
+ await commandHandler.aggregate();
594
+ await commandHandler.serialize();
595
+ }
596
+
597
+ function handler(argv) {
598
+ const [command] = argv._;
599
+ runPromise(main(command, argv));
600
+ }
@@ -0,0 +1,101 @@
1
+ #!/usr/bin/env node
2
+
3
+ import yargs from 'yargs';
4
+ import { hideBin } from 'yargs/helpers';
5
+
6
+ import {
7
+ getConfig, updateConfig, GLOBAL_CONFIG, PROJECT_CONFIG, LOCAL_CONFIG
8
+ } from '../lib/config.js';
9
+ import { setVerbosityFromEnv } from '../lib/verbosity.js';
10
+
11
+ setVerbosityFromEnv();
12
+
13
+ const args = yargs(hideBin(process.argv))
14
+ .completion('completion')
15
+ .command({
16
+ command: 'set <key> <value>',
17
+ desc: 'Set a config variable',
18
+ builder: (yargs) => {
19
+ yargs
20
+ .positional('key', {
21
+ describe: 'key of the configuration',
22
+ type: 'string'
23
+ })
24
+ .positional('value', {
25
+ describe: 'value of the configuration'
26
+ });
27
+ },
28
+ handler: setHandler
29
+ })
30
+ .command({
31
+ command: 'get <key>',
32
+ desc: 'Get a config variable',
33
+ builder: (yargs) => {
34
+ yargs
35
+ .positional('key', {
36
+ describe: 'key of the configuration',
37
+ type: 'string'
38
+ });
39
+ },
40
+ handler: getHandler
41
+ })
42
+ .command({
43
+ command: 'list',
44
+ desc: 'List the configurations',
45
+ handler: listHandler
46
+ })
47
+ .demandCommand(1, 'must provide a valid command')
48
+ // Can't set default of boolean variables if using conflict
49
+ // https://github.com/yargs/yargs/issues/929
50
+ // default: false
51
+ .option('global', {
52
+ alias: 'g',
53
+ type: 'boolean',
54
+ describe: 'Use global config (~/.ncurc)'
55
+ })
56
+ .option('project', {
57
+ alias: 'p',
58
+ type: 'boolean',
59
+ describe: 'Use project config (./.ncurc)'
60
+ })
61
+ .conflicts('global', 'project')
62
+ .help();
63
+
64
+ const argv = args.parse();
65
+
66
+ function getConfigType(argv) {
67
+ if (argv.global) {
68
+ return { configName: 'global', configType: GLOBAL_CONFIG };
69
+ }
70
+ if (argv.project) {
71
+ return { configName: 'project', configType: PROJECT_CONFIG };
72
+ }
73
+ return { configName: 'local', configType: LOCAL_CONFIG };
74
+ }
75
+
76
+ function setHandler(argv) {
77
+ const { configName, configType } = getConfigType(argv);
78
+ const config = getConfig(configType);
79
+ console.log(
80
+ `Updating ${configName} configuration ` +
81
+ `[${argv.key}]: ${config[argv.key]} -> ${argv.value}`);
82
+ updateConfig(configType, { [argv.key]: argv.value });
83
+ }
84
+
85
+ function getHandler(argv) {
86
+ const { configType } = getConfigType(argv);
87
+ const config = getConfig(configType);
88
+ console.log(config[argv.key]);
89
+ }
90
+
91
+ function listHandler(argv) {
92
+ const { configType } = getConfigType(argv);
93
+ const config = getConfig(configType);
94
+ for (const key of Object.keys(config)) {
95
+ console.log(`${key}: ${config[key]}`);
96
+ }
97
+ }
98
+
99
+ if (!['get', 'set', 'list'].includes(argv._[0])) {
100
+ args.showHelp();
101
+ }