@datadog/datadog-ci 0.18.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/LICENSE-3rdparty.csv +1 -0
  2. package/README.md +28 -2
  3. package/dist/commands/git-metadata/__tests__/git.test.js +44 -2
  4. package/dist/commands/{dependencies/__tests__/upload.test.d.ts → git-metadata/__tests__/library.test.d.ts} +0 -0
  5. package/dist/commands/git-metadata/__tests__/library.test.js +82 -0
  6. package/dist/commands/git-metadata/git.d.ts +1 -2
  7. package/dist/commands/git-metadata/git.js +13 -25
  8. package/dist/commands/git-metadata/index.d.ts +1 -0
  9. package/dist/commands/git-metadata/index.js +13 -0
  10. package/dist/commands/git-metadata/library.d.ts +6 -0
  11. package/dist/commands/git-metadata/library.js +68 -0
  12. package/dist/commands/git-metadata/upload.d.ts +1 -2
  13. package/dist/commands/git-metadata/upload.js +31 -27
  14. package/dist/commands/lambda/__tests__/functions/instrument.test.js +2 -2
  15. package/dist/commands/lambda/__tests__/functions/uninstrument.test.js +2 -2
  16. package/dist/commands/lambda/__tests__/instrument.test.js +14 -9
  17. package/dist/commands/lambda/__tests__/prompt.test.js +0 -4
  18. package/dist/commands/lambda/__tests__/uninstrument.test.js +12 -7
  19. package/dist/commands/lambda/constants.d.ts +0 -1
  20. package/dist/commands/lambda/constants.js +2 -27
  21. package/dist/commands/lambda/functions/commons.js +1 -1
  22. package/dist/commands/lambda/instrument.js +5 -2
  23. package/dist/commands/lambda/prompt.d.ts +1 -0
  24. package/dist/commands/lambda/prompt.js +19 -10
  25. package/dist/commands/lambda/tags.js +1 -5
  26. package/dist/commands/lambda/uninstrument.js +1 -1
  27. package/dist/commands/synthetics/__tests__/cli.test.js +107 -4
  28. package/dist/commands/synthetics/__tests__/fixtures.d.ts +2 -1
  29. package/dist/commands/synthetics/__tests__/fixtures.js +4 -1
  30. package/dist/commands/synthetics/__tests__/reporters/default.test.js +2 -1
  31. package/dist/commands/synthetics/__tests__/run-test.test.js +4 -4
  32. package/dist/commands/synthetics/__tests__/utils.test.js +31 -1
  33. package/dist/commands/synthetics/api.d.ts +1 -0
  34. package/dist/commands/synthetics/api.js +5 -3
  35. package/dist/commands/synthetics/command.js +13 -5
  36. package/dist/commands/synthetics/crypto.d.ts +2 -1
  37. package/dist/commands/synthetics/errors.d.ts +7 -2
  38. package/dist/commands/synthetics/errors.js +6 -3
  39. package/dist/commands/synthetics/index.d.ts +1 -1
  40. package/dist/commands/synthetics/index.js +2 -1
  41. package/dist/commands/synthetics/interfaces.d.ts +5 -1
  42. package/dist/commands/synthetics/reporters/default.d.ts +1 -0
  43. package/dist/commands/synthetics/reporters/default.js +16 -8
  44. package/dist/commands/synthetics/run-test.js +9 -15
  45. package/dist/commands/synthetics/utils.d.ts +1 -0
  46. package/dist/commands/synthetics/utils.js +29 -13
  47. package/dist/commands/{dependencies → version}/cli.d.ts +0 -0
  48. package/dist/commands/version/cli.js +27 -0
  49. package/dist/index.d.ts +2 -1
  50. package/dist/index.js +3 -1
  51. package/package.json +14 -2
  52. package/dist/commands/dependencies/__tests__/helpers/context.d.ts +0 -12
  53. package/dist/commands/dependencies/__tests__/helpers/context.js +0 -31
  54. package/dist/commands/dependencies/__tests__/helpers/stream.d.ts +0 -2
  55. package/dist/commands/dependencies/__tests__/helpers/stream.js +0 -24
  56. package/dist/commands/dependencies/__tests__/helpers/upload.run.d.ts +0 -13
  57. package/dist/commands/dependencies/__tests__/helpers/upload.run.js +0 -40
  58. package/dist/commands/dependencies/__tests__/upload.test.js +0 -264
  59. package/dist/commands/dependencies/api.d.ts +0 -2
  60. package/dist/commands/dependencies/api.js +0 -27
  61. package/dist/commands/dependencies/cli.js +0 -4
  62. package/dist/commands/dependencies/interfaces.d.ts +0 -10
  63. package/dist/commands/dependencies/interfaces.js +0 -2
  64. package/dist/commands/dependencies/renderer.d.ts +0 -13
  65. package/dist/commands/dependencies/renderer.js +0 -57
  66. package/dist/commands/dependencies/upload.d.ts +0 -16
  67. package/dist/commands/dependencies/upload.js +0 -171
@@ -257,15 +257,20 @@ UpdateFunctionConfiguration -> arn:aws:lambda:us-east-1:000000000000:function:un
257
257
  test('aborts if the the aws-sdk fails', () => __awaiter(void 0, void 0, void 0, function* () {
258
258
  ;
259
259
  fs.readFile.mockImplementation((a, b, callback) => callback({}));
260
- aws_sdk_1.Lambda.mockImplementation(() => ({ promise: Promise.reject() }));
260
+ aws_sdk_1.Lambda.mockImplementation(() => ({
261
+ listFunctions: jest.fn().mockImplementation(() => ({ promise: () => Promise.reject('ListFunctionsError') })),
262
+ }));
261
263
  process.env = {};
264
+ aws_sdk_1.Lambda.mockImplementation(() => ({
265
+ listFunctions: jest.fn().mockImplementation(() => ({ promise: () => Promise.reject('ListFunctionsError') })),
266
+ }));
262
267
  const command = fixtures_1.createCommand(uninstrument_1.UninstrumentCommand);
263
268
  command['region'] = 'ap-southeast-1';
264
269
  command['regExPattern'] = 'my-function';
265
270
  const code = yield command['execute']();
266
271
  const output = command.context.stdout.toString();
267
272
  expect(code).toBe(1);
268
- expect(output).toMatch(`Fetching Lambda functions, this might take a while.\n${chalk_1.red('[Error]')} Couldn't fetch Lambda functions. Error: Max retry count exceeded.\n`);
273
+ expect(output).toMatch(`Fetching Lambda functions, this might take a while.\n${chalk_1.red('[Error]')} Couldn't fetch Lambda functions. Error: Max retry count exceeded. ListFunctionsError\n`);
269
274
  }));
