@capraconsulting/cals-cli 3.6.0 → 3.7.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/lib/{cals-cli.js → cals-cli.mjs} +226 -304
- package/lib/{cals-cli.js.map → cals-cli.mjs.map} +1 -1
- package/lib/definition/index.d.ts +1 -1
- package/lib/github/index.d.ts +3 -2
- package/lib/index.es.js +79 -86
- package/lib/index.es.js.map +1 -1
- package/lib/index.js +153 -223
- package/lib/index.js.map +1 -1
- package/lib/snyk/index.d.ts +1 -1
- package/lib/sonarcloud/index.d.ts +2 -1
- package/package.json +15 -11
|
@@ -1,74 +1,32 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
2
|
+
import semver from 'semver';
|
|
3
|
+
import yargs from 'yargs';
|
|
4
|
+
import { hideBin } from 'yargs/helpers';
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
import yaml from 'js-yaml';
|
|
7
|
+
import { uniq, keyBy, sortBy, groupBy, sumBy, repeat } from 'lodash-es';
|
|
8
|
+
import pMap from 'p-map';
|
|
9
|
+
import AJV from 'ajv';
|
|
10
|
+
import { Octokit } from '@octokit/rest';
|
|
11
|
+
import fetch from 'node-fetch';
|
|
12
|
+
import pLimit from 'p-limit';
|
|
13
|
+
import keytar from 'keytar';
|
|
14
|
+
import * as process$2 from 'process';
|
|
15
|
+
import { performance } from 'perf_hooks';
|
|
16
|
+
import { deprecate } from 'util';
|
|
17
|
+
import path from 'path';
|
|
18
|
+
import rimraf from 'rimraf';
|
|
19
|
+
import cachedir from 'cachedir';
|
|
20
|
+
import https from 'https';
|
|
21
|
+
import os from 'os';
|
|
22
|
+
import chalk from 'chalk';
|
|
23
|
+
import readline from 'readline';
|
|
24
|
+
import { sprintf } from 'sprintf-js';
|
|
25
|
+
import read from 'read';
|
|
26
|
+
import findUp from 'find-up';
|
|
27
|
+
import execa from 'execa';
|
|
3
28
|
|
|
4
|
-
var
|
|
5
|
-
var yargs = require('yargs');
|
|
6
|
-
var fs = require('fs');
|
|
7
|
-
var yaml = require('js-yaml');
|
|
8
|
-
var lodash = require('lodash');
|
|
9
|
-
var pMap = require('p-map');
|
|
10
|
-
var AJV = require('ajv');
|
|
11
|
-
var rest = require('@octokit/rest');
|
|
12
|
-
var fetch = require('node-fetch');
|
|
13
|
-
var pLimit = require('p-limit');
|
|
14
|
-
var keytar = require('keytar');
|
|
15
|
-
var process$2 = require('process');
|
|
16
|
-
var perf_hooks = require('perf_hooks');
|
|
17
|
-
var util = require('util');
|
|
18
|
-
var path = require('path');
|
|
19
|
-
var rimraf = require('rimraf');
|
|
20
|
-
var cachedir = require('cachedir');
|
|
21
|
-
var https = require('https');
|
|
22
|
-
var os = require('os');
|
|
23
|
-
var chalk = require('chalk');
|
|
24
|
-
var readline = require('readline');
|
|
25
|
-
var sprintfJs = require('sprintf-js');
|
|
26
|
-
var read = require('read');
|
|
27
|
-
var findUp = require('find-up');
|
|
28
|
-
var execa = require('execa');
|
|
29
|
-
|
|
30
|
-
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
31
|
-
|
|
32
|
-
function _interopNamespace(e) {
|
|
33
|
-
if (e && e.__esModule) return e;
|
|
34
|
-
var n = Object.create(null);
|
|
35
|
-
if (e) {
|
|
36
|
-
Object.keys(e).forEach(function (k) {
|
|
37
|
-
if (k !== 'default') {
|
|
38
|
-
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
39
|
-
Object.defineProperty(n, k, d.get ? d : {
|
|
40
|
-
enumerable: true,
|
|
41
|
-
get: function () { return e[k]; }
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
n["default"] = e;
|
|
47
|
-
return Object.freeze(n);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
var semver__default = /*#__PURE__*/_interopDefaultLegacy(semver);
|
|
51
|
-
var yargs__default = /*#__PURE__*/_interopDefaultLegacy(yargs);
|
|
52
|
-
var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
|
|
53
|
-
var yaml__default = /*#__PURE__*/_interopDefaultLegacy(yaml);
|
|
54
|
-
var pMap__default = /*#__PURE__*/_interopDefaultLegacy(pMap);
|
|
55
|
-
var AJV__default = /*#__PURE__*/_interopDefaultLegacy(AJV);
|
|
56
|
-
var fetch__default = /*#__PURE__*/_interopDefaultLegacy(fetch);
|
|
57
|
-
var pLimit__default = /*#__PURE__*/_interopDefaultLegacy(pLimit);
|
|
58
|
-
var keytar__default = /*#__PURE__*/_interopDefaultLegacy(keytar);
|
|
59
|
-
var process__namespace = /*#__PURE__*/_interopNamespace(process$2);
|
|
60
|
-
var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
|
|
61
|
-
var rimraf__default = /*#__PURE__*/_interopDefaultLegacy(rimraf);
|
|
62
|
-
var cachedir__default = /*#__PURE__*/_interopDefaultLegacy(cachedir);
|
|
63
|
-
var https__default = /*#__PURE__*/_interopDefaultLegacy(https);
|
|
64
|
-
var os__default = /*#__PURE__*/_interopDefaultLegacy(os);
|
|
65
|
-
var chalk__default = /*#__PURE__*/_interopDefaultLegacy(chalk);
|
|
66
|
-
var readline__default = /*#__PURE__*/_interopDefaultLegacy(readline);
|
|
67
|
-
var read__default = /*#__PURE__*/_interopDefaultLegacy(read);
|
|
68
|
-
var findUp__default = /*#__PURE__*/_interopDefaultLegacy(findUp);
|
|
69
|
-
var execa__default = /*#__PURE__*/_interopDefaultLegacy(execa);
|
|
70
|
-
|
|
71
|
-
var version = "3.6.0";
|
|
29
|
+
var version = "3.7.0";
|
|
72
30
|
var engines = {
|
|
73
31
|
node: ">=12.0.0"
|
|
74
32
|
};
|
|
@@ -374,12 +332,11 @@ function getRepoId(orgName, repoName) {
|
|
|
374
332
|
return `${orgName}/${repoName}`;
|
|
375
333
|
}
|
|
376
334
|
function checkAgainstSchema(value) {
|
|
377
|
-
|
|
378
|
-
const ajv = new AJV__default["default"]({ allErrors: true });
|
|
335
|
+
const ajv = new AJV({ allErrors: true });
|
|
379
336
|
const valid = ajv.validate(schema, value);
|
|
380
337
|
return valid
|
|
381
338
|
? { definition: value }
|
|
382
|
-
: { error:
|
|
339
|
+
: { error: ajv.errorsText() ?? "Unknown error" };
|
|
383
340
|
}
|
|
384
341
|
function requireValidDefinition(definition) {
|
|
385
342
|
// Verify no duplicates in users and extract known logins.
|
|
@@ -424,7 +381,8 @@ function requireValidDefinition(definition) {
|
|
|
424
381
|
if (!teamIdList.includes(id)) {
|
|
425
382
|
throw new Error(`Project team ${id} in project ${project.name} is not registered in team list`);
|
|
426
383
|
}
|
|
427
|
-
})
|
|
384
|
+
}) // Verify repo teams exists as teams.
|
|
385
|
+
;
|
|
428
386
|
(org.repos || []).forEach((repo) => {
|
|
429
387
|
(repo.teams || []).forEach((team) => {
|
|
430
388
|
const id = getTeamId(org.organization, team.name);
|
|
@@ -448,11 +406,12 @@ function requireValidDefinition(definition) {
|
|
|
448
406
|
}, []);
|
|
449
407
|
}
|
|
450
408
|
class DefinitionFile {
|
|
409
|
+
path;
|
|
451
410
|
constructor(path) {
|
|
452
411
|
this.path = path;
|
|
453
412
|
}
|
|
454
413
|
async getContents() {
|
|
455
|
-
return new Promise((resolve, reject) =>
|
|
414
|
+
return new Promise((resolve, reject) => fs.readFile(this.path, "utf-8", (err, data) => {
|
|
456
415
|
if (err)
|
|
457
416
|
reject(err);
|
|
458
417
|
else
|
|
@@ -464,7 +423,7 @@ class DefinitionFile {
|
|
|
464
423
|
}
|
|
465
424
|
}
|
|
466
425
|
function parseDefinition(value) {
|
|
467
|
-
const result = checkAgainstSchema(
|
|
426
|
+
const result = checkAgainstSchema(yaml.load(value));
|
|
468
427
|
if ("error" in result) {
|
|
469
428
|
throw new Error("Definition content invalid: " + result.error);
|
|
470
429
|
}
|
|
@@ -482,19 +441,17 @@ function getRepos(definition) {
|
|
|
482
441
|
.flat());
|
|
483
442
|
}
|
|
484
443
|
function getGitHubOrgs(definition) {
|
|
485
|
-
return
|
|
444
|
+
return uniq(definition.projects.flatMap((project) => project.github.map((it) => it.organization)));
|
|
486
445
|
}
|
|
487
446
|
|
|
488
447
|
class GitHubTokenCliProvider {
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
this.keyringAccount = "github-token";
|
|
492
|
-
}
|
|
448
|
+
keyringService = "cals";
|
|
449
|
+
keyringAccount = "github-token";
|
|
493
450
|
async getToken() {
|
|
494
451
|
if (process.env.CALS_GITHUB_TOKEN) {
|
|
495
452
|
return process.env.CALS_GITHUB_TOKEN;
|
|
496
453
|
}
|
|
497
|
-
const result = await
|
|
454
|
+
const result = await keytar.getPassword(this.keyringService, this.keyringAccount);
|
|
498
455
|
if (result == null) {
|
|
499
456
|
process.stderr.write("No token found. Register using `cals github set-token`\n");
|
|
500
457
|
return undefined;
|
|
@@ -502,10 +459,10 @@ class GitHubTokenCliProvider {
|
|
|
502
459
|
return result;
|
|
503
460
|
}
|
|
504
461
|
async markInvalid() {
|
|
505
|
-
await
|
|
462
|
+
await keytar.deletePassword(this.keyringService, this.keyringAccount);
|
|
506
463
|
}
|
|
507
464
|
async setToken(value) {
|
|
508
|
-
await
|
|
465
|
+
await keytar.setPassword(this.keyringService, this.keyringAccount, value);
|
|
509
466
|
}
|
|
510
467
|
}
|
|
511
468
|
|
|
@@ -570,15 +527,19 @@ async function undefinedForNotFound(value) {
|
|
|
570
527
|
}
|
|
571
528
|
|
|
572
529
|
class GitHubService {
|
|
530
|
+
config;
|
|
531
|
+
octokit;
|
|
532
|
+
cache;
|
|
533
|
+
tokenProvider;
|
|
534
|
+
semaphore;
|
|
573
535
|
constructor(props) {
|
|
574
|
-
this._requestCount = 0;
|
|
575
536
|
this.config = props.config;
|
|
576
537
|
this.octokit = props.octokit;
|
|
577
538
|
this.cache = props.cache;
|
|
578
539
|
this.tokenProvider = props.tokenProvider;
|
|
579
540
|
// Control concurrency to GitHub API at service level so we
|
|
580
541
|
// can maximize concurrency all other places.
|
|
581
|
-
this.semaphore =
|
|
542
|
+
this.semaphore = pLimit(6);
|
|
582
543
|
this.octokit.hook.wrap("request", async (request, options) => {
|
|
583
544
|
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
|
584
545
|
this._requestCount++;
|
|
@@ -644,6 +605,7 @@ class GitHubService {
|
|
|
644
605
|
/* eslint-enable @typescript-eslint/no-unsafe-member-access */
|
|
645
606
|
});
|
|
646
607
|
}
|
|
608
|
+
_requestCount = 0;
|
|
647
609
|
get requestCount() {
|
|
648
610
|
return this._requestCount;
|
|
649
611
|
}
|
|
@@ -658,18 +620,18 @@ class GitHubService {
|
|
|
658
620
|
};
|
|
659
621
|
let requestDuration = -1;
|
|
660
622
|
const response = await this.semaphore(() => {
|
|
661
|
-
const requestStart =
|
|
662
|
-
const result =
|
|
623
|
+
const requestStart = performance.now();
|
|
624
|
+
const result = fetch(url, {
|
|
663
625
|
method: "POST",
|
|
664
626
|
headers,
|
|
665
627
|
body: JSON.stringify({ query }),
|
|
666
628
|
agent: this.config.agent,
|
|
667
629
|
});
|
|
668
|
-
requestDuration =
|
|
630
|
+
requestDuration = performance.now() - requestStart;
|
|
669
631
|
return result;
|
|
670
632
|
});
|
|
671
633
|
if (response.status === 401) {
|
|
672
|
-
|
|
634
|
+
process$2.stderr.write("Unauthorized\n");
|
|
673
635
|
await this.tokenProvider.markInvalid();
|
|
674
636
|
}
|
|
675
637
|
// If you get 502 after 10s, it is a timeout.
|
|
@@ -762,15 +724,12 @@ class GitHubService {
|
|
|
762
724
|
login: it.login,
|
|
763
725
|
data: it,
|
|
764
726
|
})),
|
|
765
|
-
...(await this.getOrgMembersInvitedList(org)).map((it) => {
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
data: it,
|
|
772
|
-
});
|
|
773
|
-
}),
|
|
727
|
+
...(await this.getOrgMembersInvitedList(org)).map((it) => ({
|
|
728
|
+
type: "invited",
|
|
729
|
+
// TODO: Fix ?? case properly
|
|
730
|
+
login: it.login ?? "invalid",
|
|
731
|
+
data: it,
|
|
732
|
+
})),
|
|
774
733
|
];
|
|
775
734
|
}
|
|
776
735
|
async getRepository(owner, repo) {
|
|
@@ -839,15 +798,12 @@ class GitHubService {
|
|
|
839
798
|
login: it.login,
|
|
840
799
|
data: it,
|
|
841
800
|
})),
|
|
842
|
-
...(await this.getTeamMemberInvitedList(org, team)).map((it) => {
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
data: it,
|
|
849
|
-
});
|
|
850
|
-
}),
|
|
801
|
+
...(await this.getTeamMemberInvitedList(org, team)).map((it) => ({
|
|
802
|
+
type: "invited",
|
|
803
|
+
// TODO: Fix ?? case properly
|
|
804
|
+
login: it.login ?? "invalid",
|
|
805
|
+
data: it,
|
|
806
|
+
})),
|
|
851
807
|
];
|
|
852
808
|
}
|
|
853
809
|
async getSearchedPullRequestList(owner) {
|
|
@@ -972,17 +928,16 @@ class GitHubService {
|
|
|
972
928
|
}
|
|
973
929
|
}`;
|
|
974
930
|
return this.cache.json(`vulnerability-alerts-${owner}-${repo}`, async () => {
|
|
975
|
-
var _a, _b, _c, _d, _e;
|
|
976
931
|
const result = [];
|
|
977
932
|
let after = null;
|
|
978
933
|
while (true) {
|
|
979
934
|
const query = getQuery(after);
|
|
980
935
|
const res = await this.runGraphqlQuery(query);
|
|
981
|
-
result.push(...(
|
|
982
|
-
if (!
|
|
936
|
+
result.push(...(res.repository?.vulnerabilityAlerts.edges?.map((it) => it.node) ?? []));
|
|
937
|
+
if (!res.repository?.vulnerabilityAlerts.pageInfo.hasNextPage) {
|
|
983
938
|
break;
|
|
984
939
|
}
|
|
985
|
-
after =
|
|
940
|
+
after = res.repository?.vulnerabilityAlerts.pageInfo.endCursor;
|
|
986
941
|
}
|
|
987
942
|
return result;
|
|
988
943
|
});
|
|
@@ -1024,27 +979,23 @@ class GitHubService {
|
|
|
1024
979
|
}
|
|
1025
980
|
}`;
|
|
1026
981
|
const issues = await this.cache.json(`renovate-bot-issues-${owner}-${repo}`, async () => {
|
|
1027
|
-
var _a, _b, _c, _d, _e;
|
|
1028
982
|
const result = [];
|
|
1029
983
|
let after = null;
|
|
1030
984
|
while (true) {
|
|
1031
985
|
const query = getQuery(after);
|
|
1032
986
|
const res = await this.runGraphqlQuery(query);
|
|
1033
|
-
const nodes =
|
|
987
|
+
const nodes = res.repository?.issues.edges?.map((it) => it.node) ?? [];
|
|
1034
988
|
result.push(...nodes
|
|
1035
989
|
.filter((it) => it.title === "Dependency Dashboard")
|
|
1036
|
-
.map((it) => {
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
});
|
|
1043
|
-
}));
|
|
1044
|
-
if (!((_d = res.repository) === null || _d === void 0 ? void 0 : _d.issues.pageInfo.hasNextPage)) {
|
|
990
|
+
.map((it) => ({
|
|
991
|
+
number: it.number,
|
|
992
|
+
body: it.body,
|
|
993
|
+
lastUpdatedByRenovate: it.userContentEdits?.nodes?.filter((it) => it.editor?.login === "renovate")?.[0]?.createdAt ?? null,
|
|
994
|
+
})));
|
|
995
|
+
if (!res.repository?.issues.pageInfo.hasNextPage) {
|
|
1045
996
|
break;
|
|
1046
997
|
}
|
|
1047
|
-
after =
|
|
998
|
+
after = res.repository?.issues.pageInfo.endCursor;
|
|
1048
999
|
}
|
|
1049
1000
|
return result;
|
|
1050
1001
|
});
|
|
@@ -1055,7 +1006,7 @@ class GitHubService {
|
|
|
1055
1006
|
}
|
|
1056
1007
|
}
|
|
1057
1008
|
async function createOctokit(config, tokenProvider) {
|
|
1058
|
-
return new
|
|
1009
|
+
return new Octokit({
|
|
1059
1010
|
auth: await tokenProvider.getToken(),
|
|
1060
1011
|
request: {
|
|
1061
1012
|
agent: config.agent,
|
|
@@ -1063,8 +1014,7 @@ async function createOctokit(config, tokenProvider) {
|
|
|
1063
1014
|
});
|
|
1064
1015
|
}
|
|
1065
1016
|
async function createGitHubService(props) {
|
|
1066
|
-
|
|
1067
|
-
const tokenProvider = (_a = props.tokenProvider) !== null && _a !== void 0 ? _a : new GitHubTokenCliProvider();
|
|
1017
|
+
const tokenProvider = props.tokenProvider ?? new GitHubTokenCliProvider();
|
|
1068
1018
|
return new GitHubService({
|
|
1069
1019
|
config: props.config,
|
|
1070
1020
|
octokit: await createOctokit(props.config, tokenProvider),
|
|
@@ -1074,15 +1024,13 @@ async function createGitHubService(props) {
|
|
|
1074
1024
|
}
|
|
1075
1025
|
|
|
1076
1026
|
class SnykTokenCliProvider {
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
this.keyringAccount = "snyk-token";
|
|
1080
|
-
}
|
|
1027
|
+
keyringService = "cals";
|
|
1028
|
+
keyringAccount = "snyk-token";
|
|
1081
1029
|
async getToken() {
|
|
1082
1030
|
if (process.env.CALS_SNYK_TOKEN) {
|
|
1083
1031
|
return process.env.CALS_SNYK_TOKEN;
|
|
1084
1032
|
}
|
|
1085
|
-
const result = await
|
|
1033
|
+
const result = await keytar.getPassword(this.keyringService, this.keyringAccount);
|
|
1086
1034
|
if (result == null) {
|
|
1087
1035
|
process.stderr.write("No token found. Register using `cals snyk set-token`\n");
|
|
1088
1036
|
return undefined;
|
|
@@ -1090,21 +1038,22 @@ class SnykTokenCliProvider {
|
|
|
1090
1038
|
return result;
|
|
1091
1039
|
}
|
|
1092
1040
|
async markInvalid() {
|
|
1093
|
-
await
|
|
1041
|
+
await keytar.deletePassword(this.keyringService, this.keyringAccount);
|
|
1094
1042
|
}
|
|
1095
1043
|
async setToken(value) {
|
|
1096
|
-
await
|
|
1044
|
+
await keytar.setPassword(this.keyringService, this.keyringAccount, value);
|
|
1097
1045
|
}
|
|
1098
1046
|
}
|
|
1099
1047
|
|
|
1100
1048
|
class SnykService {
|
|
1049
|
+
config;
|
|
1050
|
+
tokenProvider;
|
|
1101
1051
|
constructor(props) {
|
|
1102
1052
|
this.config = props.config;
|
|
1103
1053
|
this.tokenProvider = props.tokenProvider;
|
|
1104
1054
|
}
|
|
1105
1055
|
async getProjects(definition) {
|
|
1106
|
-
|
|
1107
|
-
const snykAccountId = (_a = definition.snyk) === null || _a === void 0 ? void 0 : _a.accountId;
|
|
1056
|
+
const snykAccountId = definition.snyk?.accountId;
|
|
1108
1057
|
if (snykAccountId === undefined) {
|
|
1109
1058
|
return [];
|
|
1110
1059
|
}
|
|
@@ -1132,7 +1081,7 @@ class SnykService {
|
|
|
1132
1081
|
* We continue calling the Snyk API and retrieving more projects until links.next is null
|
|
1133
1082
|
* */
|
|
1134
1083
|
while (nextUrl) {
|
|
1135
|
-
const response = await
|
|
1084
|
+
const response = await fetch(`https://api.snyk.io/rest${nextUrl}`, {
|
|
1136
1085
|
method: "GET",
|
|
1137
1086
|
headers: {
|
|
1138
1087
|
Accept: "application/json",
|
|
@@ -1169,7 +1118,7 @@ class SnykService {
|
|
|
1169
1118
|
totalDependencies: project.meta.latest_dependency_total.total,
|
|
1170
1119
|
issueCountsBySeverity: project.meta.latest_issue_counts,
|
|
1171
1120
|
lastTestedDate: project.meta.latest_dependency_total.updated_at,
|
|
1172
|
-
browseUrl: `https://app.snyk.io/org/${snykOrgSlugId
|
|
1121
|
+
browseUrl: `https://app.snyk.io/org/${snykOrgSlugId ?? "it"}/project/${project.id}`,
|
|
1173
1122
|
};
|
|
1174
1123
|
}),
|
|
1175
1124
|
];
|
|
@@ -1181,10 +1130,9 @@ class SnykService {
|
|
|
1181
1130
|
}
|
|
1182
1131
|
}
|
|
1183
1132
|
function createSnykService(props) {
|
|
1184
|
-
var _a;
|
|
1185
1133
|
return new SnykService({
|
|
1186
1134
|
config: props.config,
|
|
1187
|
-
tokenProvider:
|
|
1135
|
+
tokenProvider: props.tokenProvider ?? new SnykTokenCliProvider(),
|
|
1188
1136
|
});
|
|
1189
1137
|
}
|
|
1190
1138
|
|
|
@@ -1222,23 +1170,24 @@ function getGitHubRepoId(repo) {
|
|
|
1222
1170
|
|
|
1223
1171
|
class CacheProvider {
|
|
1224
1172
|
constructor(config) {
|
|
1225
|
-
this.mustValidate = false;
|
|
1226
|
-
this.defaultCacheTime = 1800;
|
|
1227
1173
|
this.config = config;
|
|
1228
1174
|
}
|
|
1175
|
+
mustValidate = false;
|
|
1176
|
+
config;
|
|
1177
|
+
defaultCacheTime = 1800;
|
|
1229
1178
|
/**
|
|
1230
1179
|
* Retrieve cache if existent, ignoring the time.
|
|
1231
1180
|
*
|
|
1232
1181
|
* The caller is responsible for handling proper validation,
|
|
1233
1182
|
*/
|
|
1234
1183
|
retrieveJson(cachekey) {
|
|
1235
|
-
const cachefile =
|
|
1236
|
-
if (!
|
|
1184
|
+
const cachefile = path.join(this.config.cacheDir, `${cachekey}.json`);
|
|
1185
|
+
if (!fs.existsSync(cachefile)) {
|
|
1237
1186
|
return undefined;
|
|
1238
1187
|
}
|
|
1239
|
-
const data =
|
|
1188
|
+
const data = fs.readFileSync(cachefile, "utf-8");
|
|
1240
1189
|
return {
|
|
1241
|
-
cacheTime:
|
|
1190
|
+
cacheTime: fs.statSync(cachefile).mtime.getTime(),
|
|
1242
1191
|
data: (data === "undefined" ? undefined : JSON.parse(data)),
|
|
1243
1192
|
};
|
|
1244
1193
|
}
|
|
@@ -1246,11 +1195,11 @@ class CacheProvider {
|
|
|
1246
1195
|
* Save data to cache.
|
|
1247
1196
|
*/
|
|
1248
1197
|
storeJson(cachekey, data) {
|
|
1249
|
-
const cachefile =
|
|
1250
|
-
if (!
|
|
1251
|
-
|
|
1198
|
+
const cachefile = path.join(this.config.cacheDir, `${cachekey}.json`);
|
|
1199
|
+
if (!fs.existsSync(this.config.cacheDir)) {
|
|
1200
|
+
fs.mkdirSync(this.config.cacheDir, { recursive: true });
|
|
1252
1201
|
}
|
|
1253
|
-
|
|
1202
|
+
fs.writeFileSync(cachefile, data === undefined ? "undefined" : JSON.stringify(data));
|
|
1254
1203
|
}
|
|
1255
1204
|
async json(cachekey, block, cachetime = this.defaultCacheTime) {
|
|
1256
1205
|
const cacheItem = this.mustValidate
|
|
@@ -1268,20 +1217,18 @@ class CacheProvider {
|
|
|
1268
1217
|
* Delete all cached data.
|
|
1269
1218
|
*/
|
|
1270
1219
|
cleanup() {
|
|
1271
|
-
|
|
1220
|
+
rimraf.sync(this.config.cacheDir);
|
|
1272
1221
|
}
|
|
1273
1222
|
}
|
|
1274
1223
|
|
|
1275
1224
|
class Config {
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
this.configCached = undefined;
|
|
1284
|
-
}
|
|
1225
|
+
cwd = path.resolve(process.cwd());
|
|
1226
|
+
configFile = path.join(os.homedir(), ".cals-config.json");
|
|
1227
|
+
cacheDir = cachedir("cals-cli");
|
|
1228
|
+
agent = new https.Agent({
|
|
1229
|
+
keepAlive: true,
|
|
1230
|
+
});
|
|
1231
|
+
configCached = undefined;
|
|
1285
1232
|
get config() {
|
|
1286
1233
|
const existingConfig = this.configCached;
|
|
1287
1234
|
if (existingConfig !== undefined) {
|
|
@@ -1292,12 +1239,12 @@ class Config {
|
|
|
1292
1239
|
return config;
|
|
1293
1240
|
}
|
|
1294
1241
|
readConfig() {
|
|
1295
|
-
if (!
|
|
1242
|
+
if (!fs.existsSync(this.configFile)) {
|
|
1296
1243
|
return {};
|
|
1297
1244
|
}
|
|
1298
1245
|
try {
|
|
1299
1246
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
1300
|
-
return JSON.parse(
|
|
1247
|
+
return JSON.parse(fs.readFileSync(this.configFile, "utf-8"));
|
|
1301
1248
|
}
|
|
1302
1249
|
catch (e) {
|
|
1303
1250
|
console.error("Failed", e);
|
|
@@ -1319,27 +1266,29 @@ class Config {
|
|
|
1319
1266
|
...this.readConfig(),
|
|
1320
1267
|
[key]: value, // undefined will remove
|
|
1321
1268
|
};
|
|
1322
|
-
|
|
1269
|
+
fs.writeFileSync(this.configFile, JSON.stringify(updatedConfig, null, " "));
|
|
1323
1270
|
this.configCached = updatedConfig;
|
|
1324
1271
|
}
|
|
1325
1272
|
}
|
|
1326
1273
|
|
|
1327
1274
|
const CLEAR_WHOLE_LINE = 0;
|
|
1328
1275
|
function clearLine(stdout) {
|
|
1329
|
-
|
|
1330
|
-
|
|
1276
|
+
readline.clearLine(stdout, CLEAR_WHOLE_LINE);
|
|
1277
|
+
readline.cursorTo(stdout, 0);
|
|
1331
1278
|
}
|
|
1332
1279
|
class Reporter {
|
|
1333
1280
|
constructor(opts = {}) {
|
|
1334
|
-
this.stdout = process.stdout;
|
|
1335
|
-
this.stderr = process.stderr;
|
|
1336
|
-
this.stdin = process.stdin;
|
|
1337
|
-
this.isTTY = this.stdout.isTTY;
|
|
1338
|
-
this.format = chalk__default["default"];
|
|
1339
|
-
this.startTime = Date.now();
|
|
1340
1281
|
this.nonInteractive = !!opts.nonInteractive;
|
|
1341
1282
|
this.isVerbose = !!opts.verbose;
|
|
1342
1283
|
}
|
|
1284
|
+
stdout = process.stdout;
|
|
1285
|
+
stderr = process.stderr;
|
|
1286
|
+
stdin = process.stdin;
|
|
1287
|
+
isTTY = this.stdout.isTTY;
|
|
1288
|
+
nonInteractive;
|
|
1289
|
+
isVerbose;
|
|
1290
|
+
format = chalk;
|
|
1291
|
+
startTime = Date.now();
|
|
1343
1292
|
error(msg) {
|
|
1344
1293
|
clearLine(this.stderr);
|
|
1345
1294
|
this.stderr.write(`${this.format.red("error")} ${msg}\n`);
|
|
@@ -1376,7 +1325,7 @@ function createCacheProvider(config, argv) {
|
|
|
1376
1325
|
}
|
|
1377
1326
|
// old option: --no-cache
|
|
1378
1327
|
if (argv.cache === false) {
|
|
1379
|
-
|
|
1328
|
+
deprecate(() => {
|
|
1380
1329
|
cache.mustValidate = true;
|
|
1381
1330
|
}, "The --no-cache option is deprecated. See new --validate-cache option")();
|
|
1382
1331
|
}
|
|
@@ -1396,7 +1345,7 @@ function getDefinitionFile(argv) {
|
|
|
1396
1345
|
throw Error("Missing --definition-file option");
|
|
1397
1346
|
}
|
|
1398
1347
|
const definitionFile = argv.definitionFile;
|
|
1399
|
-
if (!
|
|
1348
|
+
if (!fs.existsSync(definitionFile)) {
|
|
1400
1349
|
throw Error(`The file ${definitionFile} does not exist`);
|
|
1401
1350
|
}
|
|
1402
1351
|
return new DefinitionFile(definitionFile);
|
|
@@ -1453,9 +1402,9 @@ function reorderListToSimilarAsBefore(oldList, updatedList, selector, insertLast
|
|
|
1453
1402
|
}
|
|
1454
1403
|
|
|
1455
1404
|
async function getReposFromGitHub(github, orgs) {
|
|
1456
|
-
return (await
|
|
1405
|
+
return (await pMap(orgs, async (org) => {
|
|
1457
1406
|
const repos = await github.getOrgRepoList({ org: org.login });
|
|
1458
|
-
return
|
|
1407
|
+
return pMap(repos, async (repo) => {
|
|
1459
1408
|
const detailedRepo = await github.getRepository(repo.owner.login, repo.name);
|
|
1460
1409
|
if (detailedRepo === undefined) {
|
|
1461
1410
|
throw Error(`Repo not found: ${repo.owner.login}/${repo.name}`);
|
|
@@ -1469,11 +1418,11 @@ async function getReposFromGitHub(github, orgs) {
|
|
|
1469
1418
|
})).flat();
|
|
1470
1419
|
}
|
|
1471
1420
|
async function getTeams(github, orgs) {
|
|
1472
|
-
const intermediate = await
|
|
1421
|
+
const intermediate = await pMap(orgs, async (org) => {
|
|
1473
1422
|
const teams = await github.getTeamList(org);
|
|
1474
1423
|
return {
|
|
1475
1424
|
org,
|
|
1476
|
-
teams: await
|
|
1425
|
+
teams: await pMap(teams, async (team) => ({
|
|
1477
1426
|
team,
|
|
1478
1427
|
users: await github.getTeamMemberListIncludingInvited(org, team),
|
|
1479
1428
|
})),
|
|
@@ -1502,11 +1451,11 @@ function getFormattedTeams(oldTeams, teams) {
|
|
|
1502
1451
|
permission: it.permission,
|
|
1503
1452
|
}));
|
|
1504
1453
|
return result
|
|
1505
|
-
? reorderListToSimilarAsBefore(oldTeams
|
|
1454
|
+
? reorderListToSimilarAsBefore(oldTeams ?? [], result, (it) => it.name)
|
|
1506
1455
|
: undefined;
|
|
1507
1456
|
}
|
|
1508
1457
|
async function getOrgs(github, orgs) {
|
|
1509
|
-
return
|
|
1458
|
+
return pMap(orgs, (it) => github.getOrg(it));
|
|
1510
1459
|
}
|
|
1511
1460
|
function removeDuplicates(items, selector) {
|
|
1512
1461
|
const ids = [];
|
|
@@ -1521,7 +1470,7 @@ function removeDuplicates(items, selector) {
|
|
|
1521
1470
|
return result;
|
|
1522
1471
|
}
|
|
1523
1472
|
async function getMembers(github, orgs) {
|
|
1524
|
-
return removeDuplicates((await
|
|
1473
|
+
return removeDuplicates((await pMap(orgs, (org) => github.getOrgMembersListIncludingInvited(org.login)))
|
|
1525
1474
|
.flat()
|
|
1526
1475
|
.map((it) => it.login), (it) => it);
|
|
1527
1476
|
}
|
|
@@ -1535,15 +1484,14 @@ async function getProjects(github, orgs, definition, snyk) {
|
|
|
1535
1484
|
const snykReposPromise = getSnykRepos(snyk, definition);
|
|
1536
1485
|
const repos = await getReposFromGitHub(github, orgs);
|
|
1537
1486
|
const snykRepos = await snykReposPromise;
|
|
1538
|
-
const definitionRepos =
|
|
1487
|
+
const definitionRepos = keyBy(getRepos(definition), (it) => it.id);
|
|
1539
1488
|
const projectGroups = Object.values(repos.reduce((acc, cur) => {
|
|
1540
|
-
var _a, _b, _c, _d;
|
|
1541
1489
|
const org = cur.repository.owner.login;
|
|
1542
1490
|
const repoId = getRepoId(org, cur.repository.name);
|
|
1543
|
-
const projectName =
|
|
1491
|
+
const projectName = definitionRepos[repoId]?.project?.name ?? "Unknown";
|
|
1544
1492
|
const project = acc[projectName] || {
|
|
1545
1493
|
name: projectName,
|
|
1546
|
-
definition:
|
|
1494
|
+
definition: definitionRepos[repoId]?.project,
|
|
1547
1495
|
repos: [],
|
|
1548
1496
|
};
|
|
1549
1497
|
return {
|
|
@@ -1558,39 +1506,36 @@ async function getProjects(github, orgs, definition, snyk) {
|
|
|
1558
1506
|
};
|
|
1559
1507
|
}, {}));
|
|
1560
1508
|
const projects = projectGroups.map((project) => {
|
|
1561
|
-
var _a, _b;
|
|
1562
1509
|
const github = Object.entries(project.repos).map(([org, list]) => {
|
|
1563
|
-
var _a, _b, _c, _d;
|
|
1564
1510
|
const commonTeams = getCommonTeams(list);
|
|
1565
|
-
const oldOrg =
|
|
1511
|
+
const oldOrg = project.definition?.github?.find((it) => it.organization == org);
|
|
1566
1512
|
const repos = list.map((repo) => {
|
|
1567
|
-
var _a, _b;
|
|
1568
1513
|
const repoId = getRepoId(repo.basic.owner.login, repo.basic.name);
|
|
1569
1514
|
const definitionRepo = definitionRepos[repoId];
|
|
1570
1515
|
const result = {
|
|
1571
1516
|
name: repo.basic.name,
|
|
1572
|
-
previousNames: definitionRepo
|
|
1517
|
+
previousNames: definitionRepo?.repo.previousNames,
|
|
1573
1518
|
archived: repo.repository.archived ? true : undefined,
|
|
1574
1519
|
issues: repo.repository.has_issues ? undefined : false,
|
|
1575
1520
|
wiki: repo.repository.has_wiki ? undefined : false,
|
|
1576
|
-
teams: getFormattedTeams(
|
|
1521
|
+
teams: getFormattedTeams(definitionRepo?.repo?.teams ?? [], getSpecificTeams(repo.teams, commonTeams)),
|
|
1577
1522
|
snyk: snykRepos.includes(repoId) ? true : undefined,
|
|
1578
1523
|
public: repo.repository.private ? undefined : true,
|
|
1579
|
-
responsible: definitionRepo
|
|
1524
|
+
responsible: definitionRepo?.repo.responsible,
|
|
1580
1525
|
};
|
|
1581
1526
|
// Try to preserve property order.
|
|
1582
1527
|
return Object.fromEntries(reorderListToSimilarAsBefore(definitionRepo ? Object.entries(definitionRepo.repo) : [], Object.entries(result), (it) => it[0], true));
|
|
1583
1528
|
});
|
|
1584
|
-
const teams = getFormattedTeams(
|
|
1529
|
+
const teams = getFormattedTeams(oldOrg?.teams ?? [], commonTeams);
|
|
1585
1530
|
return {
|
|
1586
1531
|
organization: org,
|
|
1587
1532
|
teams: teams,
|
|
1588
|
-
repos: reorderListToSimilarAsBefore(
|
|
1533
|
+
repos: reorderListToSimilarAsBefore(oldOrg?.repos ?? [], repos, (it) => it.name),
|
|
1589
1534
|
};
|
|
1590
1535
|
});
|
|
1591
1536
|
return {
|
|
1592
1537
|
name: project.name,
|
|
1593
|
-
github: reorderListToSimilarAsBefore(
|
|
1538
|
+
github: reorderListToSimilarAsBefore(project.definition?.github ?? [], github, (it) => it.organization),
|
|
1594
1539
|
};
|
|
1595
1540
|
});
|
|
1596
1541
|
return reorderListToSimilarAsBefore(definition.projects, projects, (it) => it.name);
|
|
@@ -1635,7 +1580,7 @@ async function dumpSetup(config, reporter, github, snyk, outfile, definitionFile
|
|
|
1635
1580
|
// package. However it often produced invalid yaml, so we have removed
|
|
1636
1581
|
// it. We might want to revisit it to preserve comments.
|
|
1637
1582
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-explicit-any
|
|
1638
|
-
const doc =
|
|
1583
|
+
const doc = yaml.load(await definitionFile.getContents());
|
|
1639
1584
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
1640
1585
|
doc.snyk = generatedDefinition.snyk;
|
|
1641
1586
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
@@ -1643,7 +1588,7 @@ async function dumpSetup(config, reporter, github, snyk, outfile, definitionFile
|
|
|
1643
1588
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
1644
1589
|
doc.github = generatedDefinition.github;
|
|
1645
1590
|
// Convert to/from plain JSON so that undefined elements are removed.
|
|
1646
|
-
|
|
1591
|
+
fs.writeFileSync(outfile, yaml.dump(JSON.parse(JSON.stringify(doc))));
|
|
1647
1592
|
reporter.info(`Saved to ${outfile}`);
|
|
1648
1593
|
reporter.info(`Number of GitHub requests: ${github.requestCount}`);
|
|
1649
1594
|
}
|
|
@@ -1688,7 +1633,7 @@ const command$f = {
|
|
|
1688
1633
|
.demandCommand()
|
|
1689
1634
|
.usage(`cals definition`),
|
|
1690
1635
|
handler: () => {
|
|
1691
|
-
|
|
1636
|
+
yargs(hideBin(process.argv)).showHelp();
|
|
1692
1637
|
},
|
|
1693
1638
|
};
|
|
1694
1639
|
|
|
@@ -1716,9 +1661,9 @@ const command$d = {
|
|
|
1716
1661
|
async function analyzeDirectory(reporter, config, github, org) {
|
|
1717
1662
|
const repos = await github.getOrgRepoList({ org });
|
|
1718
1663
|
const reposDict = repos.reduce((acc, cur) => ({ ...acc, [cur.name]: cur }), {});
|
|
1719
|
-
const dirs =
|
|
1664
|
+
const dirs = fs
|
|
1720
1665
|
.readdirSync(config.cwd)
|
|
1721
|
-
.filter((it) =>
|
|
1666
|
+
.filter((it) => fs.statSync(path.join(config.cwd, it)).isDirectory())
|
|
1722
1667
|
// Skip hidden folders
|
|
1723
1668
|
.filter((it) => !it.startsWith("."))
|
|
1724
1669
|
.sort((a, b) => a.localeCompare(b));
|
|
@@ -1729,18 +1674,18 @@ async function analyzeDirectory(reporter, config, github, org) {
|
|
|
1729
1674
|
};
|
|
1730
1675
|
dirs.forEach((it) => {
|
|
1731
1676
|
if (!(it in reposDict)) {
|
|
1732
|
-
reporter.warn(
|
|
1677
|
+
reporter.warn(sprintf("%-30s <-- Not found in repository list (maybe changed name?)", it));
|
|
1733
1678
|
stats.unknown++;
|
|
1734
1679
|
return;
|
|
1735
1680
|
}
|
|
1736
1681
|
if (reposDict[it].isArchived) {
|
|
1737
|
-
reporter.info(
|
|
1682
|
+
reporter.info(sprintf("%-30s <-- Archived", it));
|
|
1738
1683
|
stats.archived++;
|
|
1739
1684
|
return;
|
|
1740
1685
|
}
|
|
1741
1686
|
stats.ok += 1;
|
|
1742
1687
|
});
|
|
1743
|
-
reporter.info(
|
|
1688
|
+
reporter.info(sprintf("Stats: unknown=%d archived=%d ok=%d", stats.unknown, stats.archived, stats.ok));
|
|
1744
1689
|
reporter.info("Use `cals github generate-clone-commands` to check for repositories not checked out");
|
|
1745
1690
|
}
|
|
1746
1691
|
const command$c = {
|
|
@@ -1763,7 +1708,6 @@ const command$c = {
|
|
|
1763
1708
|
};
|
|
1764
1709
|
|
|
1765
1710
|
function getChangedRepoAttribs(definitionRepo, actualRepo) {
|
|
1766
|
-
var _a, _b;
|
|
1767
1711
|
const attribs = [];
|
|
1768
1712
|
const archived = definitionRepo.archived || false;
|
|
1769
1713
|
if (archived !== actualRepo.archived) {
|
|
@@ -1771,13 +1715,13 @@ function getChangedRepoAttribs(definitionRepo, actualRepo) {
|
|
|
1771
1715
|
archived,
|
|
1772
1716
|
});
|
|
1773
1717
|
}
|
|
1774
|
-
const issues =
|
|
1718
|
+
const issues = definitionRepo.issues ?? true;
|
|
1775
1719
|
if (issues !== actualRepo.has_issues && !actualRepo.archived) {
|
|
1776
1720
|
attribs.push({
|
|
1777
1721
|
issues,
|
|
1778
1722
|
});
|
|
1779
1723
|
}
|
|
1780
|
-
const wiki =
|
|
1724
|
+
const wiki = definitionRepo.wiki ?? true;
|
|
1781
1725
|
if (wiki !== actualRepo.has_wiki && !actualRepo.archived) {
|
|
1782
1726
|
attribs.push({
|
|
1783
1727
|
wiki,
|
|
@@ -1794,7 +1738,7 @@ function getChangedRepoAttribs(definitionRepo, actualRepo) {
|
|
|
1794
1738
|
async function getUnknownRepos(github, definition, limitToOrg) {
|
|
1795
1739
|
const knownRepos = getRepos(definition).map((it) => it.id);
|
|
1796
1740
|
const orgs = getGitHubOrgs(definition).filter((orgName) => limitToOrg === undefined || limitToOrg === orgName);
|
|
1797
|
-
return
|
|
1741
|
+
return sortBy((await pMap(orgs, (orgName) => github.getOrgRepoList({ org: orgName })))
|
|
1798
1742
|
.flat()
|
|
1799
1743
|
.filter((it) => !knownRepos.includes(`${it.owner.login}/${it.name}`)), (it) => `${it.owner.login}/${it.name}`);
|
|
1800
1744
|
}
|
|
@@ -1809,9 +1753,8 @@ function getExpectedTeams(projectTeams, repoTeams) {
|
|
|
1809
1753
|
];
|
|
1810
1754
|
}
|
|
1811
1755
|
async function getRepoTeamChanges({ github, org, projectRepo, repo, }) {
|
|
1812
|
-
var _a, _b;
|
|
1813
1756
|
const changes = [];
|
|
1814
|
-
const expectedTeams = getExpectedTeams(
|
|
1757
|
+
const expectedTeams = getExpectedTeams(org.teams ?? [], projectRepo.teams ?? []);
|
|
1815
1758
|
const existingTeams = await github.getRepositoryTeamsList(repo);
|
|
1816
1759
|
// Check for teams to be added / modified.
|
|
1817
1760
|
for (const repoteam of expectedTeams) {
|
|
@@ -1889,7 +1832,7 @@ async function createChangeSetItemsForProjects(github, definition, limitToOrg) {
|
|
|
1889
1832
|
const orgs = definition.projects
|
|
1890
1833
|
.flatMap((it) => it.github)
|
|
1891
1834
|
.filter((org) => limitToOrg === undefined || limitToOrg === org.organization);
|
|
1892
|
-
changes.push(...(await
|
|
1835
|
+
changes.push(...(await pMap(orgs, async (org) => pMap(org.repos || [], (projectRepo) => getProjectRepoChanges({
|
|
1893
1836
|
github,
|
|
1894
1837
|
org,
|
|
1895
1838
|
projectRepo,
|
|
@@ -1987,7 +1930,7 @@ async function createChangeSetItemsForTeams(github, definition, org) {
|
|
|
1987
1930
|
}
|
|
1988
1931
|
});
|
|
1989
1932
|
const overlappingTeams = actualTeams.filter((it) => wantedTeamNames.includes(it.name));
|
|
1990
|
-
await
|
|
1933
|
+
await pMap(overlappingTeams, async (actualTeam) => {
|
|
1991
1934
|
const wantedTeam = teams.find((it) => it.name === actualTeam.name);
|
|
1992
1935
|
const actualMembers = await github.getTeamMemberListIncludingInvited(org, actualTeam);
|
|
1993
1936
|
actualMembers
|
|
@@ -2190,7 +2133,7 @@ function createOrgGetter(github) {
|
|
|
2190
2133
|
const semaphores = {};
|
|
2191
2134
|
function getSemaphore(orgName) {
|
|
2192
2135
|
if (!(orgName in semaphores)) {
|
|
2193
|
-
semaphores[orgName] =
|
|
2136
|
+
semaphores[orgName] = pLimit(1);
|
|
2194
2137
|
}
|
|
2195
2138
|
return semaphores[orgName];
|
|
2196
2139
|
}
|
|
@@ -2247,7 +2190,7 @@ async function process$1(reporter, github, definition, getOrg, execute, limitToO
|
|
|
2247
2190
|
}
|
|
2248
2191
|
if (execute && changes.length > 0) {
|
|
2249
2192
|
const cont = await new Promise((resolve, reject) => {
|
|
2250
|
-
|
|
2193
|
+
read({
|
|
2251
2194
|
prompt: "Confirm you want to execute the changes [y/N]: ",
|
|
2252
2195
|
timeout: 60000,
|
|
2253
2196
|
}, (err, answer) => {
|
|
@@ -2298,7 +2241,7 @@ const command$b = {
|
|
|
2298
2241
|
|
|
2299
2242
|
async function generateCloneCommands({ reporter, config, github, org, ...opt }) {
|
|
2300
2243
|
if (!opt.listGroups && !opt.all && opt.group === undefined) {
|
|
2301
|
-
|
|
2244
|
+
yargs(hideBin(process.argv)).showHelp();
|
|
2302
2245
|
return;
|
|
2303
2246
|
}
|
|
2304
2247
|
const repos = await github.getOrgRepoList({ org });
|
|
@@ -2318,11 +2261,11 @@ async function generateCloneCommands({ reporter, config, github, org, ...opt })
|
|
|
2318
2261
|
.filter((it) => opt.name === undefined || it.name.includes(opt.name))
|
|
2319
2262
|
.filter((it) => opt.topic === undefined || includesTopic(it, opt.topic))
|
|
2320
2263
|
.filter((it) => !opt.excludeExisting ||
|
|
2321
|
-
!
|
|
2264
|
+
!fs.existsSync(path.resolve(config.cwd, it.name)))
|
|
2322
2265
|
.forEach((repo) => {
|
|
2323
2266
|
// The output of this is used to pipe into e.g. bash.
|
|
2324
2267
|
// We cannot use reporter.log as it adds additional characters.
|
|
2325
|
-
process.stdout.write(
|
|
2268
|
+
process.stdout.write(sprintf('[ ! -e "%s" ] && git clone %s\n', repo.name, repo.sshUrl));
|
|
2326
2269
|
});
|
|
2327
2270
|
});
|
|
2328
2271
|
}
|
|
@@ -2420,10 +2363,10 @@ async function listPullRequestsStats({ reporter, github, }) {
|
|
|
2420
2363
|
reporter.log("Pull requests stats:");
|
|
2421
2364
|
reporter.log("A pull request is considered old after 60 days");
|
|
2422
2365
|
reporter.log("");
|
|
2423
|
-
reporter.log(
|
|
2424
|
-
reporter.log(
|
|
2366
|
+
reporter.log(sprintf("%-40s %12s %2s %12s %2s", "", "normal", "", "snyk", ""));
|
|
2367
|
+
reporter.log(sprintf("%-40s %7s %7s %7s %7s", "Repo", "old", "recent", "old", "recent"));
|
|
2425
2368
|
categories.forEach((cat) => {
|
|
2426
|
-
reporter.log(
|
|
2369
|
+
reporter.log(sprintf("%-40s %7s %7s %7s %7s", cat.key, cat.old.length === 0 ? "" : cat.old.length, cat.recent.length === 0 ? "" : cat.recent.length, cat.oldSnyk.length === 0 ? "" : cat.oldSnyk.length, cat.recentSnyk.length === 0 ? "" : cat.recentSnyk.length));
|
|
2427
2370
|
});
|
|
2428
2371
|
}
|
|
2429
2372
|
}
|
|
@@ -2588,15 +2531,15 @@ async function listWebhooks(reporter, cache, github, org) {
|
|
|
2588
2531
|
}
|
|
2589
2532
|
switch (hook.name) {
|
|
2590
2533
|
case "web":
|
|
2591
|
-
reporter.log(
|
|
2534
|
+
reporter.log(sprintf(" web: %s (%s) (%s)", hook.config.url, hook.last_response.code, hook.events.join(", ")));
|
|
2592
2535
|
break;
|
|
2593
2536
|
case "jenkinsgit":
|
|
2594
|
-
reporter.log(
|
|
2537
|
+
reporter.log(sprintf(" jenkinsgit: %s (%s) (%s)",
|
|
2595
2538
|
// This is undocumented.
|
|
2596
2539
|
hook.config.jenkins_url, hook.last_response.code, hook.events.join(", ")));
|
|
2597
2540
|
break;
|
|
2598
2541
|
case "docker":
|
|
2599
|
-
reporter.log(
|
|
2542
|
+
reporter.log(sprintf(" docker (%s) (%s)", hook.last_response.code, hook.events.join(", ")));
|
|
2600
2543
|
break;
|
|
2601
2544
|
default:
|
|
2602
2545
|
reporter.log(` ${hook.name}: <unknown type>`);
|
|
@@ -2625,7 +2568,7 @@ async function setToken$1({ reporter, token, tokenProvider, }) {
|
|
|
2625
2568
|
reporter.info("Need API token to talk to GitHub");
|
|
2626
2569
|
reporter.info("https://github.com/settings/tokens/new?scopes=repo:status,read:repo_hook");
|
|
2627
2570
|
token = await new Promise((resolve, reject) => {
|
|
2628
|
-
|
|
2571
|
+
read({
|
|
2629
2572
|
prompt: "Enter new GitHub API token: ",
|
|
2630
2573
|
silent: true,
|
|
2631
2574
|
}, (err, answer) => {
|
|
@@ -2689,20 +2632,22 @@ var CloneType;
|
|
|
2689
2632
|
CloneType[CloneType["SSH"] = 1] = "SSH";
|
|
2690
2633
|
})(CloneType || (CloneType = {}));
|
|
2691
2634
|
class GitRepo {
|
|
2635
|
+
path;
|
|
2636
|
+
logCommand;
|
|
2692
2637
|
constructor(path, logCommand) {
|
|
2693
2638
|
this.path = path;
|
|
2694
2639
|
this.logCommand = logCommand;
|
|
2695
2640
|
}
|
|
2696
2641
|
async cloneGitHubRepo(org, name, cloneType) {
|
|
2697
|
-
const parent =
|
|
2698
|
-
if (!
|
|
2699
|
-
await
|
|
2642
|
+
const parent = path.dirname(this.path);
|
|
2643
|
+
if (!fs.existsSync(parent)) {
|
|
2644
|
+
await fs.promises.mkdir(parent, { recursive: true });
|
|
2700
2645
|
}
|
|
2701
2646
|
const cloneUrl = cloneType === CloneType.SSH
|
|
2702
2647
|
? `git@github.com:${org}/${name}.git`
|
|
2703
2648
|
: `https://github.com/${org}/${name}.git`;
|
|
2704
2649
|
try {
|
|
2705
|
-
const result = await
|
|
2650
|
+
const result = await execa("git", ["clone", cloneUrl, this.path], {
|
|
2706
2651
|
cwd: parent,
|
|
2707
2652
|
});
|
|
2708
2653
|
await this.logCommand(result);
|
|
@@ -2715,7 +2660,7 @@ class GitRepo {
|
|
|
2715
2660
|
}
|
|
2716
2661
|
async git(args) {
|
|
2717
2662
|
try {
|
|
2718
|
-
const result = await
|
|
2663
|
+
const result = await execa("git", args, {
|
|
2719
2664
|
cwd: this.path,
|
|
2720
2665
|
});
|
|
2721
2666
|
await this.logCommand(result);
|
|
@@ -2748,7 +2693,6 @@ class GitRepo {
|
|
|
2748
2693
|
return parseShortlogSummary(result.stdout);
|
|
2749
2694
|
}
|
|
2750
2695
|
async update() {
|
|
2751
|
-
var _a;
|
|
2752
2696
|
const fetchOnly = async () => {
|
|
2753
2697
|
await this.git(["fetch"]);
|
|
2754
2698
|
return {
|
|
@@ -2771,7 +2715,7 @@ class GitRepo {
|
|
|
2771
2715
|
return {
|
|
2772
2716
|
dirty: false,
|
|
2773
2717
|
updated: wasUpdated(result.stdout),
|
|
2774
|
-
updatedRange:
|
|
2718
|
+
updatedRange: getUpdateRange(result.stdout) ?? undefined,
|
|
2775
2719
|
};
|
|
2776
2720
|
}
|
|
2777
2721
|
}
|
|
@@ -2783,11 +2727,11 @@ const CALS_LOG = ".cals.log";
|
|
|
2783
2727
|
* backward slashes in paths.
|
|
2784
2728
|
*/
|
|
2785
2729
|
function getRelpath(it) {
|
|
2786
|
-
return
|
|
2730
|
+
return path.join(it.group, it.name);
|
|
2787
2731
|
}
|
|
2788
2732
|
async function appendFile(path, data) {
|
|
2789
2733
|
return new Promise((resolve, reject) => {
|
|
2790
|
-
|
|
2734
|
+
fs.appendFile(path, data, { encoding: "utf-8" }, (err) => {
|
|
2791
2735
|
if (err !== null) {
|
|
2792
2736
|
reject(err);
|
|
2793
2737
|
}
|
|
@@ -2796,15 +2740,14 @@ async function appendFile(path, data) {
|
|
|
2796
2740
|
});
|
|
2797
2741
|
}
|
|
2798
2742
|
function getAliases(repo) {
|
|
2799
|
-
|
|
2800
|
-
return ((_a = repo.previousNames) !== null && _a !== void 0 ? _a : []).map((it) => ({
|
|
2743
|
+
return (repo.previousNames ?? []).map((it) => ({
|
|
2801
2744
|
group: it.project,
|
|
2802
2745
|
name: it.name,
|
|
2803
2746
|
}));
|
|
2804
2747
|
}
|
|
2805
2748
|
async function updateReposInParallel(reporter, items) {
|
|
2806
2749
|
// Perform git operations in parallel, but limit how much.
|
|
2807
|
-
const semaphore =
|
|
2750
|
+
const semaphore = pLimit(30);
|
|
2808
2751
|
const promises = items.map((repo) => semaphore(async () => {
|
|
2809
2752
|
try {
|
|
2810
2753
|
return {
|
|
@@ -2848,7 +2791,7 @@ async function updateRepos(reporter, foundRepos) {
|
|
|
2848
2791
|
: undefined;
|
|
2849
2792
|
// We only focus on changes made by humans, which we make
|
|
2850
2793
|
// green while keeping the other changes gray.
|
|
2851
|
-
const repoNameFormat =
|
|
2794
|
+
const repoNameFormat = authors?.every(({ name }) => isBotAuthor(name))
|
|
2852
2795
|
? reporter.format.gray
|
|
2853
2796
|
: reporter.format.greenBright;
|
|
2854
2797
|
reporter.info(`Updated: ${repoNameFormat(repo.id)}`);
|
|
@@ -2865,8 +2808,8 @@ async function updateRepos(reporter, foundRepos) {
|
|
|
2865
2808
|
}
|
|
2866
2809
|
}
|
|
2867
2810
|
function guessDefinitionRepoName(rootdir, cals) {
|
|
2868
|
-
const p =
|
|
2869
|
-
const relativePath =
|
|
2811
|
+
const p = path.resolve(rootdir, cals.resourcesDefinition.path);
|
|
2812
|
+
const relativePath = path.relative(rootdir, p);
|
|
2870
2813
|
if (relativePath.slice(0, 1) == ".") {
|
|
2871
2814
|
return null;
|
|
2872
2815
|
}
|
|
@@ -2879,8 +2822,8 @@ function guessDefinitionRepoName(rootdir, cals) {
|
|
|
2879
2822
|
return parts[1];
|
|
2880
2823
|
}
|
|
2881
2824
|
async function getDefinition(rootdir, cals) {
|
|
2882
|
-
const p =
|
|
2883
|
-
if (!
|
|
2825
|
+
const p = path.resolve(rootdir, cals.resourcesDefinition.path);
|
|
2826
|
+
if (!fs.existsSync(p)) {
|
|
2884
2827
|
throw Error(`The file ${p} does not exist`);
|
|
2885
2828
|
}
|
|
2886
2829
|
return new DefinitionFile(p).getDefinition();
|
|
@@ -2889,9 +2832,9 @@ async function getDefinition(rootdir, cals) {
|
|
|
2889
2832
|
* Get directory names within a directory.
|
|
2890
2833
|
*/
|
|
2891
2834
|
function getDirNames(parent) {
|
|
2892
|
-
return (
|
|
2835
|
+
return (fs
|
|
2893
2836
|
.readdirSync(parent)
|
|
2894
|
-
.filter((it) =>
|
|
2837
|
+
.filter((it) => fs.statSync(path.join(parent, it)).isDirectory())
|
|
2895
2838
|
// Skip hidden folders
|
|
2896
2839
|
.filter((it) => !it.startsWith("."))
|
|
2897
2840
|
.sort((a, b) => a.localeCompare(b)));
|
|
@@ -2901,9 +2844,9 @@ async function getReposInOrg(cals, rootdir) {
|
|
|
2901
2844
|
return getRepos(definition)
|
|
2902
2845
|
.filter((it) => it.orgName === cals.githubOrganization)
|
|
2903
2846
|
.filter((it) => cals.resourcesDefinition.tags === undefined ||
|
|
2904
|
-
(it.project.tags || []).some((tag) =>
|
|
2847
|
+
(it.project.tags || []).some((tag) => cals.resourcesDefinition.tags?.includes(tag)) ||
|
|
2905
2848
|
// Always include if already checked out to avoid stale state.
|
|
2906
|
-
|
|
2849
|
+
fs.existsSync(path.join(rootdir, it.project.name, it.repo.name)));
|
|
2907
2850
|
}
|
|
2908
2851
|
function getExpectedRepo(item) {
|
|
2909
2852
|
return {
|
|
@@ -2916,8 +2859,8 @@ function getExpectedRepo(item) {
|
|
|
2916
2859
|
};
|
|
2917
2860
|
}
|
|
2918
2861
|
function getGitRepo(rootdir, relpath) {
|
|
2919
|
-
return new GitRepo(
|
|
2920
|
-
await appendFile(
|
|
2862
|
+
return new GitRepo(path.resolve(rootdir, relpath), async (result) => {
|
|
2863
|
+
await appendFile(path.resolve(rootdir, CALS_LOG), JSON.stringify({
|
|
2921
2864
|
time: new Date().toISOString(),
|
|
2922
2865
|
context: relpath,
|
|
2923
2866
|
type: "exec-result",
|
|
@@ -2971,7 +2914,7 @@ async function getExpectedRepos(reporter, github, cals, rootdir) {
|
|
|
2971
2914
|
}
|
|
2972
2915
|
async function getInput(prompt) {
|
|
2973
2916
|
return new Promise((resolve, reject) => {
|
|
2974
|
-
|
|
2917
|
+
read({
|
|
2975
2918
|
prompt,
|
|
2976
2919
|
timeout: 60000,
|
|
2977
2920
|
}, (err, answer) => {
|
|
@@ -3009,15 +2952,15 @@ async function sync$1({ reporter, github, cals, rootdir, askClone, askMove, }) {
|
|
|
3009
2952
|
const foundRepos = [];
|
|
3010
2953
|
// Categorize all dirs.
|
|
3011
2954
|
for (const topdir of getDirNames(rootdir)) {
|
|
3012
|
-
const isGitDir =
|
|
2955
|
+
const isGitDir = fs.existsSync(path.join(rootdir, topdir, ".git"));
|
|
3013
2956
|
if (isGitDir) {
|
|
3014
2957
|
// Do not traverse deeper inside another Git repo, as that might
|
|
3015
2958
|
// mean we do not have the proper grouped structure.
|
|
3016
2959
|
unknownDirs.push(topdir);
|
|
3017
2960
|
continue;
|
|
3018
2961
|
}
|
|
3019
|
-
for (const subdir of getDirNames(
|
|
3020
|
-
const p =
|
|
2962
|
+
for (const subdir of getDirNames(path.join(rootdir, topdir))) {
|
|
2963
|
+
const p = path.join(topdir, subdir);
|
|
3021
2964
|
const expectedRepo = expectedRepos.find((it) => getRelpath(it) === p ||
|
|
3022
2965
|
it.aliases.some((alias) => getRelpath(alias) === p));
|
|
3023
2966
|
if (expectedRepo === undefined) {
|
|
@@ -3045,9 +2988,9 @@ async function sync$1({ reporter, github, cals, rootdir, askClone, askMove, }) {
|
|
|
3045
2988
|
for (const it of archivedRepos) {
|
|
3046
2989
|
reporter.info(` ${it.actualRelpath}`);
|
|
3047
2990
|
}
|
|
3048
|
-
const thisDirName =
|
|
2991
|
+
const thisDirName = path.basename(process.cwd());
|
|
3049
2992
|
const archiveDir = `../${thisDirName}-archive`;
|
|
3050
|
-
const hasArchiveDir =
|
|
2993
|
+
const hasArchiveDir = fs.existsSync(archiveDir);
|
|
3051
2994
|
if (hasArchiveDir) {
|
|
3052
2995
|
reporter.info("To move these:");
|
|
3053
2996
|
for (const it of archivedRepos) {
|
|
@@ -3070,17 +3013,17 @@ async function sync$1({ reporter, github, cals, rootdir, askClone, askMove, }) {
|
|
|
3070
3013
|
const shouldMove = await askMoveConfirm();
|
|
3071
3014
|
if (shouldMove) {
|
|
3072
3015
|
for (const it of movedRepos) {
|
|
3073
|
-
const src =
|
|
3074
|
-
const dest =
|
|
3075
|
-
const destParent =
|
|
3076
|
-
if (
|
|
3016
|
+
const src = path.join(rootdir, it.actualRelpath);
|
|
3017
|
+
const dest = path.join(rootdir, getRelpath(it));
|
|
3018
|
+
const destParent = path.join(rootdir, it.group);
|
|
3019
|
+
if (fs.existsSync(dest)) {
|
|
3077
3020
|
throw new Error(`Target directory already exists: ${dest} - cannot move ${it.actualRelpath}`);
|
|
3078
3021
|
}
|
|
3079
3022
|
reporter.info(`Moving ${it.actualRelpath} -> ${getRelpath(it)}`);
|
|
3080
|
-
if (!
|
|
3081
|
-
await
|
|
3023
|
+
if (!fs.existsSync(destParent)) {
|
|
3024
|
+
await fs.promises.mkdir(destParent, { recursive: true });
|
|
3082
3025
|
}
|
|
3083
|
-
await
|
|
3026
|
+
await fs.promises.rename(src, dest);
|
|
3084
3027
|
}
|
|
3085
3028
|
// We would have to update expectedRepos if we want to continue.
|
|
3086
3029
|
// Let's try keeping this simple.
|
|
@@ -3113,7 +3056,7 @@ async function sync$1({ reporter, github, cals, rootdir, askClone, askMove, }) {
|
|
|
3113
3056
|
}
|
|
3114
3057
|
const reposToUpdate = foundRepos.filter((it) =>
|
|
3115
3058
|
// Avoid double-processing the defintion repo.
|
|
3116
|
-
|
|
3059
|
+
definitionRepo?.id !== it.id);
|
|
3117
3060
|
reporter.info(`${reposToUpdate.length} repos identified to be updated`);
|
|
3118
3061
|
await updateRepos(reporter, reposToUpdate);
|
|
3119
3062
|
// Report repos with changes ahead.
|
|
@@ -3124,7 +3067,7 @@ async function sync$1({ reporter, github, cals, rootdir, askClone, askMove, }) {
|
|
|
3124
3067
|
}
|
|
3125
3068
|
}
|
|
3126
3069
|
async function loadCalsManifest(config, reporter) {
|
|
3127
|
-
const p = await
|
|
3070
|
+
const p = await findUp(CALS_YAML, { cwd: config.cwd });
|
|
3128
3071
|
if (p === undefined) {
|
|
3129
3072
|
reporter.error(`File ${CALS_YAML} not found. See help`);
|
|
3130
3073
|
process.exitCode = 1;
|
|
@@ -3133,12 +3076,12 @@ async function loadCalsManifest(config, reporter) {
|
|
|
3133
3076
|
// TODO: Verify file has expected contents.
|
|
3134
3077
|
// (Can we easily generate schema for type and verify?)
|
|
3135
3078
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-explicit-any
|
|
3136
|
-
const cals =
|
|
3079
|
+
const cals = yaml.load(fs.readFileSync(p, "utf-8"));
|
|
3137
3080
|
if (cals.version !== 2) {
|
|
3138
3081
|
throw new Error(`Unexpected version in ${p}`);
|
|
3139
3082
|
}
|
|
3140
3083
|
return {
|
|
3141
|
-
dir:
|
|
3084
|
+
dir: path.dirname(p),
|
|
3142
3085
|
cals,
|
|
3143
3086
|
};
|
|
3144
3087
|
}
|
|
@@ -3243,23 +3186,21 @@ Notes:
|
|
|
3243
3186
|
option to avoid stale cache. The cache can also be cleared with
|
|
3244
3187
|
the "cals delete-cache" command.`),
|
|
3245
3188
|
handler: () => {
|
|
3246
|
-
|
|
3189
|
+
yargs(hideBin(process.argv)).showHelp();
|
|
3247
3190
|
},
|
|
3248
3191
|
};
|
|
3249
3192
|
|
|
3250
3193
|
function totalSeverityCount(project) {
|
|
3251
|
-
|
|
3252
|
-
return (((_a = project.issueCountsBySeverity.critical) !== null && _a !== void 0 ? _a : 0) +
|
|
3194
|
+
return ((project.issueCountsBySeverity.critical ?? 0) +
|
|
3253
3195
|
project.issueCountsBySeverity.high +
|
|
3254
3196
|
project.issueCountsBySeverity.medium +
|
|
3255
3197
|
project.issueCountsBySeverity.low);
|
|
3256
3198
|
}
|
|
3257
3199
|
function buildStatsLine(stats) {
|
|
3258
|
-
var _a;
|
|
3259
3200
|
function item(num, str) {
|
|
3260
|
-
return num === 0 ?
|
|
3201
|
+
return num === 0 ? repeat(" ", str.length + 4) : sprintf("%3d %s", num, str);
|
|
3261
3202
|
}
|
|
3262
|
-
return
|
|
3203
|
+
return sprintf("%s %s %s %s", item(stats.critical ?? 0, "critical"), item(stats.high, "high"), item(stats.medium, "medium"), item(stats.low, "low"));
|
|
3263
3204
|
}
|
|
3264
3205
|
async function report({ reporter, snyk, definitionFile, }) {
|
|
3265
3206
|
const definition = await definitionFile.getDefinition();
|
|
@@ -3277,30 +3218,30 @@ async function report({ reporter, snyk, definitionFile, }) {
|
|
|
3277
3218
|
function getProjectName(project) {
|
|
3278
3219
|
return project ? project.name : "unknown project";
|
|
3279
3220
|
}
|
|
3280
|
-
const byProjects =
|
|
3221
|
+
const byProjects = sortBy(Object.values(groupBy(enhancedRepos, (it) => it.project ? it.project.name : "unknown")), (it) => getProjectName(it[0].project));
|
|
3281
3222
|
if (byProjects.length === 0) {
|
|
3282
3223
|
reporter.info("No issues found");
|
|
3283
3224
|
}
|
|
3284
3225
|
else {
|
|
3285
|
-
reporter.info(
|
|
3286
|
-
critical:
|
|
3287
|
-
high:
|
|
3288
|
-
medium:
|
|
3289
|
-
low:
|
|
3226
|
+
reporter.info(sprintf("%-70s %s", "Total count", buildStatsLine({
|
|
3227
|
+
critical: sumBy(reposWithIssues, (it) => it.issueCountsBySeverity.critical ?? 0),
|
|
3228
|
+
high: sumBy(reposWithIssues, (it) => it.issueCountsBySeverity.high),
|
|
3229
|
+
medium: sumBy(reposWithIssues, (it) => it.issueCountsBySeverity.medium),
|
|
3230
|
+
low: sumBy(reposWithIssues, (it) => it.issueCountsBySeverity.low),
|
|
3290
3231
|
})));
|
|
3291
3232
|
reporter.info("Issues by project:");
|
|
3292
3233
|
byProjects.forEach((repos) => {
|
|
3293
3234
|
const project = repos[0].project;
|
|
3294
3235
|
const totalCount = {
|
|
3295
|
-
critical:
|
|
3296
|
-
high:
|
|
3297
|
-
medium:
|
|
3298
|
-
low:
|
|
3236
|
+
critical: sumBy(repos, (it) => it.repo.issueCountsBySeverity.critical ?? 0),
|
|
3237
|
+
high: sumBy(repos, (it) => it.repo.issueCountsBySeverity.high),
|
|
3238
|
+
medium: sumBy(repos, (it) => it.repo.issueCountsBySeverity.medium),
|
|
3239
|
+
low: sumBy(repos, (it) => it.repo.issueCountsBySeverity.low),
|
|
3299
3240
|
};
|
|
3300
3241
|
reporter.info("");
|
|
3301
|
-
reporter.info(
|
|
3242
|
+
reporter.info(sprintf("%-70s %s", getProjectName(project), buildStatsLine(totalCount)));
|
|
3302
3243
|
for (const { repo } of repos) {
|
|
3303
|
-
reporter.info(
|
|
3244
|
+
reporter.info(sprintf(" %-68s %s", repo.name, buildStatsLine(repo.issueCountsBySeverity)));
|
|
3304
3245
|
}
|
|
3305
3246
|
});
|
|
3306
3247
|
}
|
|
@@ -3321,7 +3262,7 @@ async function setToken({ reporter, token, tokenProvider, }) {
|
|
|
3321
3262
|
reporter.info("Need API token to talk to Snyk");
|
|
3322
3263
|
reporter.info("See https://app.snyk.io/account");
|
|
3323
3264
|
token = await new Promise((resolve, reject) => {
|
|
3324
|
-
|
|
3265
|
+
read({
|
|
3325
3266
|
prompt: "Enter new Snyk API token: ",
|
|
3326
3267
|
silent: true,
|
|
3327
3268
|
}, (err, answer) => {
|
|
@@ -3393,30 +3334,17 @@ Notes:
|
|
|
3393
3334
|
and provide a link to generate one:
|
|
3394
3335
|
$ cals snyk set-token`),
|
|
3395
3336
|
handler: () => {
|
|
3396
|
-
|
|
3337
|
+
yargs(hideBin(process.argv)).showHelp();
|
|
3397
3338
|
},
|
|
3398
3339
|
};
|
|
3399
3340
|
|
|
3400
3341
|
async function main() {
|
|
3401
|
-
if (!
|
|
3342
|
+
if (!semver.satisfies(process.version, engines.node)) {
|
|
3402
3343
|
console.error(`Required node version ${engines.node} not satisfied with current version ${process.version}.`);
|
|
3403
3344
|
process.exit(1);
|
|
3404
3345
|
}
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
_________ __ _____
|
|
3408
|
-
/ ____/ | / / / ___/
|
|
3409
|
-
/ / / /| | / / \\__ \\
|
|
3410
|
-
/ /___/ ___ |/ /______/ /
|
|
3411
|
-
\\____/_/ |_/_____/____/
|
|
3412
|
-
cli ${version}
|
|
3413
|
-
built ${"2024-12-05T12:13:34+0000"}
|
|
3414
|
-
|
|
3415
|
-
https://github.com/capralifecycle/cals-cli/
|
|
3416
|
-
|
|
3417
|
-
Usage: cals <command>`;
|
|
3418
|
-
await yargs__default["default"]
|
|
3419
|
-
.usage(header)
|
|
3346
|
+
await yargs(hideBin(process.argv))
|
|
3347
|
+
.usage(`cals-cli v${version} (build: ${"2024-12-11T13:09:43+0000"})`)
|
|
3420
3348
|
.scriptName("cals")
|
|
3421
3349
|
.locale("en")
|
|
3422
3350
|
.help("help")
|
|
@@ -3441,12 +3369,6 @@ Usage: cals <command>`;
|
|
|
3441
3369
|
})
|
|
3442
3370
|
.parse();
|
|
3443
3371
|
}
|
|
3444
|
-
// Definer prosjekt-navn
|
|
3445
|
-
// Definer ønskede miljøer
|
|
3446
|
-
// Baselines som skal brukse
|
|
3447
|
-
// Er bruker logget inn?
|
|
3448
|
-
// Lag repo og endre commit
|
|
3449
|
-
// Osv. osv. osv.
|
|
3450
3372
|
|
|
3451
3373
|
void main();
|
|
3452
|
-
//# sourceMappingURL=cals-cli.
|
|
3374
|
+
//# sourceMappingURL=cals-cli.mjs.map
|