@capraconsulting/cals-cli 2.25.22 → 2.25.24

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 (63) hide show
  1. package/lib/cache.d.ts +27 -27
  2. package/lib/cals-cli.d.ts +1 -1
  3. package/lib/cals-cli.js +2748 -2748
  4. package/lib/cli/commands/definition/dump-setup.d.ts +3 -3
  5. package/lib/cli/commands/definition/util.d.ts +6 -6
  6. package/lib/cli/commands/definition/util.test.d.ts +1 -1
  7. package/lib/cli/commands/definition/validate.d.ts +3 -3
  8. package/lib/cli/commands/definition.d.ts +3 -3
  9. package/lib/cli/commands/delete-cache.d.ts +3 -3
  10. package/lib/cli/commands/getting-started.d.ts +3 -3
  11. package/lib/cli/commands/github/analyze-directory.d.ts +3 -3
  12. package/lib/cli/commands/github/configure.d.ts +3 -3
  13. package/lib/cli/commands/github/generate-clone-commands.d.ts +3 -3
  14. package/lib/cli/commands/github/list-pull-requests-stats.d.ts +3 -3
  15. package/lib/cli/commands/github/list-repos.d.ts +3 -3
  16. package/lib/cli/commands/github/list-webhooks.d.ts +3 -3
  17. package/lib/cli/commands/github/set-token.d.ts +3 -3
  18. package/lib/cli/commands/github/sync.d.ts +9 -9
  19. package/lib/cli/commands/github/util.d.ts +3 -3
  20. package/lib/cli/commands/github.d.ts +3 -3
  21. package/lib/cli/commands/snyk/report.d.ts +3 -3
  22. package/lib/cli/commands/snyk/set-token.d.ts +3 -3
  23. package/lib/cli/commands/snyk/sync.d.ts +3 -3
  24. package/lib/cli/commands/snyk.d.ts +3 -3
  25. package/lib/cli/index.d.ts +1 -1
  26. package/lib/cli/index.test.d.ts +1 -1
  27. package/lib/cli/reporter.d.ts +27 -27
  28. package/lib/cli/util.d.ts +11 -11
  29. package/lib/config.d.ts +14 -14
  30. package/lib/definition/definition.d.ts +13 -13
  31. package/lib/definition/definition.test.d.ts +1 -1
  32. package/lib/definition/index.d.ts +2 -2
  33. package/lib/definition/types.d.ts +78 -78
  34. package/lib/git/GitRepo.d.ts +31 -31
  35. package/lib/git/util.d.ts +16 -16
  36. package/lib/git/util.test.d.ts +1 -1
  37. package/lib/github/changeset/changeset.d.ts +21 -21
  38. package/lib/github/changeset/execute.d.ts +10 -10
  39. package/lib/github/changeset/types.d.ts +93 -93
  40. package/lib/github/index.d.ts +2 -2
  41. package/lib/github/service.d.ts +91 -91
  42. package/lib/github/token.d.ts +11 -11
  43. package/lib/github/types.d.ts +85 -85
  44. package/lib/github/util.d.ts +8 -8
  45. package/lib/index.d.ts +14 -14
  46. package/lib/index.es.js +1519 -1519
  47. package/lib/index.js +1519 -1519
  48. package/lib/load-secrets/index.d.ts +2 -2
  49. package/lib/load-secrets/load-secrets.d.ts +7 -7
  50. package/lib/load-secrets/types.d.ts +22 -22
  51. package/lib/snyk/index.d.ts +3 -3
  52. package/lib/snyk/service.d.ts +21 -21
  53. package/lib/snyk/token.d.ts +11 -11
  54. package/lib/snyk/types.d.ts +27 -27
  55. package/lib/snyk/util.d.ts +3 -3
  56. package/lib/snyk/util.test.d.ts +1 -1
  57. package/lib/sonarcloud/index.d.ts +2 -2
  58. package/lib/sonarcloud/service.d.ts +33 -33
  59. package/lib/sonarcloud/token.d.ts +8 -8
  60. package/lib/testing/executor.d.ts +25 -25
  61. package/lib/testing/index.d.ts +2 -2
  62. package/lib/testing/lib.d.ts +64 -64
  63. package/package.json +7 -7
package/lib/index.es.js CHANGED
@@ -22,148 +22,148 @@ import execa from 'execa';
22
22
  import { performance } from 'perf_hooks';
23
23
  import { Transform } from 'stream';
24
24
 
25
- var version = "2.25.22";
25
+ var version = "2.25.24";
26
26
 