270
275
  test('uninstrument multiple functions interactively', () => __awaiter(void 0, void 0, void 0, function* () {
271
276
  ;
@@ -357,7 +362,7 @@ UpdateFunctionConfiguration -> arn:aws:lambda:us-east-1:000000000000:function:un
357
362
  const output = context.stdout.toString();
358
363
  expect(code).toBe(0);
359
364
  expect(output).toMatchInlineSnapshot(`
360
- "${chalk_1.bold(chalk_1.yellow('[!]'))} No existing AWS credentials found, let's set them up!
365
+ "${chalk_1.bold(chalk_1.yellow('[!]'))} No AWS credentials found, let's set them up! Or you can re-run the command and supply the AWS credentials in the same way when you invoke the AWS CLI.
361
366
  Fetching Lambda functions, this might take a while.\n
362
367
  ${chalk_1.bold(chalk_1.yellow('[!]'))} Functions to be updated:
363
368
  \t- ${chalk_1.bold('arn:aws:lambda:sa-east-1:123456789012:function:lambda-hello-world')}
@@ -486,7 +491,7 @@ ${chalk_1.yellow('[!]')} Uninstrumenting functions.
486
491
  const output = context.stdout.toString();
487
492
  expect(code).toBe(0);
488
493
  expect(output).toMatchInlineSnapshot(`
489
- "${chalk_1.bold(chalk_1.yellow('[!]'))} No existing AWS credentials found, let's set them up!\n
494
+ "${chalk_1.bold(chalk_1.yellow('[!]'))} No AWS credentials found, let's set them up! Or you can re-run the command and supply the AWS credentials in the same way when you invoke the AWS CLI.\n
490
495
  ${chalk_1.bold(chalk_1.yellow('[!]'))} Functions to be updated:
491
496
  \t- ${chalk_1.bold('arn:aws:lambda:sa-east-1:123456789012:function:lambda-hello-world')}
492
497
  \t- ${chalk_1.bold('arn:aws:lambda:sa-east-1:123456789012:function:lambda-hello-world-2')}\n
@@ -526,7 +531,7 @@ ${chalk_1.yellow('[!]')} Uninstrumenting functions.
526
531
  const output = context.stdout.toString();
527
532
  expect(code).toBe(1);
528
533
  expect(output).toMatchInlineSnapshot(`
529
- "${chalk_1.bold(chalk_1.yellow('[!]'))} No existing AWS credentials found, let's set them up!
534
+ "${chalk_1.bold(chalk_1.yellow('[!]'))} No AWS credentials found, let's set them up! Or you can re-run the command and supply the AWS credentials in the same way when you invoke the AWS CLI.
530
535
  ${chalk_1.red('[Error]')} Unexpected error
531
536
  "
532
537
  `);
@@ -558,7 +563,7 @@ ${chalk_1.red('[Error]')} Couldn't find any Lambda functions in the specified re
558
563
  };
559
564
  fs.readFile.mockImplementation((a, b, callback) => callback({ code: 'ENOENT' }));
560
565
  aws_sdk_1.Lambda.mockImplementation(() => ({
561
- listFunctions: jest.fn().mockImplementation(() => ({ promise: () => Promise.reject('Lambda failed') })),
566
+ listFunctions: jest.fn().mockImplementation(() => ({ promise: () => Promise.reject('ListFunctionsError') })),
562
567
  }));
563
568
  const cli = fixtures_1.makeCli();
564
569
  const context = fixtures_1.createMockContext();
@@ -567,7 +572,7 @@ ${chalk_1.red('[Error]')} Couldn't find any Lambda functions in the specified re
567
572
  expect(code).toBe(1);
568
573
  expect(output).toMatchInlineSnapshot(`
569
574
  "Fetching Lambda functions, this might take a while.
570
- ${chalk_1.red('[Error]')} Couldn't fetch Lambda functions. Error: Max retry count exceeded.
575
+ ${chalk_1.red('[Error]')} Couldn't fetch Lambda functions. Error: Max retry count exceeded. ListFunctionsError
571
576
  "
572
577
  `);
573
578
  }));
@@ -41,7 +41,6 @@ export declare const ARM_LAYER_SUFFIX = "-ARM";
41
41
  export declare const PYTHON_HANDLER_LOCATION = "datadog_lambda.handler.handler";
42
42
  export declare const NODE_HANDLER_LOCATION = "/opt/nodejs/node_modules/datadog-lambda-js/handler.handler";
43
43
  export declare const SITES: string[];
44
- export declare const AWS_REGIONS: string[];
45
44
  export declare const DEFAULT_LAYER_AWS_ACCOUNT = "464622532012";
46
45
  export declare const GOVCLOUD_LAYER_AWS_ACCOUNT = "002406178527";
47
46
  export declare const SUBSCRIPTION_FILTER_NAME = "datadog-ci-filter";
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.EXTRA_TAGS_REG_EXP = exports.MAX_LAMBDA_STATE_CHECK_ATTEMPTS = exports.LIST_FUNCTIONS_MAX_RETRY_COUNT = exports.AWS_SESSION_TOKEN_ENV_VAR = exports.AWS_DEFAULT_REGION_ENV_VAR = exports.AWS_SECRET_ACCESS_KEY_ENV_VAR = exports.AWS_ACCESS_KEY_ID_ENV_VAR = exports.CI_KMS_API_KEY_ENV_VAR = exports.CI_API_KEY_SECRET_ARN_ENV_VAR = exports.CI_API_KEY_ENV_VAR = exports.CI_SITE_ENV_VAR = exports.DOTNET_TRACER_HOME_ENV_VAR = exports.PROFILER_PATH_ENV_VAR = exports.PROFILER_ENV_VAR = exports.ENABLE_PROFILING_ENV_VAR = exports.CAPTURE_LAMBDA_PAYLOAD_ENV_VAR = exports.EXTRA_TAGS_ENV_VAR = exports.ENVIRONMENT_ENV_VAR = exports.VERSION_ENV_VAR = exports.SERVICE_ENV_VAR = exports.LAMBDA_HANDLER_ENV_VAR = exports.LOG_LEVEL_ENV_VAR = exports.FLUSH_TO_LOG_ENV_VAR = exports.MERGE_XRAY_TRACES_ENV_VAR = exports.TRACE_ENABLED_ENV_VAR = exports.SITE_ENV_VAR = exports.KMS_API_KEY_ENV_VAR = exports.API_KEY_SECRET_ARN_ENV_VAR = exports.API_KEY_ENV_VAR = exports.DD_DOTNET_TRACER_HOME = exports.CORECLR_PROFILER_PATH = exports.CORECLR_PROFILER = exports.CORECLR_ENABLE_PROFILING = exports.TAG_VERSION_NAME = exports.SUBSCRIPTION_FILTER_NAME = exports.GOVCLOUD_LAYER_AWS_ACCOUNT = exports.DEFAULT_LAYER_AWS_ACCOUNT = exports.AWS_REGIONS = exports.SITES = exports.NODE_HANDLER_LOCATION = exports.PYTHON_HANDLER_LOCATION = exports.ARM_LAYER_SUFFIX = exports.ARM64_ARCHITECTURE = exports.ARM_LAYERS = exports.RUNTIME_LOOKUP = exports.RuntimeType = exports.LAYER_LOOKUP = exports.DOTNET_RUNTIME = exports.EXTENSION_LAYER_KEY = exports.DD_LAMBDA_EXTENSION_LAYER_NAME = void 0;
4
- exports.DATADOG_APP_KEY_REG_EXP = exports.DATADOG_API_KEY_REG_EXP = exports.AWS_SECRET_ACCESS_KEY_REG_EXP = exports.AWS_ACCESS_KEY_ID_REG_EXP = void 0;
3
+ exports.AWS_ACCESS_KEY_ID_REG_EXP = exports.EXTRA_TAGS_REG_EXP = exports.MAX_LAMBDA_STATE_CHECK_ATTEMPTS = exports.LIST_FUNCTIONS_MAX_RETRY_COUNT = exports.AWS_SESSION_TOKEN_ENV_VAR = exports.AWS_DEFAULT_REGION_ENV_VAR = exports.AWS_SECRET_ACCESS_KEY_ENV_VAR = exports.AWS_ACCESS_KEY_ID_ENV_VAR = exports.CI_KMS_API_KEY_ENV_VAR = exports.CI_API_KEY_SECRET_ARN_ENV_VAR = exports.CI_API_KEY_ENV_VAR = exports.CI_SITE_ENV_VAR = exports.DOTNET_TRACER_HOME_ENV_VAR = exports.PROFILER_PATH_ENV_VAR = exports.PROFILER_ENV_VAR = exports.ENABLE_PROFILING_ENV_VAR = exports.CAPTURE_LAMBDA_PAYLOAD_ENV_VAR = exports.EXTRA_TAGS_ENV_VAR = exports.ENVIRONMENT_ENV_VAR = exports.VERSION_ENV_VAR = exports.SERVICE_ENV_VAR = exports.LAMBDA_HANDLER_ENV_VAR = exports.LOG_LEVEL_ENV_VAR = exports.FLUSH_TO_LOG_ENV_VAR = exports.MERGE_XRAY_TRACES_ENV_VAR = exports.TRACE_ENABLED_ENV_VAR = exports.SITE_ENV_VAR = exports.KMS_API_KEY_ENV_VAR = exports.API_KEY_SECRET_ARN_ENV_VAR = exports.API_KEY_ENV_VAR = exports.DD_DOTNET_TRACER_HOME = exports.CORECLR_PROFILER_PATH = exports.CORECLR_PROFILER = exports.CORECLR_ENABLE_PROFILING = exports.TAG_VERSION_NAME = exports.SUBSCRIPTION_FILTER_NAME = exports.GOVCLOUD_LAYER_AWS_ACCOUNT = exports.DEFAULT_LAYER_AWS_ACCOUNT = exports.SITES = exports.NODE_HANDLER_LOCATION = exports.PYTHON_HANDLER_LOCATION = exports.ARM_LAYER_SUFFIX = exports.ARM64_ARCHITECTURE = exports.ARM_LAYERS = exports.RUNTIME_LOOKUP = exports.RuntimeType = exports.LAYER_LOOKUP = exports.DOTNET_RUNTIME = exports.EXTENSION_LAYER_KEY = exports.DD_LAMBDA_EXTENSION_LAYER_NAME = void 0;
4
+ exports.DATADOG_APP_KEY_REG_EXP = exports.DATADOG_API_KEY_REG_EXP = exports.AWS_SECRET_ACCESS_KEY_REG_EXP = void 0;
5
5
  exports.DD_LAMBDA_EXTENSION_LAYER_NAME = 'Datadog-Extension';
6
6
  exports.EXTENSION_LAYER_KEY = 'extension';
7
7
  exports.DOTNET_RUNTIME = 'dotnetcore3.1';
@@ -50,31 +50,6 @@ exports.SITES = [
50
50
  'us5.datadoghq.com',
51
51
  'ddog-gov.com',
52
52
  ];
53
- exports.AWS_REGIONS = [
54
- 'us-east-1',
55
- 'us-east-2',
56
- 'us-west-1',
57
- 'us-west-2',
58
- 'af-south-1',
59
- 'ap-east-1',
60
- 'ap-south-1',
61
- 'ap-northeast-3',
62
- 'ap-northeast-2',
63
- 'ap-southeast-1',
64
- 'ap-southeast-2',
65
- 'ap-northeast-1',
66
- 'ca-central-1',
67
- 'eu-central-1',
68
- 'eu-west-1',
69
- 'eu-west-2',
70
- 'eu-south-1',
71
- 'eu-west-3',
72
- 'eu-north-1',
73
- 'me-south-1',
74
- 'sa-east-1',
75
- 'us-gov-east-1',
76
- 'us-gov-west-1',
77
- ];
78
53
  exports.DEFAULT_LAYER_AWS_ACCOUNT = '464622532012';
79
54
  exports.GOVCLOUD_LAYER_AWS_ACCOUNT = '002406178527';
80
55
  exports.SUBSCRIPTION_FILTER_NAME = 'datadog-ci-filter';
@@ -206,7 +206,7 @@ const getLambdaFunctionConfigsFromRegex = (lambda, pattern) => __awaiter(void 0,
206
206
  catch (e) {
207
207
  retryCount++;
208
208
  if (retryCount > constants_1.LIST_FUNCTIONS_MAX_RETRY_COUNT) {
209
- throw Error('Max retry count exceeded.');
209
+ throw Error(`Max retry count exceeded. ${e}`);
210
210
  }
211
211
  }
212
212
  }