27
- class CacheProvider {
28
- constructor(config) {
29
- this.mustValidate = false;
30
- this.defaultCacheTime = 1800;
31
- this.config = config;
32
- }
33
- /**
34
- * Retrieve cache if existent, ignoring the time.
35
- *
36
- * The caller is responsible for handling proper validation,
37
- */
38
- retrieveJson(cachekey) {
39
- const cachefile = path.join(this.config.cacheDir, `${cachekey}.json`);
40
- if (!fs.existsSync(cachefile)) {
41
- return undefined;
42
- }
43
- const data = fs.readFileSync(cachefile, "utf-8");
44
- return {
45
- cacheTime: fs.statSync(cachefile).mtime.getTime(),
46
- data: (data === "undefined" ? undefined : JSON.parse(data)),
47
- };
48
- }
49
- /**
50
- * Save data to cache.
51
- */
52
- storeJson(cachekey, data) {
53
- const cachefile = path.join(this.config.cacheDir, `${cachekey}.json`);
54
- if (!fs.existsSync(this.config.cacheDir)) {
55
- fs.mkdirSync(this.config.cacheDir, { recursive: true });
56
- }
57
- fs.writeFileSync(cachefile, data === undefined ? "undefined" : JSON.stringify(data));
58
- }
59
- async json(cachekey, block, cachetime = this.defaultCacheTime) {
60
- const cacheItem = this.mustValidate
61
- ? undefined
62
- : this.retrieveJson(cachekey);
63
- const expire = new Date(new Date().getTime() - cachetime * 1000).getTime();
64
- if (cacheItem !== undefined && cacheItem.cacheTime > expire) {
65
- return cacheItem.data;
66
- }
67
- const result = await block();
68
- this.storeJson(cachekey, result);
69
- return result;
70
- }
71
- /**
72
- * Delete all cached data.
73
- */
74
- cleanup() {
75
- rimraf.sync(this.config.cacheDir);
76
- }
27
+ class CacheProvider {
28
+ constructor(config) {
29
+ this.mustValidate = false;
30
+ this.defaultCacheTime = 1800;
31
+ this.config = config;
32
+ }
33
+ /**
34
+ * Retrieve cache if existent, ignoring the time.
35
+ *
36
+ * The caller is responsible for handling proper validation,
37
+ */
38
+ retrieveJson(cachekey) {
39
+ const cachefile = path.join(this.config.cacheDir, `${cachekey}.json`);
40
+ if (!fs.existsSync(cachefile)) {
41
+ return undefined;
42
+ }
43
+ const data = fs.readFileSync(cachefile, "utf-8");
44
+ return {
45
+ cacheTime: fs.statSync(cachefile).mtime.getTime(),
46
+ data: (data === "undefined" ? undefined : JSON.parse(data)),
47
+ };
48
+ }
49
+ /**
50
+ * Save data to cache.
51
+ */
52
+ storeJson(cachekey, data) {
53
+ const cachefile = path.join(this.config.cacheDir, `${cachekey}.json`);
54
+ if (!fs.existsSync(this.config.cacheDir)) {
55
+ fs.mkdirSync(this.config.cacheDir, { recursive: true });
56
+ }
57
+ fs.writeFileSync(cachefile, data === undefined ? "undefined" : JSON.stringify(data));
58
+ }
59
+ async json(cachekey, block, cachetime = this.defaultCacheTime) {
60
+ const cacheItem = this.mustValidate
61
+ ? undefined
62
+ : this.retrieveJson(cachekey);
63
+ const expire = new Date(new Date().getTime() - cachetime * 1000).getTime();
64
+ if (cacheItem !== undefined && cacheItem.cacheTime > expire) {
65
+ return cacheItem.data;
66
+ }
67
+ const result = await block();
68
+ this.storeJson(cachekey, result);
69
+ return result;
70
+ }
71
+ /**
72
+ * Delete all cached data.
73
+ */
74
+ cleanup() {
75
+ rimraf.sync(this.config.cacheDir);
76
+ }
77
77
  }
78
78
 
79
- const CLEAR_WHOLE_LINE = 0;
80
- function clearLine(stdout) {
81
- readline.clearLine(stdout, CLEAR_WHOLE_LINE);
82
- readline.cursorTo(stdout, 0);
83
- }
84
- class Reporter {
85
- constructor(opts = {}) {
86
- this.stdout = process.stdout;
87
- this.stderr = process.stderr;
88
- this.stdin = process.stdin;
89
- this.isTTY = this.stdout.isTTY;
90
- this.format = chalk;
91
- this.startTime = Date.now();
92
- this.nonInteractive = !!opts.nonInteractive;
93
- this.isVerbose = !!opts.verbose;
94
- }
95
- error(msg) {
96
- clearLine(this.stderr);
97
- this.stderr.write(`${this.format.red("error")} ${msg}\n`);
98
- }
99
- log(msg) {
100
- clearLine(this.stdout);
101
- this.stdout.write(`${msg}\n`);
102
- }
103
- warn(msg) {
104
- clearLine(this.stderr);
105
- this.stderr.write(`${this.format.yellow("warning")} ${msg}\n`);
106
- }
107
- success(msg) {
108
- clearLine(this.stdout);
109
- this.stdout.write(`${this.format.green("success")} ${msg}\n`);
110
- }
111
- info(msg) {
112
- clearLine(this.stdout);
113
- this.stdout.write(`${this.format.blue("info")} ${msg}\n`);
114
- }
79
+ const CLEAR_WHOLE_LINE = 0;
80
+ function clearLine(stdout) {
81
+ readline.clearLine(stdout, CLEAR_WHOLE_LINE);
82
+ readline.cursorTo(stdout, 0);
83
+ }
84
+ class Reporter {
85
+ constructor(opts = {}) {
86
+ this.stdout = process.stdout;
87
+ this.stderr = process.stderr;
88
+ this.stdin = process.stdin;
89
+ this.isTTY = this.stdout.isTTY;
90
+ this.format = chalk;
91
+ this.startTime = Date.now();
92
+ this.nonInteractive = !!opts.nonInteractive;
93
+ this.isVerbose = !!opts.verbose;
94
+ }
95
+ error(msg) {
96
+ clearLine(this.stderr);
97
+ this.stderr.write(`${this.format.red("error")} ${msg}\n`);
98
+ }
99
+ log(msg) {
100
+ clearLine(this.stdout);
101
+ this.stdout.write(`${msg}\n`);
102
+ }
103
+ warn(msg) {
104
+ clearLine(this.stderr);
105
+ this.stderr.write(`${this.format.yellow("warning")} ${msg}\n`);
106
+ }
107
+ success(msg) {
108
+ clearLine(this.stdout);
109
+ this.stdout.write(`${this.format.green("success")} ${msg}\n`);
110
+ }
111
+ info(msg) {
112
+ clearLine(this.stdout);
113
+ this.stdout.write(`${this.format.blue("info")} ${msg}\n`);
114
+ }
115
115
  }
116
116
 
117
- class Config {
118
- constructor() {
119
- this.cwd = path.resolve(process.cwd());
120
- this.configFile = path.join(os.homedir(), ".cals-config.json");
121
- this.cacheDir = cachedir("cals-cli");
122
- this.agent = new https.Agent({
123
- keepAlive: true,
124
- });
125
- this.configCached = undefined;
126
- }
127
- get config() {
128
- const existingConfig = this.configCached;
129
- if (existingConfig !== undefined) {
130
- return existingConfig;
131
- }
132
- const config = this.readConfig();
133
- this.configCached = config;
134
- return config;
135
- }
136
- readConfig() {
137
- if (!fs.existsSync(this.configFile)) {
138
- return {};
139
- }
140
- try {
141
- // eslint-disable-next-line @typescript-eslint/no-unsafe-return
142
- return JSON.parse(fs.readFileSync(this.configFile, "utf-8"));
143
- }
144
- catch (e) {
145
- console.error("Failed", e);
146
- throw new Error("Failed to read config");
147
- }
148
- }
149
- getConfig(key) {
150
- return this.config[key];
151
- }
152
- requireConfig(key) {
153
- const result = this.config[key];
154
- if (result === undefined) {
155
- throw Error(`Configuration for ${key} missing. Add manually to ${this.configFile}`);
156
- }
157
- return result;
158
- }
159
- updateConfig(key, value) {
160
- const updatedConfig = {
161
- ...this.readConfig(),
162
- [key]: value, // undefined will remove
163
- };
164
- fs.writeFileSync(this.configFile, JSON.stringify(updatedConfig, null, " "));
165
- this.configCached = updatedConfig;
166
- }
117
+ class Config {
118
+ constructor() {
119
+ this.cwd = path.resolve(process.cwd());
120
+ this.configFile = path.join(os.homedir(), ".cals-config.json");
121
+ this.cacheDir = cachedir("cals-cli");
122
+ this.agent = new https.Agent({
123
+ keepAlive: true,
124
+ });
125
+ this.configCached = undefined;
126
+ }
127
+ get config() {
128
+ const existingConfig = this.configCached;
129
+ if (existingConfig !== undefined) {
130
+ return existingConfig;
131
+ }
132
+ const config = this.readConfig();
133
+ this.configCached = config;
134
+ return config;
135
+ }
136
+ readConfig() {
137
+ if (!fs.existsSync(this.configFile)) {
138
+ return {};
139
+ }
140
+ try {
141
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
142
+ return JSON.parse(fs.readFileSync(this.configFile, "utf-8"));
143
+ }
144
+ catch (e) {
145
+ console.error("Failed", e);
146
+ throw new Error("Failed to read config");
147
+ }
148
+ }
149
+ getConfig(key) {
150
+ return this.config[key];
151
+ }
152
+ requireConfig(key) {
153
+ const result = this.config[key];
154
+ if (result === undefined) {
155
+ throw Error(`Configuration for ${key} missing. Add manually to ${this.configFile}`);
156
+ }
157
+ return result;
158
+ }
159
+ updateConfig(key, value) {
160
+ const updatedConfig = {
161
+ ...this.readConfig(),
162
+ [key]: value, // undefined will remove
163
+ };
164
+ fs.writeFileSync(this.configFile, JSON.stringify(updatedConfig, null, " "));
165
+ this.configCached = updatedConfig;
166
+ }
167
167
  }
168
168
 
169
169
  var type = "object";
@@ -466,282 +466,282 @@ var schema = {
466
466
  $schema: $schema
467
467
  };
468
468
 
469
- function getTeamId(org, teamName) {
470
- return `${org}/${teamName}`;
471
- }
472
- function getRepoId(orgName, repoName) {
473
- return `${orgName}/${repoName}`;
474
- }
475
- function checkAgainstSchema(value) {
476
- var _a;
477
- const ajv = new AJV({ allErrors: true });
478
- const valid = ajv.validate(schema, value);
479
- return valid
480
- ? { definition: value }
481
- : { error: (_a = ajv.errorsText()) !== null && _a !== void 0 ? _a : "Unknown error" };
482
- }
483
- function requireValidDefinition(definition) {
484
- // Verify no duplicates in users and extract known logins.
485
- const loginList = definition.github.users.reduce((acc, user) => {
486
- if (acc.includes(user.login)) {
487
- throw new Error(`Duplicate login: ${user.login}`);
488
- }
489
- return [...acc, user.login];
490
- }, []);
491
- // Verify no duplicates in teams and extract team names.
492
- const teamIdList = definition.github.teams.reduce((acc, orgTeams) => {
493
- return orgTeams.teams.reduce((acc1, team) => {
494
- const id = getTeamId(orgTeams.organization, team.name);
495
- if (acc1.includes(id)) {
496
- throw new Error(`Duplicate team: ${id}`);
497
- }
498
- return [...acc1, id];
499
- }, acc);
500
- }, []);
501
- // Verify team members exists as users.
502
- definition.github.teams
503
- .map((it) => it.teams)
504
- .flat()
505
- .forEach((team) => {
506
- team.members.forEach((login) => {
507
- if (!loginList.includes(login)) {
508
- throw new Error(`Team member ${login} in team ${team.name} is not registered in user list`);
509
- }
510
- });
511
- });
512
- // Verify no duplicates in project names.
513
- definition.projects.reduce((acc, project) => {
514
- if (acc.includes(project.name)) {
515
- throw new Error(`Duplicate project: ${project.name}`);
516
- }
517
- return [...acc, project.name];
518
- }, []);
519
- definition.projects.forEach((project) => {
520
- project.github.forEach((org) => {
521
- (org.teams || []).forEach((team) => {
522
- const id = getTeamId(org.organization, team.name);
523
- if (!teamIdList.includes(id)) {
524
- throw new Error(`Project team ${id} in project ${project.name} is not registered in team list`);
525
- }
526
- });
527
- (org.repos || []).forEach((repo) => {
528
- (repo.teams || []).forEach((team) => {
529
- const id = getTeamId(org.organization, team.name);
530
- if (!teamIdList.includes(id)) {
531
- throw new Error(`Repo team ${id} for repo ${repo.name} in project ${project.name} is not registered in team list`);
532
- }
533
- });
534
- });
535
- });
536
- });
537
- // Verify no duplicates in repos.
538
- definition.projects
539
- .flatMap((project) => project.github
540
- .map((org) => (org.repos || []).map((repo) => getRepoId(org.organization, repo.name)))
541
- .flat())
542
- .reduce((acc, repoName) => {
543
- if (acc.includes(repoName)) {
544
- throw new Error(`Duplicate repo: ${repoName}`);
545
- }
546
- return [...acc, repoName];
547
- }, []);
548
- }
549
- class DefinitionFile {
550
- constructor(path) {
551
- this.path = path;
552
- }
553
- async getContents() {
554
- return new Promise((resolve, reject) => fs.readFile(this.path, "utf-8", (err, data) => {
555
- if (err)
556
- reject(err);
557
- else
558
- resolve(data);
559
- }));
560
- }
561
- async getDefinition() {
562
- return parseDefinition(await this.getContents());
563
- }
564
- }
565
- function parseDefinition(value) {
566
- const result = checkAgainstSchema(yaml.load(value));
567
- if ("error" in result) {
568
- throw new Error("Definition content invalid: " + result.error);
569
- }
570
- requireValidDefinition(result.definition);
571
- return result.definition;
572
- }
573
- function getRepos(definition) {
574
- return definition.projects.flatMap((project) => project.github
575
- .map((org) => (org.repos || []).map((repo) => ({
576
- id: getRepoId(org.organization, repo.name),
577
- orgName: org.organization,
578
- project,
579
- repo,
580
- })))
581
- .flat());
582
- }
583
- function getGitHubOrgs(definition) {
584
- return uniq(definition.projects.flatMap((project) => project.github.map((it) => it.organization)));
469
+ function getTeamId(org, teamName) {
470
+ return `${org}/${teamName}`;
471
+ }
472
+ function getRepoId(orgName, repoName) {
473
+ return `${orgName}/${repoName}`;
474
+ }
475
+ function checkAgainstSchema(value) {
476
+ var _a;
477
+ const ajv = new AJV({ allErrors: true });
478
+ const valid = ajv.validate(schema, value);
479
+ return valid
480
+ ? { definition: value }
481
+ : { error: (_a = ajv.errorsText()) !== null && _a !== void 0 ? _a : "Unknown error" };
482
+ }
483
+ function requireValidDefinition(definition) {
484
+ // Verify no duplicates in users and extract known logins.
485
+ const loginList = definition.github.users.reduce((acc, user) => {
486
+ if (acc.includes(user.login)) {
487
+ throw new Error(`Duplicate login: ${user.login}`);
488
+ }
489
+ return [...acc, user.login];
490
+ }, []);
491
+ // Verify no duplicates in teams and extract team names.
492
+ const teamIdList = definition.github.teams.reduce((acc, orgTeams) => {
493
+ return orgTeams.teams.reduce((acc1, team) => {
494
+ const id = getTeamId(orgTeams.organization, team.name);
495
+ if (acc1.includes(id)) {
496
+ throw new Error(`Duplicate team: ${id}`);
497
+ }
498
+ return [...acc1, id];
499
+ }, acc);
500
+ }, []);
501
+ // Verify team members exists as users.
502
+ definition.github.teams
503
+ .map((it) => it.teams)
504
+ .flat()
505
+ .forEach((team) => {
506
+ team.members.forEach((login) => {
507
+ if (!loginList.includes(login)) {
508
+ throw new Error(`Team member ${login} in team ${team.name} is not registered in user list`);
509
+ }
510
+ });
511
+ });
512
+ // Verify no duplicates in project names.
513
+ definition.projects.reduce((acc, project) => {
514
+ if (acc.includes(project.name)) {
515
+ throw new Error(`Duplicate project: ${project.name}`);
516
+ }
517
+ return [...acc, project.name];
518
+ }, []);
519
+ definition.projects.forEach((project) => {
520
+ project.github.forEach((org) => {
521
+ (org.teams || []).forEach((team) => {
522
+ const id = getTeamId(org.organization, team.name);
523
+ if (!teamIdList.includes(id)) {
524
+ throw new Error(`Project team ${id} in project ${project.name} is not registered in team list`);
525
+ }
526
+ });
527
+ (org.repos || []).forEach((repo) => {
528
+ (repo.teams || []).forEach((team) => {
529
+ const id = getTeamId(org.organization, team.name);
530
+ if (!teamIdList.includes(id)) {
531
+ throw new Error(`Repo team ${id} for repo ${repo.name} in project ${project.name} is not registered in team list`);
532
+ }
533
+ });
534
+ });
535
+ });
536
+ });
537
+ // Verify no duplicates in repos.
538
+ definition.projects
539
+ .flatMap((project) => project.github
540
+ .map((org) => (org.repos || []).map((repo) => getRepoId(org.organization, repo.name)))
541
+ .flat())
542
+ .reduce((acc, repoName) => {
543
+ if (acc.includes(repoName)) {
544
+ throw new Error(`Duplicate repo: ${repoName}`);
545
+ }
546
+ return [...acc, repoName];
547
+ }, []);
548
+ }
549
+ class DefinitionFile {
550
+ constructor(path) {
551
+ this.path = path;
552
+ }
553
+ async getContents() {
554
+ return new Promise((resolve, reject) => fs.readFile(this.path, "utf-8", (err, data) => {
555
+ if (err)
556
+ reject(err);
557
+ else
558
+ resolve(data);
559
+ }));
560
+ }
561
+ async getDefinition() {
562
+ return parseDefinition(await this.getContents());
563
+ }
564
+ }
565
+ function parseDefinition(value) {
566
+ const result = checkAgainstSchema(yaml.load(value));
567
+ if ("error" in result) {
568
+ throw new Error("Definition content invalid: " + result.error);
569
+ }
570
+ requireValidDefinition(result.definition);
571
+ return result.definition;
572
+ }
573
+ function getRepos(definition) {
574
+ return definition.projects.flatMap((project) => project.github
575
+ .map((org) => (org.repos || []).map((repo) => ({
576
+ id: getRepoId(org.organization, repo.name),
577
+ orgName: org.organization,
578
+ project,
579
+ repo,
580
+ })))
581
+ .flat());
582
+ }
583
+ function getGitHubOrgs(definition) {
584
+ return uniq(definition.projects.flatMap((project) => project.github.map((it) => it.organization)));
585
585
  }
586
586
 
587
- function createReporter(argv) {
588
- return new Reporter({
589
- verbose: !!argv.verbose,
590
- nonInteractive: !!argv.nonInteractive,
591
- });
587
+ function createReporter(argv) {
588
+ return new Reporter({
589
+ verbose: !!argv.verbose,
590
+ nonInteractive: !!argv.nonInteractive,
591
+ });
592
592
  }
593
593
 
594
- class GitHubTokenCliProvider {
595
- constructor() {
596
- this.keyringService = "cals";
597
- this.keyringAccount = "github-token";
598
- }
599
- async getToken() {
600
- if (process.env.CALS_GITHUB_TOKEN) {
601
- return process.env.CALS_GITHUB_TOKEN;
602
- }
603
- const result = await keytar.getPassword(this.keyringService, this.keyringAccount);
604
- if (result == null) {
605
- process.stderr.write("No token found. Register using `cals github set-token`\n");
606
- return undefined;
607
- }
608
- return result;
609
- }
610
- async markInvalid() {
611
- await keytar.deletePassword(this.keyringService, this.keyringAccount);
612
- }
613
- async setToken(value) {
614
- await keytar.setPassword(this.keyringService, this.keyringAccount, value);
615
- }
594
+ class GitHubTokenCliProvider {
595
+ constructor() {
596
+ this.keyringService = "cals";
597
+ this.keyringAccount = "github-token";
598
+ }
599
+ async getToken() {
600
+ if (process.env.CALS_GITHUB_TOKEN) {
601
+ return process.env.CALS_GITHUB_TOKEN;
602
+ }
603
+ const result = await keytar.getPassword(this.keyringService, this.keyringAccount);
604
+ if (result == null) {
605
+ process.stderr.write("No token found. Register using `cals github set-token`\n");
606
+ return undefined;
607
+ }
608
+ return result;
609
+ }
610
+ async markInvalid() {
611
+ await keytar.deletePassword(this.keyringService, this.keyringAccount);
612
+ }
613
+ async setToken(value) {
614
+ await keytar.setPassword(this.keyringService, this.keyringAccount, value);
615
+ }
616
616
  }
617
617
 
618
- async function undefinedForNotFound(value) {
619
- try {
620
- return await value;
621
- }
622
- catch (e) {
623
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
624
- if (e.name === "HttpError" && e.status === 404) {
625
- return undefined;
626
- }
627
- else {
628
- throw e;
629
- }
630
- }
618
+ async function undefinedForNotFound(value) {
619
+ try {
620
+ return await value;
621
+ }
622
+ catch (e) {
623
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
624
+ if (e.name === "HttpError" && e.status === 404) {
625
+ return undefined;
626
+ }
627
+ else {
628
+ throw e;
629
+ }
630
+ }
631
631
  }
632
632
 
633
- class GitHubService {
634
- constructor(props) {
635
- this._requestCount = 0;
636
- this.config = props.config;
637
- this.octokit = props.octokit;
638
- this.cache = props.cache;
639
- this.tokenProvider = props.tokenProvider;
640
- // Control concurrency to GitHub API at service level so we
641
- // can maximize concurrency all other places.
642
- this.semaphore = pLimit(6);
643
- this.octokit.hook.wrap("request", async (request, options) => {
644
- /* eslint-disable @typescript-eslint/no-unsafe-member-access */
645
- this._requestCount++;
646
- if (options.method !== "GET") {
647
- // eslint-disable-next-line @typescript-eslint/no-unsafe-return
648
- return this.semaphore(() => request(options));
649
- }
650
- // Try to cache ETag for GET requests to save on rate limiting.
651
- // Hits on ETag does not count towards rate limiting.
652
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
653
- const rest = {
654
- ...options,
655
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
656
- };
657
- delete rest.method;
658
- delete rest.baseUrl;
659
- delete rest.headers;
660
- delete rest.mediaType;
661
- delete rest.request;
662
- // Build a key that is used to identify this request.
663
- const key = Buffer.from(JSON.stringify(rest)).toString("base64");
664
- const cacheItem = this.cache.retrieveJson(key);
665
- if (cacheItem !== undefined) {
666
- // Copying doesn't work, seems we need to mutate this.
667
- options.headers["If-None-Match"] = cacheItem.data.etag;
668
- }
669
- const getResponse = async (allowRetry = true) => {
670
- try {
671
- // eslint-disable-next-line @typescript-eslint/no-unsafe-return
672
- return await request(options);
673
- }
674
- catch (e) {
675
- // Handle no change in ETag.
676
- if (e.status === 304) {
677
- return undefined;
678
- }
679
- // GitHub seems to throw a lot of 502 errors.
680
- // Let's give it a few seconds and retry one time.
681
- if (e.status === 502 && allowRetry) {
682
- await new Promise((resolve) => setTimeout(resolve, 2000));
683
- return await getResponse(false);
684
- }
685
- throw e;
686
- }
687
- };
688
- const response = await this.semaphore(async () => {
689
- return getResponse();
690
- });
691
- if (response === undefined) {
692
- // Undefined is returned for cached data.
693
- if (cacheItem === undefined) {
694
- throw new Error("Missing expected cache item");
695
- }
696
- // Use previous value.
697
- return cacheItem.data.data;
698
- }
699
- // New value. Store Etag.
700
- if (response.headers.etag) {
701
- this.cache.storeJson(key, {
702
- etag: response.headers.etag,
703
- data: response,
704
- });
705
- }
706
- return response;
707
- /* eslint-enable @typescript-eslint/no-unsafe-member-access */
708
- });
709
- }
710
- get requestCount() {
711
- return this._requestCount;
712
- }
713
- async runGraphqlQuery(query) {
714
- const token = await this.tokenProvider.getToken();
715
- if (token === undefined) {
716
- throw new Error("Missing token for GitHub");
717
- }
718
- const url = "https://api.github.com/graphql";
719
- const headers = {
720
- Authorization: `Bearer ${token}`,
721
- };
722
- const response = await this.semaphore(() => fetch(url, {
723
- method: "POST",
724
- headers,
725
- body: JSON.stringify({ query }),
726
- agent: this.config.agent,
727
- }));
728
- if (response.status === 401) {
729
- process.stderr.write("Unauthorized\n");
730
- await this.tokenProvider.markInvalid();
731
- }
732
- if (!response.ok) {
733
- throw new Error(`Response from GitHub not OK (${response.status}): ${JSON.stringify(response)}`);
734
- }
735
- const json = (await response.json());
736
- if (!!json.errors) {
737
- throw new Error(`Error from GitHub GraphQL API: ${JSON.stringify(json.errors)}`);
738
- }
739
- if (json.data == null) {
740
- throw new Error(`No data received from GitHub GraphQL API (unknown reason)`);
741
- }
742
- return json.data;
743
- }
744
- async getOrgRepoList({ org }) {
633
+ class GitHubService {
634
+ constructor(props) {
635
+ this._requestCount = 0;
636
+ this.config = props.config;
637
+ this.octokit = props.octokit;
638
+ this.cache = props.cache;
639
+ this.tokenProvider = props.tokenProvider;
640
+ // Control concurrency to GitHub API at service level so we
641
+ // can maximize concurrency all other places.
642
+ this.semaphore = pLimit(6);
643
+ this.octokit.hook.wrap("request", async (request, options) => {
644
+ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
645
+ this._requestCount++;
646
+ if (options.method !== "GET") {
647
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
648
+ return this.semaphore(() => request(options));
649
+ }
650
+ // Try to cache ETag for GET requests to save on rate limiting.
651
+ // Hits on ETag does not count towards rate limiting.
652
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
653
+ const rest = {
654
+ ...options,
655
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
656
+ };
657
+ delete rest.method;
658
+ delete rest.baseUrl;
659
+ delete rest.headers;
660
+ delete rest.mediaType;
661
+ delete rest.request;
662
+ // Build a key that is used to identify this request.
663
+ const key = Buffer.from(JSON.stringify(rest)).toString("base64");
664
+ const cacheItem = this.cache.retrieveJson(key);
665
+ if (cacheItem !== undefined) {
666
+ // Copying doesn't work, seems we need to mutate this.
667
+ options.headers["If-None-Match"] = cacheItem.data.etag;
668
+ }
669
+ const getResponse = async (allowRetry = true) => {
670
+ try {
671
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
672
+ return await request(options);
673
+ }
674
+ catch (e) {
675
+ // Handle no change in ETag.
676
+ if (e.status === 304) {
677
+ return undefined;
678
+ }
679
+ // GitHub seems to throw a lot of 502 errors.
680
+ // Let's give it a few seconds and retry one time.
681
+ if (e.status === 502 && allowRetry) {
682
+ await new Promise((resolve) => setTimeout(resolve, 2000));
683
+ return await getResponse(false);
684
+ }
685
+ throw e;
686
+ }
687
+ };
688
+ const response = await this.semaphore(async () => {
689
+ return getResponse();
690
+ });
691
+ if (response === undefined) {
692
+ // Undefined is returned for cached data.
693
+ if (cacheItem === undefined) {
694
+ throw new Error("Missing expected cache item");
695
+ }
696
+ // Use previous value.
697
+ return cacheItem.data.data;
698
+ }
699
+ // New value. Store Etag.
700
+ if (response.headers.etag) {
701
+ this.cache.storeJson(key, {
702
+ etag: response.headers.etag,
703
+ data: response,
704
+ });
705
+ }
706
+ return response;
707
+ /* eslint-enable @typescript-eslint/no-unsafe-member-access */
708
+ });
709
+ }
710
+ get requestCount() {
711
+ return this._requestCount;
712
+ }
713
+ async runGraphqlQuery(query) {
714
+ const token = await this.tokenProvider.getToken();
715
+ if (token === undefined) {
716
+ throw new Error("Missing token for GitHub");
717
+ }
718
+ const url = "https://api.github.com/graphql";
719
+ const headers = {
720
+ Authorization: `Bearer ${token}`,
721
+ };
722
+ const response = await this.semaphore(() => fetch(url, {
723
+ method: "POST",
724
+ headers,
725
+ body: JSON.stringify({ query }),
726
+ agent: this.config.agent,
727
+ }));
728
+ if (response.status === 401) {
729
+ process.stderr.write("Unauthorized\n");
730
+ await this.tokenProvider.markInvalid();
731
+ }
732
+ if (!response.ok) {
733
+ throw new Error(`Response from GitHub not OK (${response.status}): ${JSON.stringify(response)}`);
734
+ }
735
+ const json = (await response.json());
736
+ if (!!json.errors) {
737
+ throw new Error(`Error from GitHub GraphQL API: ${JSON.stringify(json.errors)}`);
738
+ }
739
+ if (json.data == null) {
740
+ throw new Error(`No data received from GitHub GraphQL API (unknown reason)`);
741
+ }
742
+ return json.data;
743
+ }
744
+ async getOrgRepoList({ org }) {
745
745
  const getQuery = (after) => `{
746
746
  organization(login: "${org}") {
747
747
  repositories(first: 100${after === null ? "" : `, after: "${after}"`}) {
@@ -774,144 +774,144 @@ class GitHubService {
774
774
  }
775
775
  }
776
776
  }
777
- }`;
778
- return this.cache.json(`repos-${org}`, async () => {
779
- const repos = [];
780
- let after = null;
781
- while (true) {
782
- const query = getQuery(after);
783
- const res = await this.runGraphqlQuery(query);
784
- if (res.organization == null) {
785
- throw new Error("Missing organization");
786
- }
787
- if (res.organization.repositories.nodes == null) {
788
- throw new Error("Missing organization nodes");
789
- }
790
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
791
- repos.push(...res.organization.repositories.nodes);
792
- if (!res.organization.repositories.pageInfo.hasNextPage) {
793
- break;
794
- }
795
- after = res.organization.repositories.pageInfo.endCursor;
796
- }
797
- return repos.sort((a, b) => a.name.localeCompare(b.name));
798
- });
799
- }
800
- async getOrgMembersList(org) {
801
- const options = this.octokit.orgs.listMembers.endpoint.merge({
802
- org,
803
- });
804
- return ((await undefinedForNotFound(this.octokit.paginate(options))) || []);
805
- }
806
- async getOrgMembersInvitedList(org) {
807
- const options = this.octokit.orgs.listPendingInvitations.endpoint.merge({
808
- org,
809
- });
810
- return ((await undefinedForNotFound(this.octokit.paginate(options))) || []);
811
- }
812
- async getOrgMembersListIncludingInvited(org) {
813
- return [
814
- ...(await this.getOrgMembersList(org)).map((it) => ({
815
- type: "member",
816
- login: it.login,
817
- data: it,
818
- })),
819
- ...(await this.getOrgMembersInvitedList(org)).map((it) => {
820
- var _a;
821
- return ({
822
- type: "invited",
823
- // TODO: Fix ?? case properly
824
- login: (_a = it.login) !== null && _a !== void 0 ? _a : "invalid",
825
- data: it,
826
- });
827
- }),
828
- ];
829
- }
830
- async getRepository(owner, repo) {
831
- return this.cache.json(`get-repository-${owner}-${repo}`, async () => {
832
- const response = await undefinedForNotFound(this.octokit.repos.get({
833
- owner,
834
- repo,
835
- }));
836
- return response === undefined ? undefined : response.data;
837
- });
838
- }
839
- async getRepositoryTeamsList(repo) {
840
- return this.cache.json(`repository-teams-list-${repo.id}`, async () => {
841
- const options = this.octokit.repos.listTeams.endpoint.merge({
842
- owner: repo.owner.login,
843
- repo: repo.name,
844
- });
845
- return ((await undefinedForNotFound(this.octokit.paginate(options))) || []);
846
- });
847
- }
848
- async getRepositoryHooks(owner, repo) {
849
- return this.cache.json(`repository-hooks-${owner}-${repo}`, async () => {
850
- const options = this.octokit.repos.listWebhooks.endpoint.merge({
851
- owner,
852
- repo,
853
- });
854
- return ((await undefinedForNotFound(this.octokit.paginate(options))) || []);
855
- });
856
- }
857
- async getOrg(org) {
858
- const orgResponse = await this.octokit.orgs.get({
859
- org,
860
- });
861
- return orgResponse.data;
862
- }
863
- async getTeamList(org) {
864
- return this.cache.json(`team-list-${org.login}`, async () => {
865
- const options = this.octokit.teams.list.endpoint.merge({
866
- org: org.login,
867
- });
868
- return (await this.octokit.paginate(options));
869
- });
870
- }
871
- async getTeamMemberList(org, team) {
872
- return this.cache.json(`team-member-list-${team.id}`, async () => {
873
- const options = this.octokit.teams.listMembersInOrg.endpoint.merge({
874
- org: org.login,
875
- team_slug: team.slug,
876
- });
877
- return (await this.octokit.paginate(options));
878
- });
879
- }
880
- async getTeamMemberInvitedList(org, team) {
881
- return this.cache.json(`team-member-invited-list-${team.id}`, async () => {
882
- const options = this.octokit.teams.listPendingInvitationsInOrg.endpoint.merge({
883
- org: org.login,
884
- team_slug: team.slug,
885
- });
886
- return (await this.octokit.paginate(options));
887
- });
888
- }
889
- async getTeamMemberListIncludingInvited(org, team) {
890
- return [
891
- ...(await this.getTeamMemberList(org, team)).map((it) => ({
892
- type: "member",
893
- login: it.login,
894
- data: it,
895
- })),
896
- ...(await this.getTeamMemberInvitedList(org, team)).map((it) => {
897
- var _a;
898
- return ({
899
- type: "invited",
900
- // TODO: Fix ?? case properly
901
- login: (_a = it.login) !== null && _a !== void 0 ? _a : "invalid",
902
- data: it,
903
- });
904
- }),
905
- ];
906
- }
907
- async getSearchedPullRequestList() {
908
- // NOTE: Changes to this must by synced with SearchedPullRequestListQueryResult.
777
+ }`;
778
+ return this.cache.json(`repos-${org}`, async () => {
779
+ const repos = [];
780
+ let after = null;
781
+ while (true) {
782
+ const query = getQuery(after);
783
+ const res = await this.runGraphqlQuery(query);
784
+ if (res.organization == null) {
785
+ throw new Error("Missing organization");
786
+ }
787
+ if (res.organization.repositories.nodes == null) {
788
+ throw new Error("Missing organization nodes");
789
+ }
790
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
791
+ repos.push(...res.organization.repositories.nodes);
792
+ if (!res.organization.repositories.pageInfo.hasNextPage) {
793
+ break;
794
+ }
795
+ after = res.organization.repositories.pageInfo.endCursor;
796
+ }
797
+ return repos.sort((a, b) => a.name.localeCompare(b.name));
798
+ });
799
+ }
800
+ async getOrgMembersList(org) {
801
+ const options = this.octokit.orgs.listMembers.endpoint.merge({
802
+ org,
803
+ });
804
+ return ((await undefinedForNotFound(this.octokit.paginate(options))) || []);
805
+ }
806
+ async getOrgMembersInvitedList(org) {
807
+ const options = this.octokit.orgs.listPendingInvitations.endpoint.merge({
808
+ org,
809
+ });
810
+ return ((await undefinedForNotFound(this.octokit.paginate(options))) || []);
811
+ }
812
+ async getOrgMembersListIncludingInvited(org) {
813
+ return [
814
+ ...(await this.getOrgMembersList(org)).map((it) => ({
815
+ type: "member",
816
+ login: it.login,
817
+ data: it,
818
+ })),
819
+ ...(await this.getOrgMembersInvitedList(org)).map((it) => {
820
+ var _a;
821
+ return ({
822
+ type: "invited",
823
+ // TODO: Fix ?? case properly
824
+ login: (_a = it.login) !== null && _a !== void 0 ? _a : "invalid",
825
+ data: it,
826
+ });
827
+ }),
828
+ ];
829
+ }
830
+ async getRepository(owner, repo) {
831
+ return this.cache.json(`get-repository-${owner}-${repo}`, async () => {
832
+ const response = await undefinedForNotFound(this.octokit.repos.get({
833
+ owner,
834
+ repo,
835
+ }));
836
+ return response === undefined ? undefined : response.data;
837
+ });
838
+ }
839
+ async getRepositoryTeamsList(repo) {
840
+ return this.cache.json(`repository-teams-list-${repo.id}`, async () => {
841
+ const options = this.octokit.repos.listTeams.endpoint.merge({
842
+ owner: repo.owner.login,
843
+ repo: repo.name,
844
+ });
845
+ return ((await undefinedForNotFound(this.octokit.paginate(options))) || []);
846
+ });
847
+ }
848
+ async getRepositoryHooks(owner, repo) {
849
+ return this.cache.json(`repository-hooks-${owner}-${repo}`, async () => {
850
+ const options = this.octokit.repos.listWebhooks.endpoint.merge({
851
+ owner,
852
+ repo,
853
+ });
854
+ return ((await undefinedForNotFound(this.octokit.paginate(options))) || []);
855
+ });
856
+ }
857
+ async getOrg(org) {
858
+ const orgResponse = await this.octokit.orgs.get({
859
+ org,
860
+ });
861
+ return orgResponse.data;
862
+ }
863
+ async getTeamList(org) {
864
+ return this.cache.json(`team-list-${org.login}`, async () => {
865
+ const options = this.octokit.teams.list.endpoint.merge({
866
+ org: org.login,
867
+ });
868
+ return (await this.octokit.paginate(options));
869
+ });
870
+ }
871
+ async getTeamMemberList(org, team) {
872
+ return this.cache.json(`team-member-list-${team.id}`, async () => {
873
+ const options = this.octokit.teams.listMembersInOrg.endpoint.merge({
874
+ org: org.login,
875
+ team_slug: team.slug,
876
+ });
877
+ return (await this.octokit.paginate(options));
878
+ });
879
+ }
880
+ async getTeamMemberInvitedList(org, team) {
881
+ return this.cache.json(`team-member-invited-list-${team.id}`, async () => {
882
+ const options = this.octokit.teams.listPendingInvitationsInOrg.endpoint.merge({
883
+ org: org.login,
884
+ team_slug: team.slug,
885
+ });
886
+ return (await this.octokit.paginate(options));
887
+ });
888
+ }
889
+ async getTeamMemberListIncludingInvited(org, team) {
890
+ return [
891
+ ...(await this.getTeamMemberList(org, team)).map((it) => ({
892
+ type: "member",
893
+ login: it.login,
894
+ data: it,
895
+ })),
896
+ ...(await this.getTeamMemberInvitedList(org, team)).map((it) => {
897
+ var _a;
898
+ return ({
899
+ type: "invited",
900
+ // TODO: Fix ?? case properly
901
+ login: (_a = it.login) !== null && _a !== void 0 ? _a : "invalid",
902
+ data: it,
903
+ });
904
+ }),
905
+ ];
906
+ }
907
+ async getSearchedPullRequestList() {
908
+ // NOTE: Changes to this must by synced with SearchedPullRequestListQueryResult.
909
909
  const getQuery = (after) => `{
910
910
  search(
911
911
  query: "is:open is:pr user:capralifecycle user:capraconsulting archived:false",
912
912
  type: ISSUE,
913
- first: 100${after === null
914
- ? ""
913
+ first: 100${after === null
914
+ ? ""
915
915
  : `,
916
916
  after: "${after}"`}
917
917
  ) {
@@ -950,51 +950,51 @@ class GitHubService {
950
950
  }
951
951
  }
952
952
  }
953
- }`;
954
- const pulls = [];
955
- let after = null;
956
- while (true) {
957
- const query = getQuery(after);
958
- const res = await this.runGraphqlQuery(query);
959
- pulls.push(...res.search.edges.map((it) => it.node));
960
- if (!res.search.pageInfo.hasNextPage) {
961
- break;
962
- }
963
- after = res.search.pageInfo.endCursor;
964
- }
965
- return pulls.sort((a, b) => a.createdAt.localeCompare(b.createdAt));
966
- }
967
- async getHasVulnerabilityAlertsEnabled(owner, repo) {
968
- try {
969
- const response = await this.octokit.repos.checkVulnerabilityAlerts({
970
- owner: owner,
971
- repo: repo,
972
- });
973
- if (response.status !== 204) {
974
- console.log(response);
975
- throw new Error("Unknown response - see previous log line");
976
- }
977
- return true;
978
- }
979
- catch (e) {
980
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
981
- if (e.status === 404) {
982
- return false;
983
- }
984
- throw e;
985
- }
986
- }
987
- async enableVulnerabilityAlerts(owner, repo) {
988
- await this.octokit.repos.enableVulnerabilityAlerts({
989
- owner: owner,
990
- repo: repo,
991
- });
992
- }
993
- /**
994
- * Get the vulnerability alerts for a repository.
995
- */
996
- async getVulnerabilityAlerts(owner, repo) {
997
- // NOTE: Changes to this must by synced with VulnerabilityAlertsQueryResult.
953
+ }`;
954
+ const pulls = [];
955
+ let after = null;
956
+ while (true) {
957
+ const query = getQuery(after);
958
+ const res = await this.runGraphqlQuery(query);
959
+ pulls.push(...res.search.edges.map((it) => it.node));
960
+ if (!res.search.pageInfo.hasNextPage) {
961
+ break;
962
+ }
963
+ after = res.search.pageInfo.endCursor;
964
+ }
965
+ return pulls.sort((a, b) => a.createdAt.localeCompare(b.createdAt));
966
+ }
967
+ async getHasVulnerabilityAlertsEnabled(owner, repo) {
968
+ try {
969
+ const response = await this.octokit.repos.checkVulnerabilityAlerts({
970
+ owner: owner,
971
+ repo: repo,
972
+ });
973
+ if (response.status !== 204) {
974
+ console.log(response);
975
+ throw new Error("Unknown response - see previous log line");
976
+ }
977
+ return true;
978
+ }
979
+ catch (e) {
980
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
981
+ if (e.status === 404) {
982
+ return false;
983
+ }
984
+ throw e;
985
+ }
986
+ }
987
+ async enableVulnerabilityAlerts(owner, repo) {
988
+ await this.octokit.repos.enableVulnerabilityAlerts({
989
+ owner: owner,
990
+ repo: repo,
991
+ });
992
+ }
993
+ /**
994
+ * Get the vulnerability alerts for a repository.
995
+ */
996
+ async getVulnerabilityAlerts(owner, repo) {
997
+ // NOTE: Changes to this must by synced with VulnerabilityAlertsQueryResult.
998
998
  const getQuery = (after) => `{
999
999
  repository(owner: "${owner}", name: "${repo}") {
1000
1000
  vulnerabilityAlerts(first: 100${after === null ? "" : `, after: "${after}"`}) {
@@ -1024,28 +1024,28 @@ class GitHubService {
1024
1024
  }
1025
1025
  }
1026
1026
  }
1027
- }`;
1028
- return this.cache.json(`vulnerability-alerts-${owner}-${repo}`, async () => {
1029
- var _a, _b, _c, _d, _e;
1030
- const result = [];
1031
- let after = null;
1032
- while (true) {
1033
- const query = getQuery(after);
1034
- const res = await this.runGraphqlQuery(query);
1035
- result.push(...((_c = (_b = (_a = res.repository) === null || _a === void 0 ? void 0 : _a.vulnerabilityAlerts.edges) === null || _b === void 0 ? void 0 : _b.map((it) => it.node)) !== null && _c !== void 0 ? _c : []));
1036
- if (!((_d = res.repository) === null || _d === void 0 ? void 0 : _d.vulnerabilityAlerts.pageInfo.hasNextPage)) {
1037
- break;
1038
- }
1039
- after = (_e = res.repository) === null || _e === void 0 ? void 0 : _e.vulnerabilityAlerts.pageInfo.endCursor;
1040
- }
1041
- return result;
1042
- });
1043
- }
1044
- /**
1045
- * Get the Renovate Dependency Dashboard issue.
1046
- */
1047
- async getRenovateDependencyDashboardIssue(owner, repo) {
1048
- // NOTE: Changes to this must by synced with RenovateDependencyDashboardIssueQueryResult.
1027
+ }`;
1028
+ return this.cache.json(`vulnerability-alerts-${owner}-${repo}`, async () => {
1029
+ var _a, _b, _c, _d, _e;
1030
+ const result = [];
1031
+ let after = null;
1032
+ while (true) {
1033
+ const query = getQuery(after);
1034
+ const res = await this.runGraphqlQuery(query);
1035
+ result.push(...((_c = (_b = (_a = res.repository) === null || _a === void 0 ? void 0 : _a.vulnerabilityAlerts.edges) === null || _b === void 0 ? void 0 : _b.map((it) => it.node)) !== null && _c !== void 0 ? _c : []));
1036
+ if (!((_d = res.repository) === null || _d === void 0 ? void 0 : _d.vulnerabilityAlerts.pageInfo.hasNextPage)) {
1037
+ break;
1038
+ }
1039
+ after = (_e = res.repository) === null || _e === void 0 ? void 0 : _e.vulnerabilityAlerts.pageInfo.endCursor;
1040
+ }
1041
+ return result;
1042
+ });
1043
+ }
1044
+ /**
1045
+ * Get the Renovate Dependency Dashboard issue.
1046
+ */
1047
+ async getRenovateDependencyDashboardIssue(owner, repo) {
1048
+ // NOTE: Changes to this must by synced with RenovateDependencyDashboardIssueQueryResult.
1049
1049
  const getQuery = (after) => `{
1050
1050
  repository(owner: "${owner}", name: "${repo}") {
1051
1051
  issues(
@@ -1076,55 +1076,55 @@ class GitHubService {
1076
1076
  }
1077
1077
  }
1078
1078
  }
1079
- }`;
1080
- const issues = await this.cache.json(`renovate-bot-issues-${owner}-${repo}`, async () => {
1081
- var _a, _b, _c, _d, _e;
1082
- const result = [];
1083
- let after = null;
1084
- while (true) {
1085
- const query = getQuery(after);
1086
- const res = await this.runGraphqlQuery(query);
1087
- const nodes = (_c = (_b = (_a = res.repository) === null || _a === void 0 ? void 0 : _a.issues.edges) === null || _b === void 0 ? void 0 : _b.map((it) => it.node)) !== null && _c !== void 0 ? _c : [];
1088
- result.push(...nodes
1089
- .filter((it) => it.title === "Dependency Dashboard")
1090
- .map((it) => {
1091
- var _a, _b, _c, _d, _e;
1092
- return ({
1093
- number: it.number,
1094
- body: it.body,
1095
- lastUpdatedByRenovate: (_e = (_d = (_c = (_b = (_a = it.userContentEdits) === null || _a === void 0 ? void 0 : _a.nodes) === null || _b === void 0 ? void 0 : _b.filter((it) => { var _a; return ((_a = it.editor) === null || _a === void 0 ? void 0 : _a.login) === "renovate"; })) === null || _c === void 0 ? void 0 : _c[0]) === null || _d === void 0 ? void 0 : _d.createdAt) !== null && _e !== void 0 ? _e : null,
1096
- });
1097
- }));
1098
- if (!((_d = res.repository) === null || _d === void 0 ? void 0 : _d.issues.pageInfo.hasNextPage)) {
1099
- break;
1100
- }
1101
- after = (_e = res.repository) === null || _e === void 0 ? void 0 : _e.issues.pageInfo.endCursor;
1102
- }
1103
- return result;
1104
- });
1105
- if (issues.length == 0) {
1106
- return undefined;
1107
- }
1108
- return issues[0];
1109
- }
1110
- }
1111
- async function createOctokit(config, tokenProvider) {
1112
- return new Octokit({
1113
- auth: await tokenProvider.getToken(),
1114
- request: {
1115
- agent: config.agent,
1116
- },
1117
- });
1118
- }
1119
- async function createGitHubService(props) {
1120
- var _a;
1121
- const tokenProvider = (_a = props.tokenProvider) !== null && _a !== void 0 ? _a : new GitHubTokenCliProvider();
1122
- return new GitHubService({
1123
- config: props.config,
1124
- octokit: await createOctokit(props.config, tokenProvider),
1125
- cache: props.cache,
1126
- tokenProvider,
1127
- });
1079
+ }`;
1080
+ const issues = await this.cache.json(`renovate-bot-issues-${owner}-${repo}`, async () => {
1081
+ var _a, _b, _c, _d, _e;
1082
+ const result = [];
1083
+ let after = null;
1084
+ while (true) {
1085
+ const query = getQuery(after);
1086
+ const res = await this.runGraphqlQuery(query);
1087
+ const nodes = (_c = (_b = (_a = res.repository) === null || _a === void 0 ? void 0 : _a.issues.edges) === null || _b === void 0 ? void 0 : _b.map((it) => it.node)) !== null && _c !== void 0 ? _c : [];
1088
+ result.push(...nodes
1089
+ .filter((it) => it.title === "Dependency Dashboard")
1090
+ .map((it) => {
1091
+ var _a, _b, _c, _d, _e;
1092
+ return ({
1093
+ number: it.number,
1094
+ body: it.body,
1095
+ lastUpdatedByRenovate: (_e = (_d = (_c = (_b = (_a = it.userContentEdits) === null || _a === void 0 ? void 0 : _a.nodes) === null || _b === void 0 ? void 0 : _b.filter((it) => { var _a; return ((_a = it.editor) === null || _a === void 0 ? void 0 : _a.login) === "renovate"; })) === null || _c === void 0 ? void 0 : _c[0]) === null || _d === void 0 ? void 0 : _d.createdAt) !== null && _e !== void 0 ? _e : null,
1096
+ });
1097
+ }));
1098
+ if (!((_d = res.repository) === null || _d === void 0 ? void 0 : _d.issues.pageInfo.hasNextPage)) {
1099
+ break;
1100
+ }
1101
+ after = (_e = res.repository) === null || _e === void 0 ? void 0 : _e.issues.pageInfo.endCursor;
1102
+ }
1103
+ return result;
1104
+ });
1105
+ if (issues.length == 0) {
1106
+ return undefined;
1107
+ }
1108
+ return issues[0];
1109
+ }
1110
+ }
1111
+ async function createOctokit(config, tokenProvider) {
1112
+ return new Octokit({
1113
+ auth: await tokenProvider.getToken(),
1114
+ request: {
1115
+ agent: config.agent,
1116
+ },
1117
+ });
1118
+ }
1119
+ async function createGitHubService(props) {
1120
+ var _a;
1121
+ const tokenProvider = (_a = props.tokenProvider) !== null && _a !== void 0 ? _a : new GitHubTokenCliProvider();
1122
+ return new GitHubService({
1123
+ config: props.config,
1124
+ octokit: await createOctokit(props.config, tokenProvider),
1125
+ cache: props.cache,
1126
+ tokenProvider,
1127
+ });
1128
1128
  }
1129
1129
 
1130
1130
  var index$4 = /*#__PURE__*/Object.freeze({
@@ -1142,275 +1142,275 @@ var index$3 = /*#__PURE__*/Object.freeze({
1142
1142
  GitHubService: GitHubService
1143
1143
  });
1144
1144
 
1145
- /* eslint-disable @typescript-eslint/no-non-null-assertion */
1146
- class LoadSecrets {
1147
- constructor(props) {
1148
- this.smClientForRegions = {};
1149
- this.stsClient = new STSClient({
1150
- region: "eu-west-1",
1151
- });
1152
- this.reporter = props.reporter;
1153
- this.silent = props.silent;
1154
- }
1155
- getSmClient(region) {
1156
- if (!this.smClientForRegions[region]) {
1157
- this.smClientForRegions[region] = new SecretsManagerClient({
1158
- region,
1159
- });
1160
- }
1161
- return this.smClientForRegions[region];
1162
- }
1163
- async getInput(options) {
1164
- return new Promise((resolve, reject) => {
1165
- read(options, (err, answer) => {
1166
- if (err) {
1167
- reject(err);
1168
- }
1169
- resolve(answer);
1170
- });
1171
- });
1172
- }
1173
- async getSecretDetails(client, secretId) {
1174
- try {
1175
- return await client.send(new DescribeSecretCommand({ SecretId: secretId }));
1176
- }
1177
- catch (e) {
1178
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
1179
- if (e.name && e.name === "ResourceNotFoundException") {
1180
- return null;
1181
- }
1182
- throw e;
1183
- }
1184
- }
1185
- async handleJsonUpdate(secret) {
1186
- this.reporter.log("The secret is of type JSON with these expected fields:");
1187
- for (const field of secret.fields) {
1188
- const key = typeof field === "string" ? field : field.key;
1189
- const desc = typeof field === "string"
1190
- ? ""
1191
- : field.description
1192
- ? ` (${field.description})`
1193
- : "";
1194
- this.reporter.log(` - ${key}${desc}`);
1195
- }
1196
- this.reporter.log("");
1197
- // TODO: Ability to specify full json value as one line.
1198
- const collectedValues = {};
1199
- for (const field of secret.fields) {
1200
- const key = typeof field === "string" ? field : field.key;
1201
- this.reporter.log(`Field: ${this.reporter.format.greenBright(key)}`);
1202
- if (typeof field !== "string" && field.example != null) {
1203
- this.reporter.log(`Example: ${this.reporter.format.magentaBright(field.example)}`);
1204
- }
1205
- const value = await this.getInput({
1206
- prompt: "Enter value (Ctrl+C to abort): ",
1207
- silent: this.silent,
1208
- });
1209
- collectedValues[key] = value;
1210
- this.reporter.log("");
1211
- }
1212
- return JSON.stringify(collectedValues, undefined, " ");
1213
- }
1214
- getFullName(secretGroup, secret) {
1215
- return `${secretGroup.namePrefix}${secret.name}`;
1216
- }
1217
- async syncTags(client, secret, tags) {
1218
- const keysToRemove = secret
1219
- .Tags.filter((existingTag) => !tags.some((it) => it.Key === existingTag.Key))
1220
- .map((it) => it.Key);
1221
- if (keysToRemove.length > 0) {
1222
- this.reporter.log(`Removing obsolete tags: ${keysToRemove.join(", ")}`);
1223
- await client.send(new UntagResourceCommand({
1224
- SecretId: secret.ARN,
1225
- TagKeys: keysToRemove,
1226
- }));
1227
- }
1228
- const tagsToUpdate = tags.filter((expectedTag) => {
1229
- const existing = secret.Tags.find((it) => it.Key === expectedTag.Key);
1230
- return existing == null || existing.Value != expectedTag.Value;
1231
- });
1232
- if (tagsToUpdate.length > 0) {
1233
- this.reporter.log(`Storing tags: ${tagsToUpdate.map((it) => it.Key).join(", ")}`);
1234
- await client.send(new TagResourceCommand({
1235
- SecretId: secret.ARN,
1236
- Tags: tagsToUpdate,
1237
- }));
1238
- }
1239
- }
1240
- async getSecretValue(client, secretId) {
1241
- const result = await client.send(new GetSecretValueCommand({
1242
- SecretId: secretId,
1243
- }));
1244
- if (result.SecretString == null) {
1245
- throw new Error("Missing SecretString (is it a binary?)");
1246
- }
1247
- return result.SecretString;
1248
- }
1249
- async handleUpdate(secretGroup, secret) {
1250
- const client = this.getSmClient(secretGroup.region);
1251
- const fullName = this.getFullName(secretGroup, secret);
1252
- const describeSecret = await this.getSecretDetails(client, fullName);
1253
- this.reporter.log(`Secret: ${this.reporter.format.greenBright(fullName)}`);
1254
- if (describeSecret == null) {
1255
- this.reporter.log("The secret does not already exist and will be created");
1256
- }
1257
- else {
1258
- this.reporter.log("Current value:");
1259
- this.reporter.log(this.reporter.format.yellowBright((await this.getSecretValue(client, fullName)).replace(/^/gm, " ")));
1260
- }
1261
- this.reporter.log("");
1262
- let secretValue;
1263
- if (secret.type === "json") {
1264
- try {
1265
- secretValue = await this.handleJsonUpdate(secret);
1266
- }
1267
- catch (e) {
1268
- if (e instanceof Error && e.message === "canceled") {
1269
- this.reporter.log("Aborted");
1270
- return;
1271
- }
1272
- throw e;
1273
- }
1274
- }
1275
- else {
1276
- throw new Error(`Unsupported type`);
1277
- }
1278
- this.reporter.log("Storing secret value:");
1279
- this.reporter.log(this.reporter.format.yellowBright(secretValue.replace(/^/gm, " ")));
1280
- const tags = [
1281
- {
1282
- Key: "Source",
1283
- Value: "load-secrets script",
1284
- },
1285
- ];
1286
- let arn;
1287
- let version;
1288
- if (describeSecret == null) {
1289
- const createResult = await client.send(new CreateSecretCommand({
1290
- Name: fullName,
1291
- Description: "Created by load-secrets",
1292
- SecretString: secretValue,
1293
- Tags: tags,
1294
- }));
1295
- if (createResult.VersionId == null) {
1296
- throw new Error("Expected versionId");
1297
- }
1298
- arn = createResult.ARN;
1299
- version = createResult.VersionId;
1300
- }
1301
- else {
1302
- if (describeSecret.DeletedDate != null) {
1303
- await client.send(new RestoreSecretCommand({
1304
- SecretId: fullName,
1305
- }));
1306
- }
1307
- const updateResult = await client.send(new PutSecretValueCommand({
1308
- SecretId: fullName,
1309
- SecretString: secretValue,
1310
- }));
1311
- if (updateResult.VersionId == null) {
1312
- throw new Error("Expected versionId");
1313
- }
1314
- await this.syncTags(client, describeSecret, tags);
1315
- arn = updateResult.ARN;
1316
- version = updateResult.VersionId;
1317
- }
1318
- this.reporter.log("");
1319
- this.reporter.log("Secret stored:");
1320
- this.reporter.log(`ARN: ${this.reporter.format.greenBright(arn)}`);
1321
- this.reporter.log(`Version: ${this.reporter.format.greenBright(version)}`);
1322
- }
1323
- checkSecretGroup(secretGroup) {
1324
- if (!secretGroup.namePrefix.startsWith("/") ||
1325
- !secretGroup.namePrefix.endsWith("/")) {
1326
- throw new Error(`namePrefix should start and end with /. Current value: ${secretGroup.namePrefix}`);
1327
- }
1328
- }
1329
- getSecretDescription(details) {
1330
- var _a, _b;
1331
- return details == null
1332
- ? "not yet created"
1333
- : (details === null || details === void 0 ? void 0 : details.DeletedDate) != null
1334
- ? `scheduled for deletion ${details.DeletedDate.toISOString()}`
1335
- : `last changed ${(_b = (_a = details.LastChangedDate) === null || _a === void 0 ? void 0 : _a.toISOString()) !== null && _b !== void 0 ? _b : "unknown"}`;
1336
- }
1337
- /**
1338
- * Returns false if aborted.
1339
- */
1340
- async selectAndUpdate(secretGroups) {
1341
- const secrets = [];
1342
- this.reporter.log("Select secret to write:");
1343
- this.reporter.log("");
1344
- for (const secretGroup of secretGroups) {
1345
- this.reporter.log(`${secretGroup.description} (prefix: ${secretGroup.namePrefix})`);
1346
- for (let i = 0; i < secretGroup.secrets.length; i++) {
1347
- const offset = secrets.length;
1348
- const secret = secretGroup.secrets[i];
1349
- secrets.push({
1350
- secret: secret,
1351
- secretGroup,
1352
- });
1353
- const client = this.getSmClient(secretGroup.region);
1354
- const details = await this.getSecretDetails(client, this.getFullName(secretGroup, secret));
1355
- const desc = this.getSecretDescription(details);
1356
- this.reporter.log(` (${offset}) ${secret.name} (${desc})`);
1357
- }
1358
- this.reporter.log("");
1359
- }
1360
- let index;
1361
- try {
1362
- const answer = await this.getInput({
1363
- prompt: "Enter index (or enter to quit): ",
1364
- });
1365
- if (answer.trim() === "") {
1366
- return false;
1367
- }
1368
- index = parseInt(answer);
1369
- if (!secrets[index]) {
1370
- throw new Error();
1371
- }
1372
- }
1373
- catch (e) {
1374
- this.reporter.warn("Secret not found - aborting");
1375
- return false;
1376
- }
1377
- this.reporter.log("");
1378
- await this.handleUpdate(secrets[index].secretGroup, secrets[index].secret);
1379
- this.reporter.log("");
1380
- return true;
1381
- }
1382
- async process(secretGroups) {
1383
- this.reporter.info("Checking account for current credentials");
1384
- this.reporter.info("If any error is given, make sure you have valid credentials active");
1385
- const currentAccount = await this.stsClient.send(new GetCallerIdentityCommand({}));
1386
- this.reporter.info(`Running for account ${currentAccount.Account}`);
1387
- this.reporter.log("");
1388
- const matchedSecretGroups = secretGroups.filter((it) => it.accountId === currentAccount.Account);
1389
- if (matchedSecretGroups.length === 0) {
1390
- this.reporter.error(`No secrets specified for this account - aborting`);
1391
- return;
1392
- }
1393
- for (const secretGroup of matchedSecretGroups) {
1394
- this.checkSecretGroup(secretGroup);
1395
- }
1396
- while (await this.selectAndUpdate(matchedSecretGroups)) { }
1397
- }
1398
- }
1399
- /**
1400
- * Load secrets interactively into Secrets Manager.
1401
- */
1402
- function loadSecretsCli(props) {
1403
- const loadSecrets = new LoadSecrets({
1404
- reporter: createReporter({}),
1405
- // For now we show the secrets, so that we get positive feedback that the value
1406
- // is correctly entered.
1407
- silent: false,
1408
- });
1409
- loadSecrets.process(props.secretGroups).catch((error) => {
1410
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
1411
- console.error(error.stack || error.message || error);
1412
- process.exitCode = 1;
1413
- });
1145
+ /* eslint-disable @typescript-eslint/no-non-null-assertion */
1146
+ class LoadSecrets {
1147
+ constructor(props) {
1148
+ this.smClientForRegions = {};
1149
+ this.stsClient = new STSClient({
1150
+ region: "eu-west-1",
1151
+ });
1152
+ this.reporter = props.reporter;
1153
+ this.silent = props.silent;
1154
+ }
1155
+ getSmClient(region) {
1156
+ if (!this.smClientForRegions[region]) {
1157
+ this.smClientForRegions[region] = new SecretsManagerClient({
1158
+ region,
1159
+ });
1160
+ }
1161
+ return this.smClientForRegions[region];
1162
+ }
1163
+ async getInput(options) {
1164
+ return new Promise((resolve, reject) => {
1165
+ read(options, (err, answer) => {
1166
+ if (err) {
1167
+ reject(err);
1168
+ }
1169
+ resolve(answer);
1170
+ });
1171
+ });
1172
+ }
1173
+ async getSecretDetails(client, secretId) {
1174
+ try {
1175
+ return await client.send(new DescribeSecretCommand({ SecretId: secretId }));
1176
+ }
1177
+ catch (e) {
1178
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
1179
+ if (e.name && e.name === "ResourceNotFoundException") {
1180
+ return null;
1181
+ }
1182
+ throw e;
1183
+ }
1184
+ }
1185
+ async handleJsonUpdate(secret) {
1186
+ this.reporter.log("The secret is of type JSON with these expected fields:");
1187
+ for (const field of secret.fields) {
1188
+ const key = typeof field === "string" ? field : field.key;
1189
+ const desc = typeof field === "string"
1190
+ ? ""
1191
+ : field.description
1192
+ ? ` (${field.description})`
1193
+ : "";
1194
+ this.reporter.log(` - ${key}${desc}`);
1195
+ }
1196
+ this.reporter.log("");
1197
+ // TODO: Ability to specify full json value as one line.
1198
+ const collectedValues = {};
1199
+ for (const field of secret.fields) {
1200
+ const key = typeof field === "string" ? field : field.key;
1201
+ this.reporter.log(`Field: ${this.reporter.format.greenBright(key)}`);
1202
+ if (typeof field !== "string" && field.example != null) {
1203
+ this.reporter.log(`Example: ${this.reporter.format.magentaBright(field.example)}`);
1204
+ }
1205
+ const value = await this.getInput({
1206
+ prompt: "Enter value (Ctrl+C to abort): ",
1207
+ silent: this.silent,
1208
+ });
1209
+ collectedValues[key] = value;
1210
+ this.reporter.log("");
1211
+ }
1212
+ return JSON.stringify(collectedValues, undefined, " ");
1213
+ }
1214
+ getFullName(secretGroup, secret) {
1215
+ return `${secretGroup.namePrefix}${secret.name}`;
1216
+ }
1217
+ async syncTags(client, secret, tags) {
1218
+ const keysToRemove = secret
1219
+ .Tags.filter((existingTag) => !tags.some((it) => it.Key === existingTag.Key))
1220
+ .map((it) => it.Key);
1221
+ if (keysToRemove.length > 0) {
1222
+ this.reporter.log(`Removing obsolete tags: ${keysToRemove.join(", ")}`);
1223
+ await client.send(new UntagResourceCommand({
1224
+ SecretId: secret.ARN,
1225
+ TagKeys: keysToRemove,
1226
+ }));
1227
+ }
1228
+ const tagsToUpdate = tags.filter((expectedTag) => {
1229
+ const existing = secret.Tags.find((it) => it.Key === expectedTag.Key);
1230
+ return existing == null || existing.Value != expectedTag.Value;
1231
+ });
1232
+ if (tagsToUpdate.length > 0) {
1233
+ this.reporter.log(`Storing tags: ${tagsToUpdate.map((it) => it.Key).join(", ")}`);
1234
+ await client.send(new TagResourceCommand({
1235
+ SecretId: secret.ARN,
1236
+ Tags: tagsToUpdate,
1237
+ }));
1238
+ }
1239
+ }
1240
+ async getSecretValue(client, secretId) {
1241
+ const result = await client.send(new GetSecretValueCommand({
1242
+ SecretId: secretId,
1243
+ }));
1244
+ if (result.SecretString == null) {
1245
+ throw new Error("Missing SecretString (is it a binary?)");
1246
+ }
1247
+ return result.SecretString;
1248
+ }
1249
+ async handleUpdate(secretGroup, secret) {
1250
+ const client = this.getSmClient(secretGroup.region);
1251
+ const fullName = this.getFullName(secretGroup, secret);
1252
+ const describeSecret = await this.getSecretDetails(client, fullName);
1253
+ this.reporter.log(`Secret: ${this.reporter.format.greenBright(fullName)}`);
1254
+ if (describeSecret == null) {
1255
+ this.reporter.log("The secret does not already exist and will be created");
1256
+ }
1257
+ else {
1258
+ this.reporter.log("Current value:");
1259
+ this.reporter.log(this.reporter.format.yellowBright((await this.getSecretValue(client, fullName)).replace(/^/gm, " ")));
1260
+ }
1261
+ this.reporter.log("");
1262
+ let secretValue;
1263
+ if (secret.type === "json") {
1264
+ try {
1265
+ secretValue = await this.handleJsonUpdate(secret);
1266
+ }
1267
+ catch (e) {
1268
+ if (e instanceof Error && e.message === "canceled") {
1269
+ this.reporter.log("Aborted");
1270
+ return;
1271
+ }
1272
+ throw e;
1273
+ }
1274
+ }
1275
+ else {
1276
+ throw new Error(`Unsupported type`);
1277
+ }
1278
+ this.reporter.log("Storing secret value:");
1279
+ this.reporter.log(this.reporter.format.yellowBright(secretValue.replace(/^/gm, " ")));
1280
+ const tags = [
1281
+ {
1282
+ Key: "Source",
1283
+ Value: "load-secrets script",
1284
+ },
1285
+ ];
1286
+ let arn;
1287
+ let version;
1288
+ if (describeSecret == null) {
1289
+ const createResult = await client.send(new CreateSecretCommand({
1290
+ Name: fullName,
1291
+ Description: "Created by load-secrets",
1292
+ SecretString: secretValue,
1293
+ Tags: tags,
1294
+ }));
1295
+ if (createResult.VersionId == null) {
1296
+ throw new Error("Expected versionId");
1297
+ }
1298
+ arn = createResult.ARN;
1299
+ version = createResult.VersionId;
1300
+ }
1301
+ else {
1302
+ if (describeSecret.DeletedDate != null) {
1303
+ await client.send(new RestoreSecretCommand({
1304
+ SecretId: fullName,
1305
+ }));
1306
+ }
1307
+ const updateResult = await client.send(new PutSecretValueCommand({
1308
+ SecretId: fullName,
1309
+ SecretString: secretValue,
1310
+ }));
1311
+ if (updateResult.VersionId == null) {
1312
+ throw new Error("Expected versionId");
1313
+ }
1314
+ await this.syncTags(client, describeSecret, tags);
1315
+ arn = updateResult.ARN;
1316
+ version = updateResult.VersionId;
1317
+ }
1318
+ this.reporter.log("");
1319
+ this.reporter.log("Secret stored:");
1320
+ this.reporter.log(`ARN: ${this.reporter.format.greenBright(arn)}`);
1321
+ this.reporter.log(`Version: ${this.reporter.format.greenBright(version)}`);
1322
+ }
1323
+ checkSecretGroup(secretGroup) {
1324
+ if (!secretGroup.namePrefix.startsWith("/") ||
1325
+ !secretGroup.namePrefix.endsWith("/")) {
1326
+ throw new Error(`namePrefix should start and end with /. Current value: ${secretGroup.namePrefix}`);
1327
+ }
1328
+ }
1329
+ getSecretDescription(details) {
1330
+ var _a, _b;
1331
+ return details == null
1332
+ ? "not yet created"
1333
+ : (details === null || details === void 0 ? void 0 : details.DeletedDate) != null
1334
+ ? `scheduled for deletion ${details.DeletedDate.toISOString()}`
1335
+ : `last changed ${(_b = (_a = details.LastChangedDate) === null || _a === void 0 ? void 0 : _a.toISOString()) !== null && _b !== void 0 ? _b : "unknown"}`;
1336
+ }
1337
+ /**
1338
+ * Returns false if aborted.
1339
+ */
1340
+ async selectAndUpdate(secretGroups) {
1341
+ const secrets = [];
1342
+ this.reporter.log("Select secret to write:");
1343
+ this.reporter.log("");
1344
+ for (const secretGroup of secretGroups) {
1345
+ this.reporter.log(`${secretGroup.description} (prefix: ${secretGroup.namePrefix})`);
1346
+ for (let i = 0; i < secretGroup.secrets.length; i++) {
1347
+ const offset = secrets.length;
1348
+ const secret = secretGroup.secrets[i];
1349
+ secrets.push({
1350
+ secret: secret,
1351
+ secretGroup,
1352
+ });
1353
+ const client = this.getSmClient(secretGroup.region);
1354
+ const details = await this.getSecretDetails(client, this.getFullName(secretGroup, secret));
1355
+ const desc = this.getSecretDescription(details);
1356
+ this.reporter.log(` (${offset}) ${secret.name} (${desc})`);
1357
+ }
1358
+ this.reporter.log("");
1359
+ }
1360
+ let index;
1361
+ try {
1362
+ const answer = await this.getInput({
1363
+ prompt: "Enter index (or enter to quit): ",
1364
+ });
1365
+ if (answer.trim() === "") {
1366
+ return false;
1367
+ }
1368
+ index = parseInt(answer);
1369
+ if (!secrets[index]) {
1370
+ throw new Error();
1371
+ }
1372
+ }
1373
+ catch (e) {
1374
+ this.reporter.warn("Secret not found - aborting");
1375
+ return false;
1376
+ }
1377
+ this.reporter.log("");
1378
+ await this.handleUpdate(secrets[index].secretGroup, secrets[index].secret);
1379
+ this.reporter.log("");
1380
+ return true;
1381
+ }
1382
+ async process(secretGroups) {
1383
+ this.reporter.info("Checking account for current credentials");
1384
+ this.reporter.info("If any error is given, make sure you have valid credentials active");
1385
+ const currentAccount = await this.stsClient.send(new GetCallerIdentityCommand({}));
1386
+ this.reporter.info(`Running for account ${currentAccount.Account}`);
1387
+ this.reporter.log("");
1388
+ const matchedSecretGroups = secretGroups.filter((it) => it.accountId === currentAccount.Account);
1389
+ if (matchedSecretGroups.length === 0) {
1390
+ this.reporter.error(`No secrets specified for this account - aborting`);
1391
+ return;
1392
+ }
1393
+ for (const secretGroup of matchedSecretGroups) {
1394
+ this.checkSecretGroup(secretGroup);
1395
+ }
1396
+ while (await this.selectAndUpdate(matchedSecretGroups)) { }
1397
+ }
1398
+ }
1399
+ /**
1400
+ * Load secrets interactively into Secrets Manager.
1401
+ */
1402
+ function loadSecretsCli(props) {
1403
+ const loadSecrets = new LoadSecrets({
1404
+ reporter: createReporter({}),
1405
+ // For now we show the secrets, so that we get positive feedback that the value
1406
+ // is correctly entered.
1407
+ silent: false,
1408
+ });
1409
+ loadSecrets.process(props.secretGroups).catch((error) => {
1410
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
1411
+ console.error(error.stack || error.message || error);
1412
+ process.exitCode = 1;
1413
+ });
1414
1414
  }
1415
1415
 
1416
1416
  var index$2 = /*#__PURE__*/Object.freeze({
@@ -1418,105 +1418,105 @@ var index$2 = /*#__PURE__*/Object.freeze({
1418
1418
  loadSecretsCli: loadSecretsCli
1419
1419
  });
1420
1420
 
1421
- class SnykTokenCliProvider {
1422
- constructor() {
1423
- this.keyringService = "cals";
1424
- this.keyringAccount = "snyk-token";
1425
- }
1426
- async getToken() {
1427
- if (process.env.CALS_SNYK_TOKEN) {
1428
- return process.env.CALS_SNYK_TOKEN;
1429
- }
1430
- const result = await keytar.getPassword(this.keyringService, this.keyringAccount);
1431
- if (result == null) {
1432
- process.stderr.write("No token found. Register using `cals snyk set-token`\n");
1433
- return undefined;
1434
- }
1435
- return result;
1436
- }
1437
- async markInvalid() {
1438
- await keytar.deletePassword(this.keyringService, this.keyringAccount);
1439
- }
1440
- async setToken(value) {
1441
- await keytar.setPassword(this.keyringService, this.keyringAccount, value);
1442
- }
1421
+ class SnykTokenCliProvider {
1422
+ constructor() {
1423
+ this.keyringService = "cals";
1424
+ this.keyringAccount = "snyk-token";
1425
+ }
1426
+ async getToken() {
1427
+ if (process.env.CALS_SNYK_TOKEN) {
1428
+ return process.env.CALS_SNYK_TOKEN;
1429
+ }
1430
+ const result = await keytar.getPassword(this.keyringService, this.keyringAccount);
1431
+ if (result == null) {
1432
+ process.stderr.write("No token found. Register using `cals snyk set-token`\n");
1433
+ return undefined;
1434
+ }
1435
+ return result;
1436
+ }
1437
+ async markInvalid() {
1438
+ await keytar.deletePassword(this.keyringService, this.keyringAccount);
1439
+ }
1440
+ async setToken(value) {
1441
+ await keytar.setPassword(this.keyringService, this.keyringAccount, value);
1442
+ }
1443
1443
  }
1444
1444
 
1445
- class SnykService {
1446
- constructor(props) {
1447
- this.config = props.config;
1448
- this.tokenProvider = props.tokenProvider;
1449
- }
1450
- async getProjects(definition) {
1451
- var _a;
1452
- const snykAccountId = (_a = definition.snyk) === null || _a === void 0 ? void 0 : _a.accountId;
1453
- if (snykAccountId === undefined) {
1454
- return [];
1455
- }
1456
- return this.getProjectsByAccountId(snykAccountId);
1457
- }
1458
- async getProjectsByAccountId(snykAccountId) {
1459
- const token = await this.tokenProvider.getToken();
1460
- if (token === undefined) {
1461
- throw new Error("Missing token for Snyk");
1462
- }
1463
- const response = await fetch(`https://snyk.io/api/v1/org/${encodeURIComponent(snykAccountId)}/projects`, {
1464
- method: "GET",
1465
- headers: {
1466
- Accept: "application/json",
1467
- Authorization: `token ${token}`,
1468
- },
1469
- agent: this.config.agent,
1470
- });
1471
- if (response.status === 401) {
1472
- process.stderr.write("Unauthorized - removing token\n");
1473
- await this.tokenProvider.markInvalid();
1474
- }
1475
- if (!response.ok) {
1476
- throw new Error(`Response from Snyk not OK (${response.status}): ${JSON.stringify(response)}`);
1477
- }
1478
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
1479
- return (await response.json()).projects;
1480
- }
1481
- }
1482
- function createSnykService(props) {
1483
- var _a;
1484
- return new SnykService({
1485
- config: props.config,
1486
- tokenProvider: (_a = props.tokenProvider) !== null && _a !== void 0 ? _a : new SnykTokenCliProvider(),
1487
- });
1445
+ class SnykService {
1446
+ constructor(props) {
1447
+ this.config = props.config;
1448
+ this.tokenProvider = props.tokenProvider;
1449
+ }
1450
+ async getProjects(definition) {
1451
+ var _a;
1452
+ const snykAccountId = (_a = definition.snyk) === null || _a === void 0 ? void 0 : _a.accountId;
1453
+ if (snykAccountId === undefined) {
1454
+ return [];
1455
+ }
1456
+ return this.getProjectsByAccountId(snykAccountId);
1457
+ }
1458
+ async getProjectsByAccountId(snykAccountId) {
1459
+ const token = await this.tokenProvider.getToken();
1460
+ if (token === undefined) {
1461
+ throw new Error("Missing token for Snyk");
1462
+ }
1463
+ const response = await fetch(`https://snyk.io/api/v1/org/${encodeURIComponent(snykAccountId)}/projects`, {
1464
+ method: "GET",
1465
+ headers: {
1466
+ Accept: "application/json",
1467
+ Authorization: `token ${token}`,
1468
+ },
1469
+ agent: this.config.agent,
1470
+ });
1471
+ if (response.status === 401) {
1472
+ process.stderr.write("Unauthorized - removing token\n");
1473
+ await this.tokenProvider.markInvalid();
1474
+ }
1475
+ if (!response.ok) {
1476
+ throw new Error(`Response from Snyk not OK (${response.status}): ${JSON.stringify(response)}`);
1477
+ }
1478
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
1479
+ return (await response.json()).projects;
1480
+ }
1481
+ }
1482
+ function createSnykService(props) {
1483
+ var _a;
1484
+ return new SnykService({
1485
+ config: props.config,
1486
+ tokenProvider: (_a = props.tokenProvider) !== null && _a !== void 0 ? _a : new SnykTokenCliProvider(),
1487
+ });
1488
1488
  }
1489
1489
 
1490
- function getGitHubRepo(snykProject) {
1491
- if (snykProject.origin === "github") {
1492
- const match = /^([^/]+)\/([^:]+)(:(.+))?$/.exec(snykProject.name);
1493
- if (match === null) {
1494
- throw Error(`Could not extract components from Snyk project name: ${snykProject.name} (id: ${snykProject.id})`);
1495
- }
1496
- return {
1497
- owner: match[1],
1498
- name: match[2],
1499
- };
1500
- }
1501
- else if (snykProject.origin === "cli" &&
1502
- snykProject.remoteRepoUrl != null) {
1503
- // The remoteRepoUrl can be overriden when using the CLI, so don't
1504
- // fail if we cannot extract the value.
1505
- const match = /github.com\/([^/]+)\/(.+)\.git$/.exec(snykProject.remoteRepoUrl);
1506
- if (match === null) {
1507
- return undefined;
1508
- }
1509
- return {
1510
- owner: match[1],
1511
- name: match[2],
1512
- };
1513
- }
1514
- else {
1515
- return undefined;
1516
- }
1517
- }
1518
- function getGitHubRepoId(repo) {
1519
- return repo ? `${repo.owner}/${repo.name}` : undefined;
1490
+ function getGitHubRepo(snykProject) {
1491
+ if (snykProject.origin === "github") {
1492
+ const match = /^([^/]+)\/([^:]+)(:(.+))?$/.exec(snykProject.name);
1493
+ if (match === null) {
1494
+ throw Error(`Could not extract components from Snyk project name: ${snykProject.name} (id: ${snykProject.id})`);
1495
+ }
1496
+ return {
1497
+ owner: match[1],
1498
+ name: match[2],
1499
+ };
1500
+ }
1501
+ else if (snykProject.origin === "cli" &&
1502
+ snykProject.remoteRepoUrl != null) {
1503
+ // The remoteRepoUrl can be overriden when using the CLI, so don't
1504
+ // fail if we cannot extract the value.
1505
+ const match = /github.com\/([^/]+)\/(.+)\.git$/.exec(snykProject.remoteRepoUrl);
1506
+ if (match === null) {
1507
+ return undefined;
1508
+ }
1509
+ return {
1510
+ owner: match[1],
1511
+ name: match[2],
1512
+ };
1513
+ }
1514
+ else {
1515
+ return undefined;
1516
+ }
1517
+ }
1518
+ function getGitHubRepoId(repo) {
1519
+ return repo ? `${repo.owner}/${repo.name}` : undefined;
1520
1520
  }
1521
1521
 
1522
1522
  var index$1 = /*#__PURE__*/Object.freeze({
@@ -1527,61 +1527,61 @@ var index$1 = /*#__PURE__*/Object.freeze({
1527
1527
  getGitHubRepoId: getGitHubRepoId
1528
1528
  });
1529
1529
 
1530
- class SonarCloudTokenCliProvider {
1531
- async getToken() {
1532
- if (process.env.CALS_SONARCLOUD_TOKEN) {
1533
- return Promise.resolve(process.env.CALS_SONARCLOUD_TOKEN);
1534
- }
1535
- process.stderr.write("No environmental variable found. Set variable `CALS_SONARCLOUD_TOKEN` to token value\n");
1536
- return undefined;
1537
- }
1538
- async markInvalid() {
1539
- await Promise.resolve();
1540
- }
1530
+ class SonarCloudTokenCliProvider {
1531
+ async getToken() {
1532
+ if (process.env.CALS_SONARCLOUD_TOKEN) {
1533
+ return Promise.resolve(process.env.CALS_SONARCLOUD_TOKEN);
1534
+ }
1535
+ process.stderr.write("No environmental variable found. Set variable `CALS_SONARCLOUD_TOKEN` to token value\n");
1536
+ return undefined;
1537
+ }
1538
+ async markInvalid() {
1539
+ await Promise.resolve();
1540
+ }
1541
1541
  }
1542
1542
 
1543
- class SonarCloudService {
1544
- constructor(props) {
1545
- this.config = props.config;
1546
- this.tokenProvider = props.tokenProvider;
1547
- }
1548
- /**
1549
- * Returns metrics for project with given key.
1550
- * ONLY test coverage metrics are returned as of now
1551
- */
1552
- async getMetricsByProjectKey(sonarCloudProjectKey) {
1553
- const token = await this.tokenProvider.getToken();
1554
- if (token === undefined) {
1555
- throw new Error("Missing token for SonarCloud");
1556
- }
1557
- const response = await fetch(`https://sonarcloud.io/api/measures/component?component=${encodeURIComponent(sonarCloudProjectKey)}&metricKeys=coverage`, {
1558
- method: "GET",
1559
- headers: {
1560
- Accept: "application/json",
1561
- Authorization: `Basic ${Buffer.from(token.concat(":"), "utf8").toString("base64")}`,
1562
- },
1563
- agent: this.config.agent,
1564
- });
1565
- if (response.status === 401) {
1566
- process.stderr.write("Unauthorized - removing token\n");
1567
- await this.tokenProvider.markInvalid();
1568
- }
1569
- if (response.status === 404) {
1570
- process.stderr.write("Project does not exist in SonarCloud\n");
1571
- return undefined;
1572
- }
1573
- if (!response.ok) {
1574
- throw new Error(`Response from SonarCloud not OK (${response.status}): ${JSON.stringify(response)}`);
1575
- }
1576
- return (await response.json());
1577
- }
1578
- }
1579
- function createSonarCloudService(props) {
1580
- var _a;
1581
- return new SonarCloudService({
1582
- config: props.config,
1583
- tokenProvider: (_a = props.tokenProvider) !== null && _a !== void 0 ? _a : new SonarCloudTokenCliProvider(),
1584
- });
1543
+ class SonarCloudService {
1544
+ constructor(props) {
1545
+ this.config = props.config;
1546
+ this.tokenProvider = props.tokenProvider;
1547
+ }
1548
+ /**
1549
+ * Returns metrics for project with given key.
1550
+ * ONLY test coverage metrics are returned as of now
1551
+ */
1552
+ async getMetricsByProjectKey(sonarCloudProjectKey) {
1553
+ const token = await this.tokenProvider.getToken();
1554
+ if (token === undefined) {
1555
+ throw new Error("Missing token for SonarCloud");
1556
+ }
1557
+ const response = await fetch(`https://sonarcloud.io/api/measures/component?component=${encodeURIComponent(sonarCloudProjectKey)}&metricKeys=coverage`, {
1558
+ method: "GET",
1559
+ headers: {
1560
+ Accept: "application/json",
1561
+ Authorization: `Basic ${Buffer.from(token.concat(":"), "utf8").toString("base64")}`,
1562
+ },
1563
+ agent: this.config.agent,
1564
+ });
1565
+ if (response.status === 401) {
1566
+ process.stderr.write("Unauthorized - removing token\n");
1567
+ await this.tokenProvider.markInvalid();
1568
+ }
1569
+ if (response.status === 404) {
1570
+ process.stderr.write("Project does not exist in SonarCloud\n");
1571
+ return undefined;
1572
+ }
1573
+ if (!response.ok) {
1574
+ throw new Error(`Response from SonarCloud not OK (${response.status}): ${JSON.stringify(response)}`);
1575
+ }
1576
+ return (await response.json());
1577
+ }
1578
+ }
1579
+ function createSonarCloudService(props) {
1580
+ var _a;
1581
+ return new SonarCloudService({
1582
+ config: props.config,
1583
+ tokenProvider: (_a = props.tokenProvider) !== null && _a !== void 0 ? _a : new SonarCloudTokenCliProvider(),
1584
+ });
1585
1585
  }
1586
1586
 
1587
1587
  var index = /*#__PURE__*/Object.freeze({
@@ -1591,455 +1591,455 @@ var index = /*#__PURE__*/Object.freeze({
1591
1591
  SonarCloudTokenCliProvider: SonarCloudTokenCliProvider
1592
1592
  });
1593
1593
 
1594
- class TestExecutor {
1595
- constructor() {
1596
- this.shutdown = false;
1597
- this.cleanupTask = null;
1598
- this.usingWithCleanupTasks = false;
1599
- this.tasks = [];
1600
- }
1601
- /**
1602
- * Check if we are currently in shutdown state due to user
1603
- * asking to abort (Ctrl+C).
1604
- */
1605
- checkCanContinue() {
1606
- if (this.shutdown) {
1607
- throw new Error("In shutdown mode - aborting");
1608
- }
1609
- }
1610
- async runTasks() {
1611
- console.warn("Running cleanup tasks");
1612
- while (true) {
1613
- // We must run tasks in reverse order due to dependencies.
1614
- // E.g. we cannot delete a Docker network before deleting
1615
- // the container using it.
1616
- const task = this.tasks.pop();
1617
- if (task === undefined) {
1618
- return;
1619
- }
1620
- try {
1621
- await task();
1622
- }
1623
- catch (error) {
1624
- console.error(error);
1625
- }
1626
- }
1627
- }
1628
- /**
1629
- * Register a task that will be run during cleanup phase.
1630
- */
1631
- registerCleanupTask(task) {
1632
- if (!this.usingWithCleanupTasks) {
1633
- throw new Error("registerCleanupTask run outside runWithCleanupTasks");
1634
- }
1635
- this.tasks.push(task);
1636
- this.checkCanContinue();
1637
- }
1638
- /**
1639
- * Run the code block while ensuring we can run cleanup tasks
1640
- * after the execution or if the process is interrupted.
1641
- *
1642
- * The main method of the program should be executed by using
1643
- * this method.
1644
- */
1645
- async runWithCleanupTasks(body) {
1646
- try {
1647
- strict.strictEqual(this.usingWithCleanupTasks, false);
1648
- this.usingWithCleanupTasks = true;
1649
- // We capture Ctrl+C so that we can perform cleanup task,
1650
- // since the cleanup tasks involve async code which is not
1651
- // supported during NodeJS normal exit handling.
1652
- //
1653
- // This will not abort the running tasks until after
1654
- // we have completed the cleanup tasks. The running tasks
1655
- // can stop earlier by calling checkCanContinue.
1656
- process.on("SIGINT", () => {
1657
- console.warn("Caught interrupt signal - forcing termination");
1658
- if (this.cleanupTask != null) {
1659
- return;
1660
- }
1661
- this.shutdown = true;
1662
- this.cleanupTask = this.runTasks().then(() => {
1663
- process.exit(1);
1664
- });
1665
- });
1666
- await body(this);
1667
- }
1668
- catch (error) {
1669
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
1670
- console.error(error.stack || error.message || error);
1671
- process.exitCode = 1;
1672
- }
1673
- finally {
1674
- console.log(`Reached finally block`);
1675
- this.usingWithCleanupTasks = false;
1676
- if (this.cleanupTask == null) {
1677
- this.cleanupTask = this.runTasks();
1678
- }
1679
- await this.cleanupTask;
1680
- }
1681
- }
1682
- }
1683
- function createTestExecutor() {
1684
- return new TestExecutor();
1594
+ class TestExecutor {
1595
+ constructor() {
1596
+ this.shutdown = false;
1597
+ this.cleanupTask = null;
1598
+ this.usingWithCleanupTasks = false;
1599
+ this.tasks = [];
1600
+ }
1601
+ /**
1602
+ * Check if we are currently in shutdown state due to user
1603
+ * asking to abort (Ctrl+C).
1604
+ */
1605
+ checkCanContinue() {
1606
+ if (this.shutdown) {
1607
+ throw new Error("In shutdown mode - aborting");
1608
+ }
1609
+ }
1610
+ async runTasks() {
1611
+ console.warn("Running cleanup tasks");
1612
+ while (true) {
1613
+ // We must run tasks in reverse order due to dependencies.
1614
+ // E.g. we cannot delete a Docker network before deleting
1615
+ // the container using it.
1616
+ const task = this.tasks.pop();
1617
+ if (task === undefined) {
1618
+ return;
1619
+ }
1620
+ try {
1621
+ await task();
1622
+ }
1623
+ catch (error) {
1624
+ console.error(error);
1625
+ }
1626
+ }
1627
+ }
1628
+ /**
1629
+ * Register a task that will be run during cleanup phase.
1630
+ */
1631
+ registerCleanupTask(task) {
1632
+ if (!this.usingWithCleanupTasks) {
1633
+ throw new Error("registerCleanupTask run outside runWithCleanupTasks");
1634
+ }
1635
+ this.tasks.push(task);
1636
+ this.checkCanContinue();
1637
+ }
1638
+ /**
1639
+ * Run the code block while ensuring we can run cleanup tasks
1640
+ * after the execution or if the process is interrupted.
1641
+ *
1642
+ * The main method of the program should be executed by using
1643
+ * this method.
1644
+ */
1645
+ async runWithCleanupTasks(body) {
1646
+ try {
1647
+ strict.strictEqual(this.usingWithCleanupTasks, false);
1648
+ this.usingWithCleanupTasks = true;
1649
+ // We capture Ctrl+C so that we can perform cleanup task,
1650
+ // since the cleanup tasks involve async code which is not
1651
+ // supported during NodeJS normal exit handling.
1652
+ //
1653
+ // This will not abort the running tasks until after
1654
+ // we have completed the cleanup tasks. The running tasks
1655
+ // can stop earlier by calling checkCanContinue.
1656
+ process.on("SIGINT", () => {
1657
+ console.warn("Caught interrupt signal - forcing termination");
1658
+ if (this.cleanupTask != null) {
1659
+ return;
1660
+ }
1661
+ this.shutdown = true;
1662
+ this.cleanupTask = this.runTasks().then(() => {
1663
+ process.exit(1);
1664
+ });
1665
+ });
1666
+ await body(this);
1667
+ }
1668
+ catch (error) {
1669
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
1670
+ console.error(error.stack || error.message || error);
1671
+ process.exitCode = 1;
1672
+ }
1673
+ finally {
1674
+ console.log(`Reached finally block`);
1675
+ this.usingWithCleanupTasks = false;
1676
+ if (this.cleanupTask == null) {
1677
+ this.cleanupTask = this.runTasks();
1678
+ }
1679
+ await this.cleanupTask;
1680
+ }
1681
+ }
1682
+ }
1683
+ function createTestExecutor() {
1684
+ return new TestExecutor();
1685
1685
  }
1686
1686
 
1687
- /**
1688
- * Generate a value that can be used as part of resource names.
1689
- *
1690
- * Gives a value formatted as "yyyymmdd-xxxxxx", e.g. "20200523-3f2c87".
1691
- */
1692
- function generateRunId() {
1693
- const low = parseInt("100000", 16);
1694
- const high = parseInt("ffffff", 16);
1695
- const range = high - low + 1;
1696
- const now = new Date();
1697
- return [
1698
- now.getUTCFullYear(),
1699
- (now.getUTCMonth() + 1).toString().padStart(2, "0"),
1700
- now.getUTCDate().toString().padStart(2, "0"),
1701
- "-",
1702
- (Math.floor(Math.random() * range) + low).toString(16),
1703
- ].join("");
1704
- }
1705
- /**
1706
- * Generate a name that can be used for a resource.
1707
- */
1708
- function generateName(extra) {
1709
- const s = extra === undefined ? "" : `-${extra}`;
1710
- return `autotest-${generateRunId()}${s}`;
1711
- }
1712
- /**
1713
- * Create a new Docker network.
1714
- */
1715
- async function createNetwork(executor) {
1716
- executor.checkCanContinue();
1717
- const networkName = generateName();
1718
- await execa("docker", ["network", "create", networkName]);
1719
- const lsRes = await execa("docker", [
1720
- "network",
1721
- "ls",
1722
- "-q",
1723
- "-f",
1724
- `name=${networkName}`,
1725
- ]);
1726
- const networkId = lsRes.stdout.trim();
1727
- console.log(`Network ${networkName} (${networkId}) created`);
1728
- executor.registerCleanupTask(async () => {
1729
- await execa("docker", ["network", "rm", networkId]);
1730
- console.log(`Network ${networkName} (${networkId}) deleted`);
1731
- });
1732
- return {
1733
- id: networkId,
1734
- };
1735
- }
1736
- /**
1737
- * Execute curl within the Docker network.
1738
- */
1739
- async function curl(executor, network, ...args) {
1740
- executor.checkCanContinue();
1741
- const result = await execa("docker", [
1742
- "run",
1743
- "-i",
1744
- "--rm",
1745
- "--network",
1746
- network.id,
1747
- "byrnedo/alpine-curl",
1748
- ...args,
1749
- ]);
1750
- return result.stdout;
1751
- }
1752
- /**
1753
- * Repeatedly check for a condition until timeout.
1754
- *
1755
- * The condition can throw an error without aborting the loop.
1756
- * To abort the condition must return false.
1757
- */
1758
- async function pollForCondition({ container, attempts, waitIntervalSec, condition, }) {
1759
- function log(value) {
1760
- console.log(`${container.name} (poll): ${value}`);
1761
- }
1762
- container.executor.checkCanContinue();
1763
- log(`Waiting for condition.. Checking ${attempts} times by ${waitIntervalSec} sec`);
1764
- const start = performance.now();
1765
- const duration = () => {
1766
- const end = performance.now();
1767
- return Math.round((end - start) / 1000);
1768
- };
1769
- for (let i = 0; i < attempts; i++) {
1770
- container.executor.checkCanContinue();
1771
- if (!(await isRunning(container.executor, container))) {
1772
- throw new Error(`Container ${container.name} not running as expected`);
1773
- }
1774
- try {
1775
- const result = await condition();
1776
- if (!result) {
1777
- break;
1778
- }
1779
- log(`Took ${duration()} seconds for condition`);
1780
- return;
1781
- }
1782
- catch (e) {
1783
- log("Still waiting...");
1784
- await new Promise((resolve) => setTimeout(resolve, waitIntervalSec * 1000));
1785
- }
1786
- }
1787
- throw new Error(`Failed to wait for container ${container.name}`);
1788
- }
1789
- async function waitForHttpOk({ container, url, attempts = 30, waitIntervalSec = 1, }) {
1790
- await pollForCondition({
1791
- container,
1792
- attempts,
1793
- waitIntervalSec,
1794
- condition: async () => {
1795
- await curl(container.executor, container.network, "-fsS", url);
1796
- return true;
1797
- },
1798
- });
1799
- }
1800
- async function waitForPostgresAvailable({ container, attempts = 30, waitIntervalSec = 1, username = "user", password = "password", dbname, }) {
1801
- await pollForCondition({
1802
- container,
1803
- attempts,
1804
- waitIntervalSec,
1805
- condition: async () => {
1806
- await execa("docker", [
1807
- "exec",
1808
- "-e",
1809
- `PGPASSWORD=${password}`,
1810
- container.name,
1811
- "psql",
1812
- "-h",
1813
- "localhost",
1814
- "-U",
1815
- username,
1816
- "-c",
1817
- "select 1",
1818
- dbname,
1819
- ]);
1820
- return true;
1821
- },
1822
- });
1823
- }
1824
- async function isRunning(executor, container) {
1825
- executor.checkCanContinue();
1826
- try {
1827
- await execa("docker", ["inspect", container.name]);
1828
- return true;
1829
- }
1830
- catch (e) {
1831
- return false;
1832
- }
1833
- }
1834
- /**
1835
- * A stream transform that injects a prefix into every line
1836
- * and also forces every chunk to end with a newline so that
1837
- * it can be interleaved with other output.
1838
- */
1839
- class OutputPrefixTransform extends Transform {
1840
- constructor(prefix) {
1841
- super({
1842
- objectMode: true,
1843
- transform: (chunk, encoding, callback) => {
1844
- let result = chunk.toString(encoding);
1845
- if (result.endsWith("\n")) {
1846
- result = result.slice(0, -1);
1847
- }
1848
- // Some loggers emit newline then ANSI reset code causing
1849
- // blank lines if we do not remove the newline.
1850
- // TODO: Consider removing all ANSI escape codes as it causes
1851
- // some confusing output when interleaved.
1852
- if (result.endsWith("\n\u001B[0m")) {
1853
- result = result.slice(0, -5) + result.slice(-4);
1854
- }
1855
- result = prefix + result.replace(/\n/g, `\n${prefix}`) + "\n";
1856
- callback(null, result);
1857
- },
1858
- });
1859
- }
1860
- }
1861
- function pipeToConsole(result, name) {
1862
- var _a, _b;
1863
- (_a = result.stdout) === null || _a === void 0 ? void 0 : _a.pipe(new OutputPrefixTransform(`${name}: `)).pipe(process.stdout);
1864
- (_b = result.stderr) === null || _b === void 0 ? void 0 : _b.pipe(new OutputPrefixTransform(`${name} (stderr): `)).pipe(process.stderr);
1865
- }
1866
- function checkPidRunning(pid) {
1867
- try {
1868
- process.kill(pid, 0);
1869
- return true;
1870
- }
1871
- catch (e) {
1872
- return false;
1873
- }
1874
- }
1875
- async function getContainerId({ executor, name, hasFailed, pid, }) {
1876
- function log(value) {
1877
- console.log(`${name} (get-container-id): ${value}`);
1878
- }
1879
- async function check() {
1880
- let result;
1881
- try {
1882
- result = (await execa("docker", ["inspect", name, "-f", "{{.Id}}"]))
1883
- .stdout;
1884
- }
1885
- catch (e) {
1886
- result = null;
1887
- }
1888
- // Debugging to help us solve CALS-366.
1889
- const ps = execa("docker", ["ps"]);
1890
- pipeToConsole(ps, `${name} (ps)`);
1891
- await ps;
1892
- // Debugging to help us solve CALS-366.
1893
- if (!checkPidRunning(pid)) {
1894
- log("Process not running");
1895
- }
1896
- return result;
1897
- }
1898
- // If the container is not running, retry a few times to cover
1899
- // the initial starting where we might check before the container
1900
- // is running.
1901
- // Increased from 25 to 100 to see if it helps for solving CALS-366.
1902
- for (let i = 0; i < 100; i++) {
1903
- if (i > 0) {
1904
- // Delay a bit before checking again.
1905
- log("Retrying in a bit...");
1906
- await new Promise((resolve) => setTimeout(resolve, 200));
1907
- }
1908
- executor.checkCanContinue();
1909
- if (hasFailed()) {
1910
- break;
1911
- }
1912
- const id = await check();
1913
- if (id !== null) {
1914
- log(`Resolved to ${id}`);
1915
- return id;
1916
- }
1917
- }
1918
- throw new Error(`Could not find ID for container with name ${name}`);
1919
- }
1920
- async function pullImage({ imageId }) {
1921
- console.log(`Pulling ${imageId}`);
1922
- const process = execa("docker", ["pull", imageId]);
1923
- pipeToConsole(process, `pull-image (${imageId})`);
1924
- await process;
1925
- }
1926
- async function checkImageExistsLocally({ imageId, }) {
1927
- const result = await execa("docker", ["images", "-q", imageId]);
1928
- const found = result.stdout != "";
1929
- console.log(`image ${imageId} ${found ? "was present locally" : "was not found locally"}`);
1930
- return found;
1931
- }
1932
- async function startContainer({ executor, network, imageId, alias, env, dockerArgs = [], pull = false, }) {
1933
- executor.checkCanContinue();
1934
- const containerName = generateName(alias);
1935
- // Prefer pulling image here so that the call on getContainerId
1936
- // will not time out due to pulling the image.
1937
- // If pull is false, we will still fallback to pulling if we cannot
1938
- // find the image locally.
1939
- if (pull || !(await checkImageExistsLocally({ imageId }))) {
1940
- await pullImage({
1941
- imageId,
1942
- });
1943
- }
1944
- const args = [
1945
- "run",
1946
- "--rm",
1947
- "--network",
1948
- network.id,
1949
- "--name",
1950
- containerName,
1951
- ...dockerArgs,
1952
- ];
1953
- if (alias != null) {
1954
- args.push(`--network-alias=${alias}`);
1955
- }
1956
- if (env != null) {
1957
- for (const [key, value] of Object.entries(env)) {
1958
- args.push("-e", `${key}=${value}`);
1959
- }
1960
- }
1961
- args.push(imageId);
1962
- console.log(`Starting ${imageId}`);
1963
- const process = execa("docker", args);
1964
- pipeToConsole(process, alias !== null && alias !== void 0 ? alias : containerName);
1965
- let failed = false;
1966
- process.catch(() => {
1967
- failed = true;
1968
- });
1969
- if (!process.pid) {
1970
- throw new Error("No process identifier (PID) was returned for the process that was started when running trying to run Docker container");
1971
- }
1972
- const id = await getContainerId({
1973
- executor,
1974
- name: containerName,
1975
- hasFailed: () => failed,
1976
- pid: process.pid,
1977
- });
1978
- executor.registerCleanupTask(async () => {
1979
- console.log(`Stopping container ${containerName}`);
1980
- const r = execa("docker", ["stop", containerName]);
1981
- pipeToConsole(r, (alias !== null && alias !== void 0 ? alias : containerName) + " (stop)");
1982
- try {
1983
- await r;
1984
- }
1985
- catch (e) {
1986
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
1987
- if (!(e.stderr || "").includes("No such container")) {
1988
- throw e;
1989
- }
1990
- }
1991
- });
1992
- return {
1993
- id,
1994
- name: containerName,
1995
- network,
1996
- process,
1997
- executor,
1998
- };
1999
- }
2000
- async function runNpmRunScript(name, options) {
2001
- const result = execa("npm", ["run", name], {
2002
- env: options === null || options === void 0 ? void 0 : options.env,
2003
- });
2004
- pipeToConsole(result, `npm run ${name}`);
2005
- await result;
2006
- }
2007
- /**
2008
- * This likely does not cover all situations.
2009
- */
2010
- async function getDockerHostAddress() {
2011
- if (process.platform === "darwin" || process.platform === "win32") {
2012
- return "host.docker.internal";
2013
- }
2014
- if (fs.existsSync("/.dockerenv")) {
2015
- const process = execa("ip", ["route"]);
2016
- pipeToConsole(process, "ip route");
2017
- const res = await process;
2018
- try {
2019
- return (res.stdout
2020
- .split("\n")
2021
- .filter((it) => it.includes("default via"))
2022
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
2023
- .map((it) => /default via ([\d\.]+) /.exec(it)[1])[0]);
2024
- }
2025
- catch (e) {
2026
- throw new Error("Failed to extract docker host address");
2027
- }
2028
- }
2029
- return "localhost";
2030
- }
2031
- async function waitForEnterToContinue(prompt = "Press enter to continue") {
2032
- return new Promise((resolve, reject) => {
2033
- read({
2034
- prompt,
2035
- silent: true,
2036
- }, (err) => {
2037
- if (err) {
2038
- reject(err);
2039
- }
2040
- resolve();
2041
- });
2042
- });
1687
+ /**
1688
+ * Generate a value that can be used as part of resource names.
1689
+ *
1690
+ * Gives a value formatted as "yyyymmdd-xxxxxx", e.g. "20200523-3f2c87".
1691
+ */
1692
+ function generateRunId() {
1693
+ const low = parseInt("100000", 16);
1694
+ const high = parseInt("ffffff", 16);
1695
+ const range = high - low + 1;
1696
+ const now = new Date();
1697
+ return [
1698
+ now.getUTCFullYear(),
1699
+ (now.getUTCMonth() + 1).toString().padStart(2, "0"),
1700
+ now.getUTCDate().toString().padStart(2, "0"),
1701
+ "-",
1702
+ (Math.floor(Math.random() * range) + low).toString(16),
1703
+ ].join("");
1704
+ }
1705
+ /**
1706
+ * Generate a name that can be used for a resource.
1707
+ */
1708
+ function generateName(extra) {
1709
+ const s = extra === undefined ? "" : `-${extra}`;
1710
+ return `autotest-${generateRunId()}${s}`;
1711
+ }
1712
+ /**
1713
+ * Create a new Docker network.
1714
+ */
1715
+ async function createNetwork(executor) {
1716
+ executor.checkCanContinue();
1717
+ const networkName = generateName();
1718
+ await execa("docker", ["network", "create", networkName]);
1719
+ const lsRes = await execa("docker", [
1720
+ "network",
1721
+ "ls",
1722
+ "-q",
1723
+ "-f",
1724
+ `name=${networkName}`,
1725
+ ]);
1726
+ const networkId = lsRes.stdout.trim();
1727
+ console.log(`Network ${networkName} (${networkId}) created`);
1728
+ executor.registerCleanupTask(async () => {
1729
+ await execa("docker", ["network", "rm", networkId]);
1730
+ console.log(`Network ${networkName} (${networkId}) deleted`);
1731
+ });
1732
+ return {
1733
+ id: networkId,
1734
+ };
1735
+ }
1736
+ /**
1737
+ * Execute curl within the Docker network.
1738
+ */
1739
+ async function curl(executor, network, ...args) {
1740
+ executor.checkCanContinue();
1741
+ const result = await execa("docker", [
1742
+ "run",
1743
+ "-i",
1744
+ "--rm",
1745
+ "--network",
1746
+ network.id,
1747
+ "byrnedo/alpine-curl",
1748
+ ...args,
1749
+ ]);
1750
+ return result.stdout;
1751
+ }
1752
+ /**
1753
+ * Repeatedly check for a condition until timeout.
1754
+ *
1755
+ * The condition can throw an error without aborting the loop.
1756
+ * To abort the condition must return false.
1757
+ */
1758
+ async function pollForCondition({ container, attempts, waitIntervalSec, condition, }) {
1759
+ function log(value) {
1760
+ console.log(`${container.name} (poll): ${value}`);
1761
+ }
1762
+ container.executor.checkCanContinue();
1763
+ log(`Waiting for condition.. Checking ${attempts} times by ${waitIntervalSec} sec`);
1764
+ const start = performance.now();
1765
+ const duration = () => {
1766
+ const end = performance.now();
1767
+ return Math.round((end - start) / 1000);
1768
+ };
1769
+ for (let i = 0; i < attempts; i++) {
1770
+ container.executor.checkCanContinue();
1771
+ if (!(await isRunning(container.executor, container))) {
1772
+ throw new Error(`Container ${container.name} not running as expected`);
1773
+ }
1774
+ try {
1775
+ const result = await condition();
1776
+ if (!result) {
1777
+ break;
1778
+ }
1779
+ log(`Took ${duration()} seconds for condition`);
1780
+ return;
1781
+ }
1782
+ catch (e) {
1783
+ log("Still waiting...");
1784
+ await new Promise((resolve) => setTimeout(resolve, waitIntervalSec * 1000));
1785
+ }
1786
+ }
1787
+ throw new Error(`Failed to wait for container ${container.name}`);
1788
+ }
1789
+ async function waitForHttpOk({ container, url, attempts = 30, waitIntervalSec = 1, }) {
1790
+ await pollForCondition({
1791
+ container,
1792
+ attempts,
1793
+ waitIntervalSec,
1794
+ condition: async () => {
1795
+ await curl(container.executor, container.network, "-fsS", url);
1796
+ return true;
1797
+ },
1798
+ });
1799
+ }
1800
+ async function waitForPostgresAvailable({ container, attempts = 30, waitIntervalSec = 1, username = "user", password = "password", dbname, }) {
1801
+ await pollForCondition({
1802
+ container,
1803
+ attempts,
1804
+ waitIntervalSec,
1805
+ condition: async () => {
1806
+ await execa("docker", [
1807
+ "exec",
1808
+ "-e",
1809
+ `PGPASSWORD=${password}`,
1810
+ container.name,
1811
+ "psql",
1812
+ "-h",
1813
+ "localhost",
1814
+ "-U",
1815
+ username,
1816
+ "-c",
1817
+ "select 1",
1818
+ dbname,
1819
+ ]);
1820
+ return true;
1821
+ },
1822
+ });
1823
+ }
1824
+ async function isRunning(executor, container) {
1825
+ executor.checkCanContinue();
1826
+ try {
1827
+ await execa("docker", ["inspect", container.name]);
1828
+ return true;
1829
+ }
1830
+ catch (e) {
1831
+ return false;
1832
+ }
1833
+ }
1834
+ /**
1835
+ * A stream transform that injects a prefix into every line
1836
+ * and also forces every chunk to end with a newline so that
1837
+ * it can be interleaved with other output.
1838
+ */
1839
+ class OutputPrefixTransform extends Transform {
1840
+ constructor(prefix) {
1841
+ super({
1842
+ objectMode: true,
1843
+ transform: (chunk, encoding, callback) => {
1844
+ let result = chunk.toString(encoding);
1845
+ if (result.endsWith("\n")) {
1846
+ result = result.slice(0, -1);
1847
+ }
1848
+ // Some loggers emit newline then ANSI reset code causing
1849
+ // blank lines if we do not remove the newline.
1850
+ // TODO: Consider removing all ANSI escape codes as it causes
1851
+ // some confusing output when interleaved.
1852
+ if (result.endsWith("\n\u001B[0m")) {
1853
+ result = result.slice(0, -5) + result.slice(-4);
1854
+ }
1855
+ result = prefix + result.replace(/\n/g, `\n${prefix}`) + "\n";
1856
+ callback(null, result);
1857
+ },
1858
+ });
1859
+ }
1860
+ }
1861
+ function pipeToConsole(result, name) {
1862
+ var _a, _b;
1863
+ (_a = result.stdout) === null || _a === void 0 ? void 0 : _a.pipe(new OutputPrefixTransform(`${name}: `)).pipe(process.stdout);
1864
+ (_b = result.stderr) === null || _b === void 0 ? void 0 : _b.pipe(new OutputPrefixTransform(`${name} (stderr): `)).pipe(process.stderr);
1865
+ }
1866
+ function checkPidRunning(pid) {
1867
+ try {
1868
+ process.kill(pid, 0);
1869
+ return true;
1870
+ }
1871
+ catch (e) {
1872
+ return false;
1873
+ }
1874
+ }
1875
+ async function getContainerId({ executor, name, hasFailed, pid, }) {
1876
+ function log(value) {
1877
+ console.log(`${name} (get-container-id): ${value}`);
1878
+ }
1879
+ async function check() {
1880
+ let result;
1881
+ try {
1882
+ result = (await execa("docker", ["inspect", name, "-f", "{{.Id}}"]))
1883
+ .stdout;
1884
+ }
1885
+ catch (e) {
1886
+ result = null;
1887
+ }
1888
+ // Debugging to help us solve CALS-366.
1889
+ const ps = execa("docker", ["ps"]);
1890
+ pipeToConsole(ps, `${name} (ps)`);
1891
+ await ps;
1892
+ // Debugging to help us solve CALS-366.
1893
+ if (!checkPidRunning(pid)) {
1894
+ log("Process not running");
1895
+ }
1896
+ return result;
1897
+ }
1898
+ // If the container is not running, retry a few times to cover
1899
+ // the initial starting where we might check before the container
1900
+ // is running.
1901
+ // Increased from 25 to 100 to see if it helps for solving CALS-366.
1902
+ for (let i = 0; i < 100; i++) {
1903
+ if (i > 0) {
1904
+ // Delay a bit before checking again.
1905
+ log("Retrying in a bit...");
1906
+ await new Promise((resolve) => setTimeout(resolve, 200));
1907
+ }
1908
+ executor.checkCanContinue();
1909
+ if (hasFailed()) {
1910
+ break;
1911
+ }
1912
+ const id = await check();
1913
+ if (id !== null) {
1914
+ log(`Resolved to ${id}`);
1915
+ return id;
1916
+ }
1917
+ }
1918
+ throw new Error(`Could not find ID for container with name ${name}`);
1919
+ }
1920
+ async function pullImage({ imageId }) {
1921
+ console.log(`Pulling ${imageId}`);
1922
+ const process = execa("docker", ["pull", imageId]);
1923
+ pipeToConsole(process, `pull-image (${imageId})`);
1924
+ await process;
1925
+ }
1926
+ async function checkImageExistsLocally({ imageId, }) {
1927
+ const result = await execa("docker", ["images", "-q", imageId]);
1928
+ const found = result.stdout != "";
1929
+ console.log(`image ${imageId} ${found ? "was present locally" : "was not found locally"}`);
1930
+ return found;
1931
+ }
1932
+ async function startContainer({ executor, network, imageId, alias, env, dockerArgs = [], pull = false, }) {
1933
+ executor.checkCanContinue();
1934
+ const containerName = generateName(alias);
1935
+ // Prefer pulling image here so that the call on getContainerId
1936
+ // will not time out due to pulling the image.
1937
+ // If pull is false, we will still fallback to pulling if we cannot
1938
+ // find the image locally.
1939
+ if (pull || !(await checkImageExistsLocally({ imageId }))) {
1940
+ await pullImage({
1941
+ imageId,
1942
+ });
1943
+ }
1944
+ const args = [
1945
+ "run",
1946
+ "--rm",
1947
+ "--network",
1948
+ network.id,
1949
+ "--name",
1950
+ containerName,
1951
+ ...dockerArgs,
1952
+ ];
1953
+ if (alias != null) {
1954
+ args.push(`--network-alias=${alias}`);
1955
+ }
1956
+ if (env != null) {
1957
+ for (const [key, value] of Object.entries(env)) {
1958
+ args.push("-e", `${key}=${value}`);
1959
+ }
1960
+ }
1961
+ args.push(imageId);
1962
+ console.log(`Starting ${imageId}`);
1963
+ const process = execa("docker", args);
1964
+ pipeToConsole(process, alias !== null && alias !== void 0 ? alias : containerName);
1965
+ let failed = false;
1966
+ process.catch(() => {
1967
+ failed = true;
1968
+ });
1969
+ if (!process.pid) {
1970
+ throw new Error("No process identifier (PID) was returned for the process that was started when running trying to run Docker container");
1971
+ }
1972
+ const id = await getContainerId({
1973
+ executor,
1974
+ name: containerName,
1975
+ hasFailed: () => failed,
1976
+ pid: process.pid,
1977
+ });
1978
+ executor.registerCleanupTask(async () => {
1979
+ console.log(`Stopping container ${containerName}`);
1980
+ const r = execa("docker", ["stop", containerName]);
1981
+ pipeToConsole(r, (alias !== null && alias !== void 0 ? alias : containerName) + " (stop)");
1982
+ try {
1983
+ await r;
1984
+ }
1985
+ catch (e) {
1986
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
1987
+ if (!(e.stderr || "").includes("No such container")) {
1988
+ throw e;
1989
+ }
1990
+ }
1991
+ });
1992
+ return {
1993
+ id,
1994
+ name: containerName,
1995
+ network,
1996
+ process,
1997
+ executor,
1998
+ };
1999
+ }
2000
+ async function runNpmRunScript(name, options) {
2001
+ const result = execa("npm", ["run", name], {
2002
+ env: options === null || options === void 0 ? void 0 : options.env,
2003
+ });
2004
+ pipeToConsole(result, `npm run ${name}`);
2005
+ await result;
2006
+ }
2007
+ /**
2008
+ * This likely does not cover all situations.
2009
+ */
2010
+ async function getDockerHostAddress() {
2011
+ if (process.platform === "darwin" || process.platform === "win32") {
2012
+ return "host.docker.internal";
2013
+ }
2014
+ if (fs.existsSync("/.dockerenv")) {
2015
+ const process = execa("ip", ["route"]);
2016
+ pipeToConsole(process, "ip route");
2017
+ const res = await process;
2018
+ try {
2019
+ return (res.stdout
2020
+ .split("\n")
2021
+ .filter((it) => it.includes("default via"))
2022
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
2023
+ .map((it) => /default via ([\d\.]+) /.exec(it)[1])[0]);
2024
+ }
2025
+ catch (e) {
2026
+ throw new Error("Failed to extract docker host address");
2027
+ }
2028
+ }
2029
+ return "localhost";
2030
+ }
2031
+ async function waitForEnterToContinue(prompt = "Press enter to continue") {
2032
+ return new Promise((resolve, reject) => {
2033
+ read({
2034
+ prompt,
2035
+ silent: true,
2036
+ }, (err) => {
2037
+ if (err) {
2038
+ reject(err);
2039
+ }
2040
+ resolve();
2041
+ });
2042
+ });
2043
2043
  }
2044
2044
 
2045
2045
  const VERSION = version;