@@ -42,9 +42,12 @@ class InstrumentCommand extends clipanion_1.Command {
42
42
  if (this.interactive) {
43
43
  try {
44
44
  if (commons_1.isMissingAWSCredentials()) {
45
- this.context.stdout.write(`${chalk_1.bold(chalk_1.yellow('[!]'))} No existing AWS credentials found, let's set them up!\n`);
45
+ this.context.stdout.write(`${chalk_1.bold(chalk_1.yellow('[!]'))} No AWS credentials found, let's set them up! Or you can re-run the command and supply the AWS credentials in the same way when you invoke the AWS CLI.\n`);
46
46
  yield prompt_1.requestAWSCredentials();
47
47
  }
48
+ // Always ask for region since the user may not want to use the default
49
+ this.context.stdout.write(`${chalk_1.bold(chalk_1.yellow('[!]'))} Configure AWS region.\n`);
50
+ yield prompt_1.requestAWSRegion(process.env[constants_1.AWS_DEFAULT_REGION_ENV_VAR]);
48
51
  if (commons_1.isMissingDatadogEnvVars()) {
49
52
  this.context.stdout.write(`${chalk_1.bold(chalk_1.yellow('[!]'))} Configure Datadog settings.\n`);
50
53
  yield prompt_1.requestDatadogEnvVars();
@@ -185,7 +188,7 @@ class InstrumentCommand extends clipanion_1.Command {
185
188
  getCurrentGitStatus() {
186
189
  return __awaiter(this, void 0, void 0, function* () {
187
190
  const simpleGit = yield git_1.newSimpleGit();
188
- const gitCommitInfo = yield git_1.getCommitInfo(simpleGit, this.context.stdout);
191
+ const gitCommitInfo = yield git_1.getCommitInfo(simpleGit);
189
192
  if (gitCommitInfo === undefined) {
190
193
  throw new Error('Git commit info is not defined');
191
194
  }
@@ -4,6 +4,7 @@ export declare const datadogEnvVarsQuestions: (datadogApiKeyType: Record<string,
4
4
  export declare const confirmationQuestion: (message: string) => ConfirmQuestion;
5
5
  export declare const functionSelectionQuestion: (functionNames: string[]) => CheckboxQuestion;
6
6
  export declare const requestAWSCredentials: () => Promise<void>;
7
+ export declare const requestAWSRegion: (defaultRegion?: string | undefined) => Promise<void>;
7
8
  export declare const requestDatadogEnvVars: () => Promise<void>;
8
9
  export declare const requestChangesConfirmation: (message: string) => Promise<any>;
9
10
  export declare const requestFunctionSelection: (functionNames: string[]) => Promise<any>;
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.requestFunctionSelection = exports.requestChangesConfirmation = exports.requestDatadogEnvVars = exports.requestAWSCredentials = exports.functionSelectionQuestion = exports.confirmationQuestion = exports.datadogEnvVarsQuestions = exports.datadogApiKeyTypeQuestion = void 0;
12
+ exports.requestFunctionSelection = exports.requestChangesConfirmation = exports.requestDatadogEnvVars = exports.requestAWSRegion = exports.requestAWSCredentials = exports.functionSelectionQuestion = exports.confirmationQuestion = exports.datadogEnvVarsQuestions = exports.datadogApiKeyTypeQuestion = void 0;
13
13
  const chalk_1 = require("chalk");
14
14
  const inquirer_1 = require("inquirer");
15
15
  const constants_1 = require("./constants");
@@ -47,15 +47,13 @@ const awsCredentialsQuestions = [
47
47
  name: constants_1.AWS_SESSION_TOKEN_ENV_VAR,
48
48
  type: 'password',
49
49
  },
50
- {
51
- // AWS_DEFAULT_REGION
52
- choices: constants_1.AWS_REGIONS,
53
- default: 0,
54
- message: 'Select an AWS region:',
55
- name: constants_1.AWS_DEFAULT_REGION_ENV_VAR,
56
- type: 'list',
57
- },
58
50
  ];
51
+ const awsRegionQuestion = (defaultRegion) => ({
52
+ default: defaultRegion,
53
+ message: 'Which AWS region (e.g., us-east-1) your Lambda functions are deployed?',
54
+ name: constants_1.AWS_DEFAULT_REGION_ENV_VAR,
55
+ type: 'input',
56
+ });
59
57
  const datadogApiKeyTypeQuestion = (datadogSite) => ({
60
58
  choices: [
61
59
  {
@@ -131,7 +129,6 @@ const requestAWSCredentials = () => __awaiter(void 0, void 0, void 0, function*
131
129
  const awsCredentialsAnswers = yield inquirer_1.prompt(awsCredentialsQuestions);
132
130
  process.env[constants_1.AWS_ACCESS_KEY_ID_ENV_VAR] = awsCredentialsAnswers[constants_1.AWS_ACCESS_KEY_ID_ENV_VAR];
133
131
  process.env[constants_1.AWS_SECRET_ACCESS_KEY_ENV_VAR] = awsCredentialsAnswers[constants_1.AWS_SECRET_ACCESS_KEY_ENV_VAR];
134
- process.env[constants_1.AWS_DEFAULT_REGION_ENV_VAR] = awsCredentialsAnswers[constants_1.AWS_DEFAULT_REGION_ENV_VAR];
135
132
  if (awsCredentialsAnswers[constants_1.AWS_SESSION_TOKEN_ENV_VAR] !== undefined) {
136
133
  process.env[constants_1.AWS_SESSION_TOKEN_ENV_VAR] = awsCredentialsAnswers[constants_1.AWS_SESSION_TOKEN_ENV_VAR];
137
134
  }
@@ -143,6 +140,18 @@ const requestAWSCredentials = () => __awaiter(void 0, void 0, void 0, function*
143
140
  }
144
141
  });
145
142
  exports.requestAWSCredentials = requestAWSCredentials;
143
+ const requestAWSRegion = (defaultRegion) => __awaiter(void 0, void 0, void 0, function* () {
144
+ try {
145
+ const awsRegionAnswer = yield inquirer_1.prompt(awsRegionQuestion(defaultRegion));
146
+ process.env[constants_1.AWS_DEFAULT_REGION_ENV_VAR] = awsRegionAnswer[constants_1.AWS_DEFAULT_REGION_ENV_VAR];
147
+ }
148
+ catch (e) {
149
+ if (e instanceof Error) {
150
+ throw Error(`Couldn't set AWS region. ${e.message}`);
151
+ }
152
+ }
153
+ });
154
+ exports.requestAWSRegion = requestAWSRegion;
146
155
  const requestDatadogEnvVars = () => __awaiter(void 0, void 0, void 0, function* () {
147
156
  try {
148
157
  const datadogSiteAnswer = yield inquirer_1.prompt(datadogSiteQuestion);
@@ -8,15 +8,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
9
  });
10
10
  };
11
- var __importDefault = (this && this.__importDefault) || function (mod) {
12
- return (mod && mod.__esModule) ? mod : { "default": mod };
13
- };
14
11
  Object.defineProperty(exports, "__esModule", { value: true });
15
12
  exports.hasVersionTag = exports.calculateTagRemoveRequest = exports.calculateTagUpdateRequest = exports.applyTagConfig = void 0;
16
- const path_1 = __importDefault(require("path"));
17
13
  const constants_1 = require("./constants");
18
14
  // tslint:disable-next-line
19
- const { version } = require(path_1.default.join(__dirname, '../../../package.json'));
15
+ const { version } = require('../../../package.json');
20
16
  const applyTagConfig = (lambda, config) => __awaiter(void 0, void 0, void 0, function* () {
21
17
  const { tagResourceRequest, untagResourceRequest } = config;
22
18
  if (tagResourceRequest !== undefined) {
@@ -38,7 +38,7 @@ class UninstrumentCommand extends clipanion_1.Command {
38
38
  if (this.interactive) {
39
39
  try {
40
40
  if (commons_1.isMissingAWSCredentials()) {
41
- this.context.stdout.write(`${chalk_1.bold(chalk_1.yellow('[!]'))} No existing AWS credentials found, let's set them up!\n`);
41
+ this.context.stdout.write(`${chalk_1.bold(chalk_1.yellow('[!]'))} No AWS credentials found, let's set them up! Or you can re-run the command and supply the AWS credentials in the same way when you invoke the AWS CLI.\n`);
42
42
  yield prompt_1.requestAWSCredentials();
43
43
  }
44
44
  }
@@ -56,6 +56,12 @@ test('all option flags are supported', () => __awaiter(void 0, void 0, void 0, f
56
56
  const usage = cli.usage(command_1.RunTestCommand);
57
57
  options.forEach((option) => expect(usage).toContain(`--${option}`));
58
58
  }));
59
+ const getAxiosHttpError = (status, error) => {
60
+ const serverError = new Error(error);
61
+ serverError.response = { data: { errors: [error] }, status };
62
+ serverError.config = { baseURL: 'baseURL', url: 'url' };
63
+ return serverError;
64
+ };
59
65
  describe('run-test', () => {
60
66
  beforeEach(() => {
61
67
  jest.restoreAllMocks();
@@ -205,11 +211,8 @@ describe('run-test', () => {
205
211
  jest.spyOn(ciUtils, 'parseConfigFile').mockImplementation((config, _) => __awaiter(void 0, void 0, void 0, function* () { return config; }));
206
212
  jest.spyOn(utils, 'getSuites').mockImplementation((() => [conf]));
207
213
  // Throw to stop the test
208
- const serverError = new Error('Server Error');
209
- serverError.response = { data: { errors: ['Bad Gateway'] }, status: 502 };
210
- serverError.config = { baseURL: 'baseURL', url: 'url' };
211
214
  const triggerTests = jest.fn(() => {
212
- throw serverError;
215
+ throw getAxiosHttpError(502, 'Bad Gateway');
213
216
  });
214
217
  const apiHelper = {
215
218
  getTest: jest.fn(() => (Object.assign({}, fixtures_1.getApiTest('publicId')))),
@@ -252,4 +255,104 @@ describe('run-test', () => {
252
255
  }));
253
256
  }));
254
257
  });
258
+ describe('exit code respects `failOnCriticalErrors`', () => {
259
+ test('404 leading to `NO_TESTS_TO_RUN` never exit with 1', () => __awaiter(void 0, void 0, void 0, function* () {
260
+ const command = new command_1.RunTestCommand();
261
+ command.context = { stdout: { write: jest.fn() } };
262
+ command['config'].failOnCriticalErrors = true;
263
+ const apiHelper = {
264
+ getTest: jest.fn(() => {
265
+ throw getAxiosHttpError(404, 'Test not found');
266
+ }),
267
+ };
268
+ jest.spyOn(runTests, 'getApiHelper').mockImplementation(() => apiHelper);
269
+ jest.spyOn(ciUtils, 'parseConfigFile').mockImplementation((config, _) => __awaiter(void 0, void 0, void 0, function* () { return config; }));
270
+ jest.spyOn(utils, 'getSuites').mockImplementation((() => [fixtures_1.getTestSuite()]));
271
+ expect(yield command.execute()).toBe(0);
272
+ expect(apiHelper.getTest).toHaveBeenCalledTimes(1);
273
+ }));
274
+ test('`NO_RESULTS_TO_POLL` never exit with 1', () => __awaiter(void 0, void 0, void 0, function* () {
275
+ const command = new command_1.RunTestCommand();
276
+ command.context = { stdout: { write: jest.fn() } };
277
+ command['config'].failOnCriticalErrors = true;
278
+ const apiHelper = {
279
+ getTest: () => fixtures_1.getApiTest('123-456-789'),
280
+ triggerTests: jest.fn(() => ({})),
281
+ };
282
+ jest.spyOn(runTests, 'getApiHelper').mockImplementation(() => apiHelper);
283
+ jest.spyOn(ciUtils, 'parseConfigFile').mockImplementation((config, _) => __awaiter(void 0, void 0, void 0, function* () { return config; }));
284
+ jest.spyOn(utils, 'getSuites').mockImplementation((() => [fixtures_1.getTestSuite()]));
285
+ expect(yield command.execute()).toBe(0);
286
+ expect(apiHelper.triggerTests).toHaveBeenCalledTimes(1);
287
+ }));
288
+ describe.each([false, true])('%s', (failOnCriticalErrors) => {
289
+ const cases = [['HTTP 4xx error', 403], ['HTTP 5xx error', 502], ['Unknown error']];
290
+ const expectedExit = failOnCriticalErrors ? 1 : 0;
291
+ describe.each(cases)('%s', (_, errorCode) => {
292
+ test('unable to obtain test configurations', () => __awaiter(void 0, void 0, void 0, function* () {
293
+ const command = new command_1.RunTestCommand();
294
+ command.context = { stdout: { write: jest.fn() } };
295
+ command['config'].failOnCriticalErrors = failOnCriticalErrors;
296
+ command['testSearchQuery'] = 'test:search';
297
+ const apiHelper = {
298
+ searchTests: jest.fn(() => {
299
+ throw errorCode ? getAxiosHttpError(errorCode, 'Error') : new Error('Unknown error');
300
+ }),
301
+ };
302
+ jest.spyOn(runTests, 'getApiHelper').mockImplementation(() => apiHelper);
303
+ jest.spyOn(ciUtils, 'parseConfigFile').mockImplementation((config, __) => __awaiter(void 0, void 0, void 0, function* () { return config; }));
304
+ expect(yield command.execute()).toBe(expectedExit);
305
+ expect(apiHelper.searchTests).toHaveBeenCalledTimes(1);
306
+ }));
307
+ test('unavailable test config', () => __awaiter(void 0, void 0, void 0, function* () {
308
+ const command = new command_1.RunTestCommand();
309
+ command.context = { stdout: { write: jest.fn() } };
310
+ command['config'].failOnCriticalErrors = failOnCriticalErrors;
311
+ const apiHelper = {
312
+ getTest: jest.fn(() => {
313
+ throw errorCode ? getAxiosHttpError(errorCode, 'Error') : new Error('Unknown error');
314
+ }),
315
+ };
316
+ jest.spyOn(runTests, 'getApiHelper').mockImplementation(() => apiHelper);
317
+ jest.spyOn(ciUtils, 'parseConfigFile').mockImplementation((config, __) => __awaiter(void 0, void 0, void 0, function* () { return config; }));
318
+ jest.spyOn(utils, 'getSuites').mockImplementation((() => [fixtures_1.getTestSuite()]));
319
+ expect(yield command.execute()).toBe(expectedExit);
320
+ expect(apiHelper.getTest).toHaveBeenCalledTimes(1);
321
+ }));
322
+ test('unable to trigger tests', () => __awaiter(void 0, void 0, void 0, function* () {
323
+ const command = new command_1.RunTestCommand();
324
+ command.context = { stdout: { write: jest.fn() } };
325
+ command['config'].failOnCriticalErrors = failOnCriticalErrors;
326
+ const apiHelper = {
327
+ getTest: () => fixtures_1.getApiTest('123-456-789'),
328
+ triggerTests: jest.fn(() => {
329
+ throw errorCode ? getAxiosHttpError(errorCode, 'Error') : new Error('Unknown error');
330
+ }),
331
+ };
332
+ jest.spyOn(runTests, 'getApiHelper').mockImplementation(() => apiHelper);
333
+ jest.spyOn(ciUtils, 'parseConfigFile').mockImplementation((config, __) => __awaiter(void 0, void 0, void 0, function* () { return config; }));
334
+ jest.spyOn(utils, 'getSuites').mockImplementation((() => [fixtures_1.getTestSuite()]));
335
+ expect(yield command.execute()).toBe(expectedExit);
336
+ expect(apiHelper.triggerTests).toHaveBeenCalledTimes(1);
337
+ }));
338
+ test('unable to poll test results', () => __awaiter(void 0, void 0, void 0, function* () {
339
+ const command = new command_1.RunTestCommand();
340
+ command.context = { stdout: { write: jest.fn() } };
341
+ command['config'].failOnCriticalErrors = failOnCriticalErrors;
342
+ const apiHelper = {
343
+ getTest: () => fixtures_1.getApiTest('123-456-789'),
344
+ pollResults: jest.fn(() => {
345
+ throw errorCode ? getAxiosHttpError(errorCode, 'Error') : new Error('Unknown error');
346
+ }),
347
+ triggerTests: () => (Object.assign(Object.assign({}, fixtures_1.mockTestTriggerResponse), { results: [{ location: 1, public_id: '123-456-789', result_id: '1' }] })),
348
+ };
349
+ jest.spyOn(runTests, 'getApiHelper').mockImplementation(() => apiHelper);
350
+ jest.spyOn(ciUtils, 'parseConfigFile').mockImplementation((config, __) => __awaiter(void 0, void 0, void 0, function* () { return config; }));
351
+ jest.spyOn(utils, 'getSuites').mockImplementation((() => [fixtures_1.getTestSuite()]));
352
+ expect(yield command.execute()).toBe(expectedExit);
353
+ expect(apiHelper.pollResults).toHaveBeenCalledTimes(1);
354
+ }));
355
+ });
356
+ });
357
+ });
255
358
  });
@@ -2,12 +2,13 @@
2
2
  /// <reference types="node" />
3
3
  import * as http from 'http';
4
4
  import { ProxyConfiguration } from '../../../helpers/utils';
5
- import { ApiTestResult, BrowserTestResult, CommandConfig, MainReporter, MultiStep, MultiStepsTestResult, PollResult, Step, Test } from '../interfaces';
5
+ import { ApiTestResult, BrowserTestResult, CommandConfig, MainReporter, MultiStep, MultiStepsTestResult, PollResult, Step, Suite, Test } from '../interfaces';
6
6
  export declare const mockReporter: MainReporter;
7
7
  export declare const ciConfig: CommandConfig;
8
8
  export declare const getApiTest: (publicId: string) => Test;
9
9
  export declare const getStep: () => Step;
10
10
  export declare const getMultiStep: () => MultiStep;
11
+ export declare const getTestSuite: () => Suite;
11
12
  export declare const getBrowserPollResult: () => PollResult;
12
13
  export declare const getApiPollResult: () => PollResult;
13
14
  export declare const getBrowserResult: (opts?: any) => BrowserTestResult;
@@ -19,7 +19,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
19
19
  return result;
20
20
  };
21
21
  Object.defineProperty(exports, "__esModule", { value: true });
22
- exports.getSyntheticsProxy = exports.mockPollResultResponse = exports.mockTestTriggerResponse = exports.mockSearchResponse = exports.getMultiStepsResult = exports.getApiResult = exports.getBrowserResult = exports.getApiPollResult = exports.getBrowserPollResult = exports.getMultiStep = exports.getStep = exports.getApiTest = exports.ciConfig = exports.mockReporter = void 0;
22
+ exports.getSyntheticsProxy = exports.mockPollResultResponse = exports.mockTestTriggerResponse = exports.mockSearchResponse = exports.getMultiStepsResult = exports.getApiResult = exports.getBrowserResult = exports.getApiPollResult = exports.getBrowserPollResult = exports.getTestSuite = exports.getMultiStep = exports.getStep = exports.getApiTest = exports.ciConfig = exports.mockReporter = void 0;
23
23
  const http = __importStar(require("http"));
24
24
  const url_1 = require("url");
25
25
  const ws_1 = require("ws");
@@ -38,6 +38,7 @@ exports.mockReporter = {
38
38
  testEnd: jest.fn(),
39
39
  testTrigger: jest.fn(),
40
40
  testWait: jest.fn(),
41
+ testsWait: jest.fn(),
41
42
  };
42
43
  exports.ciConfig = {
43
44
  apiKey: '',
@@ -123,6 +124,8 @@ const getMultiStep = () => ({
123
124
  },
124
125
  });
125
126
  exports.getMultiStep = getMultiStep;
127
+ const getTestSuite = () => ({ content: { tests: [{ config: {}, id: '123-456-789' }] }, name: 'Suite 1' });
128
+ exports.getTestSuite = getTestSuite;
126
129
  const getPollResult = () => ({
127
130
  dc_id: 1,
128
131
  resultID: '123',
@@ -13,6 +13,7 @@ describe('Default reporter', () => {
13
13
  };
14
14
  const reporter = new default_1.DefaultReporter(mockContext);
15
15
  it('should log for each hook', () => {
16
+ // `testWait` is skipped as nothing is logged for the default reporter.
16
17
  const calls = [
17
18
  ['error', ['error']],
18
19
  ['initErrors', [['error']]],
@@ -21,7 +22,7 @@ describe('Default reporter', () => {
21
22
  ['runEnd', [utils_1.createSummary()]],
22
23
  ['testEnd', [{ options: {} }, [], '', []]],
23
24
  ['testTrigger', [{}, '', '', {}]],
24
- ['testWait', [{}]],
25
+ ['testsWait', [[{}]]],
25
26
  ];
26
27
  for (const [fnName, args] of calls) {
27
28
  reporter[fnName](...args);
@@ -254,10 +254,10 @@ describe('run-test', () => {
254
254
  });
255
255
  test('should throw an error if API or Application key are undefined', () => __awaiter(void 0, void 0, void 0, function* () {
256
256
  process.env = {};
257
- expect(() => runTests.getApiHelper(fixtures_1.ciConfig)).toThrow(new errors_1.CiError('MISSING_APP_KEY'));
258
- yield expect(runTests.executeTests(fixtures_1.mockReporter, fixtures_1.ciConfig)).rejects.toMatchError(new errors_1.CiError('MISSING_APP_KEY'));
259
- expect(() => runTests.getApiHelper(Object.assign(Object.assign({}, fixtures_1.ciConfig), { appKey: 'fakeappkey' }))).toThrow(new errors_1.CiError('MISSING_API_KEY'));
260
- yield expect(runTests.executeTests(fixtures_1.mockReporter, Object.assign(Object.assign({}, fixtures_1.ciConfig), { appKey: 'fakeappkey' }))).rejects.toMatchError(new errors_1.CiError('MISSING_API_KEY'));
257
+ expect(() => runTests.getApiHelper(fixtures_1.ciConfig)).toThrow(new errors_1.CriticalError('MISSING_APP_KEY'));
258
+ yield expect(runTests.executeTests(fixtures_1.mockReporter, fixtures_1.ciConfig)).rejects.toMatchError(new errors_1.CriticalError('MISSING_APP_KEY'));
259
+ expect(() => runTests.getApiHelper(Object.assign(Object.assign({}, fixtures_1.ciConfig), { appKey: 'fakeappkey' }))).toThrow(new errors_1.CriticalError('MISSING_API_KEY'));
260
+ yield expect(runTests.executeTests(fixtures_1.mockReporter, Object.assign(Object.assign({}, fixtures_1.ciConfig), { appKey: 'fakeappkey' }))).rejects.toMatchError(new errors_1.CriticalError('MISSING_API_KEY'));
261
261
  }));
262
262
  });
263
263
  describe('getTestsList', () => {
@@ -36,7 +36,9 @@ jest.mock('fs');
36
36
  const fs = __importStar(require("fs"));
37
37
  const axios_1 = __importDefault(require("axios"));
38
38
  const glob_1 = __importDefault(require("glob"));
39
+ const ciHelpers = __importStar(require("../../../helpers/ci"));
39
40
  const api_1 = require("../api");
41
+ const errors_1 = require("../errors");
40
42
  const interfaces_1 = require("../interfaces");
41
43
  const utils = __importStar(require("../utils"));
42
44
  const fixtures_1 = require("./fixtures");
@@ -82,6 +84,31 @@ describe('utils', () => {
82
84
  const output = yield utils.runTests(api, [{ public_id: fakeId, executionRule: interfaces_1.ExecutionRule.NON_BLOCKING }]);
83
85
  expect(output).toEqual(fakeTrigger);
84
86
  }));
87
+ test('runTests sends batch metadata', () => __awaiter(void 0, void 0, void 0, function* () {
88
+ jest.spyOn(ciHelpers, 'getCIMetadata').mockImplementation(() => undefined);
89
+ const payloadMetadataSpy = jest.fn();
90
+ const axiosMock = jest.spyOn(axios_1.default, 'create');
91
+ axiosMock.mockImplementation((() => (e) => {
92
+ payloadMetadataSpy(e.data.metadata);
93
+ if (e.url === '/synthetics/tests/trigger/ci') {
94
+ return { data: fakeTrigger };
95
+ }
96
+ }));
97
+ yield utils.runTests(api, [{ public_id: fakeId, executionRule: interfaces_1.ExecutionRule.NON_BLOCKING }]);
98
+ expect(payloadMetadataSpy).toHaveBeenCalledWith({
99
+ ci: { job: {}, pipeline: {}, provider: {}, stage: {} },
100
+ git: { commit: { author: {}, committer: {} } },
101
+ trigger_app: 'npm_package',
102
+ });
103
+ const metadata = {
104
+ ci: { job: { name: 'job' }, pipeline: {}, provider: { name: 'jest' }, stage: {} },
105
+ git: { commit: { author: {}, committer: {}, message: 'test' } },
106
+ };
107
+ jest.spyOn(ciHelpers, 'getCIMetadata').mockImplementation(() => metadata);
108
+ utils.setCiTriggerApp('unit_test');
109
+ yield utils.runTests(api, [{ public_id: fakeId, executionRule: interfaces_1.ExecutionRule.NON_BLOCKING }]);
110
+ expect(payloadMetadataSpy).toHaveBeenCalledWith(Object.assign(Object.assign({}, metadata), { trigger_app: 'unit_test' }));
111
+ }));
85
112
  test('should run test with publicId from url', () => __awaiter(void 0, void 0, void 0, function* () {
86
113
  const axiosMock = jest.spyOn(axios_1.default, 'create');
87
114
  axiosMock.mockImplementation((() => (e) => {
@@ -137,6 +164,9 @@ describe('utils', () => {
137
164
  if (fakeTests[publicId]) {
138
165
  return { data: fakeTests[publicId] };
139
166
  }
167
+ const error = new Error('Not found');
168
+ error.status = 404;
169
+ throw error;
140
170
  }));
141
171
  });
142
172
  afterAll(() => {
@@ -165,7 +195,7 @@ describe('utils', () => {
165
195
  expect(summary).toEqual(expectedSummary);
166
196
  }));
167
197
  test('no tests triggered throws an error', () => __awaiter(void 0, void 0, void 0, function* () {
168
- yield expect(utils.getTestsToTrigger(api, [], fixtures_1.mockReporter)).rejects.toEqual(new Error('No tests to trigger'));
198
+ yield expect(utils.getTestsToTrigger(api, [], fixtures_1.mockReporter)).rejects.toEqual(new errors_1.CiError('NO_TESTS_TO_RUN'));
169
199
  }));
170
200
  });
171
201
  describe('handleConfig', () => {
@@ -9,6 +9,7 @@ export declare class EndpointError extends Error {
9
9
  constructor(message: string, status: number);
10
10
  }
11
11
  export declare const formatBackendErrors: (requestError: AxiosError<BackendError>) => string;
12
+ export declare const isNotFoundError: (error: AxiosError | EndpointError) => boolean;
12
13
  export declare const is5xxError: (error: AxiosError | EndpointError) => boolean | 0 | undefined;
13
14
  export declare const apiConstructor: (configuration: APIConfiguration) => {
14
15
  getPresignedURL: (testIds: string[]) => Promise<{
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.apiConstructor = exports.is5xxError = exports.formatBackendErrors = exports.EndpointError = void 0;
12
+ exports.apiConstructor = exports.is5xxError = exports.isNotFoundError = exports.formatBackendErrors = exports.EndpointError = void 0;
13
13
  const querystring_1 = require("querystring");
14
14
  const utils_1 = require("../../helpers/utils");
15
15
  const utils_2 = require("./utils");
@@ -87,9 +87,11 @@ const retryOn5xxErrors = (retries, error) => {
87
87
  return 500;
88
88
  }
89
89
  };
90
+ const getErrorHttpStatus = (error) => { var _a; return 'status' in error ? error.status : (_a = error.response) === null || _a === void 0 ? void 0 : _a.status; };
91
+ const isNotFoundError = (error) => getErrorHttpStatus(error) === 404;
92
+ exports.isNotFoundError = isNotFoundError;
90
93
  const is5xxError = (error) => {
91
- var _a;
92
- const statusCode = 'status' in error ? error.status : (_a = error.response) === null || _a === void 0 ? void 0 : _a.status;
94
+ const statusCode = getErrorHttpStatus(error);
93
95
  return statusCode && statusCode >= 500 && statusCode <= 599;
94
96
  };
95
97
  exports.is5xxError = is5xxError;
@@ -68,8 +68,14 @@ class RunTestCommand extends clipanion_1.Command {
68
68
  catch (error) {
69
69
  if (error instanceof errors_1.CiError) {
70
70
  this.reportCiError(error, this.reporter);
71
- if (error instanceof errors_1.CriticalError && this.config.failOnCriticalErrors) {
72
- return 1;
71
+ if (error instanceof errors_1.CriticalError) {
72
+ if (this.config.failOnCriticalErrors) {
73
+ return 1;
74
+ }
75
+ else {
76
+ this.reporter.error(chalk_1.default.yellow('Because `failOnCriticalErrors` is not set or disabled, the command will exit with an error code 0. ' +
77
+ 'Use `failOnCriticalErrors: true` to exit with an error code 1.\n'));
78
+ }
73
79
  }
74
80
  }
75
81
  return 0;
@@ -128,12 +134,14 @@ class RunTestCommand extends clipanion_1.Command {
128
134
  }
129
135
  reportCiError(error, reporter) {
130
136
  switch (error.code) {
137
+ // Non critical errors
131
138
  case 'NO_RESULTS_TO_POLL':
132
139
  reporter.log('No results to poll.\n');
133
140
  break;
134
141
  case 'NO_TESTS_TO_RUN':
135
142
  reporter.log('No test to run.\n');
136
143
  break;
144
+ // Critical command errors
137
145
  case 'MISSING_APP_KEY':
138
146
  reporter.error(`Missing ${chalk_1.default.red.bold('DATADOG_APP_KEY')} in your environment.\n`);
139
147
  break;
@@ -144,16 +152,16 @@ class RunTestCommand extends clipanion_1.Command {
144
152
  reporter.error(`\n${chalk_1.default.bgRed.bold(' ERROR: unable to poll test results ')}\n${error.message}\n\n`);
145
153
  break;
146
154
  case 'TUNNEL_START_FAILED':
147
- reporter.error(`\n${chalk_1.default.bgRed.bold(' ERROR: unable to start tunnel')}\n${error.message}\n\n`);
155
+ reporter.error(`\n${chalk_1.default.bgRed.bold(' ERROR: unable to start tunnel ')}\n${error.message}\n\n`);
148
156
  break;
149
157
  case 'TRIGGER_TESTS_FAILED':
150
- reporter.error(`\n${chalk_1.default.bgRed.bold(' ERROR: unable to trigger tests')}\n${error.message}\n\n`);
158
+ reporter.error(`\n${chalk_1.default.bgRed.bold(' ERROR: unable to trigger tests ')}\n${error.message}\n\n`);
151
159
  break;
152
160
  case 'UNAVAILABLE_TEST_CONFIG':
153
161
  reporter.error(`\n${chalk_1.default.bgRed.bold(' ERROR: unable to obtain test configurations with search query ')}\n${error.message}\n\n`);
154
162
  break;
155
163
  case 'UNAVAILABLE_TUNNEL_CONFIG':
156
- reporter.error(`\n${chalk_1.default.bgRed.bold(' ERROR: unable to get tunnel configuration')}\n${error.message}\n\n`);
164
+ reporter.error(`\n${chalk_1.default.bgRed.bold(' ERROR: unable to get tunnel configuration ')}\n${error.message}\n\n`);
157
165
  }
158
166
  }
159
167
  resolveConfig() {
@@ -1,5 +1,6 @@
1
+ import { ParsedKey } from 'ssh2-streams';
1
2
  export declare const generateOpenSSHKeys: () => {
2
3
  privateKey: string;
3
4
  publicKey: string;
4
5
  };
5
- export declare const parseSSHKey: (key: string) => import("ssh2-streams").ParsedKey;
6
+ export declare const parseSSHKey: (key: string) => ParsedKey;