@datadog/datadog-ci 0.17.4 → 0.17.7

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 (130) hide show
  1. package/LICENSE-3rdparty.csv +1 -0
  2. package/dist/commands/dependencies/__tests__/helpers/context.d.ts +12 -0
  3. package/dist/commands/dependencies/__tests__/helpers/stream.d.ts +2 -0
  4. package/dist/commands/dependencies/__tests__/helpers/upload.run.d.ts +13 -0
  5. package/dist/commands/dependencies/__tests__/upload.test.d.ts +1 -0
  6. package/dist/commands/dependencies/api.d.ts +2 -0
  7. package/dist/commands/dependencies/index.d.ts +1 -0
  8. package/dist/commands/dependencies/interfaces.d.ts +10 -0
  9. package/dist/commands/dependencies/renderer.d.ts +13 -0
  10. package/dist/commands/dependencies/upload.d.ts +16 -0
  11. package/dist/commands/dsyms/__tests__/upload.test.d.ts +1 -0
  12. package/dist/commands/dsyms/__tests__/utils.test.d.ts +1 -0
  13. package/dist/commands/dsyms/index.d.ts +1 -0
  14. package/dist/commands/dsyms/interfaces.d.ts +7 -0
  15. package/dist/commands/dsyms/renderer.d.ts +9 -0
  16. package/dist/commands/dsyms/upload.d.ts +11 -0
  17. package/dist/commands/dsyms/utils.d.ts +9 -0
  18. package/dist/commands/git-metadata/__tests__/git.test.d.ts +1 -0
  19. package/dist/commands/{commit → git-metadata}/__tests__/git.test.js +0 -0
  20. package/dist/commands/git-metadata/__tests__/upload.test.d.ts +1 -0
  21. package/dist/commands/{commit → git-metadata}/__tests__/upload.test.js +1 -1
  22. package/dist/commands/git-metadata/api.d.ts +3 -0
  23. package/dist/commands/{commit → git-metadata}/api.js +0 -0
  24. package/dist/commands/git-metadata/git.d.ts +8 -0
  25. package/dist/commands/{commit → git-metadata}/git.js +0 -0
  26. package/dist/commands/git-metadata/index.d.ts +1 -0
  27. package/dist/commands/{commit → git-metadata}/index.js +0 -0
  28. package/dist/commands/git-metadata/interfaces.d.ts +9 -0
  29. package/dist/commands/{commit → git-metadata}/interfaces.js +0 -0
  30. package/dist/commands/git-metadata/renderer.d.ts +8 -0
  31. package/dist/commands/{commit → git-metadata}/renderer.js +5 -8
  32. package/dist/commands/git-metadata/upload.d.ts +12 -0
  33. package/dist/commands/{commit → git-metadata}/upload.js +8 -3
  34. package/dist/commands/junit/__tests__/api.test.d.ts +1 -0
  35. package/dist/commands/junit/__tests__/upload.test.d.ts +1 -0
  36. package/dist/commands/junit/api.d.ts +8 -0
  37. package/dist/commands/junit/index.d.ts +1 -0
  38. package/dist/commands/junit/interfaces.d.ts +12 -0
  39. package/dist/commands/junit/renderer.d.ts +8 -0
  40. package/dist/commands/junit/upload.d.ts +15 -0
  41. package/dist/commands/junit/upload.js +8 -25
  42. package/dist/commands/junit/utils.d.ts +1 -0
  43. package/dist/commands/lambda/__tests__/function.test.d.ts +1 -0
  44. package/dist/commands/lambda/__tests__/instrument.test.d.ts +1 -0
  45. package/dist/commands/lambda/__tests__/loggroup.test.d.ts +1 -0
  46. package/dist/commands/lambda/__tests__/tags.test.d.ts +1 -0
  47. package/dist/commands/lambda/constants.d.ts +43 -0
  48. package/dist/commands/lambda/function.d.ts +43 -0
  49. package/dist/commands/lambda/index.d.ts +1 -0
  50. package/dist/commands/lambda/instrument.d.ts +28 -0
  51. package/dist/commands/lambda/interfaces.d.ts +16 -0
  52. package/dist/commands/lambda/loggroup.d.ts +17 -0
  53. package/dist/commands/lambda/tags.d.ts +7 -0
  54. package/dist/commands/sourcemaps/__tests__/git.test.d.ts +1 -0
  55. package/dist/commands/sourcemaps/__tests__/upload.test.d.ts +1 -0
  56. package/dist/commands/sourcemaps/__tests__/utils.test.d.ts +1 -0
  57. package/dist/commands/sourcemaps/git.d.ts +20 -0
  58. package/dist/commands/sourcemaps/index.d.ts +1 -0
  59. package/dist/commands/sourcemaps/interfaces.d.ts +15 -0
  60. package/dist/commands/sourcemaps/renderer.d.ts +12 -0
  61. package/dist/commands/sourcemaps/upload.d.ts +25 -0
  62. package/dist/commands/sourcemaps/utils.d.ts +3 -0
  63. package/dist/commands/sourcemaps/validation.d.ts +6 -0
  64. package/dist/commands/synthetics/__tests__/api.test.d.ts +1 -0
  65. package/dist/commands/synthetics/__tests__/cli.test.d.ts +1 -0
  66. package/dist/commands/synthetics/__tests__/cli.test.js +232 -0
  67. package/dist/commands/synthetics/__tests__/crypto.test.d.ts +1 -0
  68. package/dist/commands/synthetics/__tests__/fixtures.d.ts +71 -0
  69. package/dist/commands/synthetics/__tests__/fixtures.js +17 -1
  70. package/dist/commands/synthetics/__tests__/reporters/default.test.d.ts +1 -0
  71. package/dist/commands/synthetics/__tests__/reporters/default.test.js +2 -2
  72. package/dist/commands/synthetics/__tests__/reporters/junit.test.d.ts +1 -0
  73. package/dist/commands/synthetics/__tests__/reporters/junit.test.js +16 -14
  74. package/dist/commands/synthetics/__tests__/run-test.test.d.ts +1 -0
  75. package/dist/commands/synthetics/__tests__/run-test.test.js +42 -313
  76. package/dist/commands/synthetics/__tests__/tunnel.test.d.ts +1 -0
  77. package/dist/commands/synthetics/__tests__/utils.test.d.ts +1 -0
  78. package/dist/commands/synthetics/__tests__/utils.test.js +4 -0
  79. package/dist/commands/synthetics/__tests__/websocket.test.d.ts +1 -0
  80. package/dist/commands/synthetics/__tests__/websocket.test.js +6 -5
  81. package/dist/commands/synthetics/api.d.ts +24 -0
  82. package/dist/commands/synthetics/cli.d.ts +26 -0
  83. package/dist/commands/synthetics/cli.js +229 -0
  84. package/dist/commands/synthetics/crypto.d.ts +5 -0
  85. package/dist/commands/synthetics/errors.d.ts +9 -0
  86. package/dist/commands/synthetics/errors.js +25 -0
  87. package/dist/commands/synthetics/index.d.ts +1 -0
  88. package/dist/commands/synthetics/index.js +2 -2
  89. package/dist/commands/synthetics/interfaces.d.ts +342 -0
  90. package/dist/commands/synthetics/reporters/default.d.ts +18 -0
  91. package/dist/commands/synthetics/reporters/junit.d.ts +95 -0
  92. package/dist/commands/synthetics/reporters/junit.js +45 -19
  93. package/dist/commands/synthetics/run-test.d.ts +74 -0
  94. package/dist/commands/synthetics/run-test.js +135 -319
  95. package/dist/commands/synthetics/tunnel.d.ts +43 -0
  96. package/dist/commands/synthetics/utils.d.ts +26 -0
  97. package/dist/commands/synthetics/utils.js +11 -1
  98. package/dist/commands/synthetics/websocket.d.ts +38 -0
  99. package/dist/commands/trace/__tests__/trace.test.d.ts +1 -0
  100. package/dist/commands/trace/api.d.ts +6 -0
  101. package/dist/commands/trace/index.d.ts +1 -0
  102. package/dist/commands/trace/interfaces.d.ts +23 -0
  103. package/dist/commands/trace/trace.d.ts +17 -0
  104. package/dist/commands/trace/trace.js +32 -3
  105. package/dist/helpers/__tests__/ci.test.d.ts +1 -0
  106. package/dist/helpers/__tests__/ci.test.js +1 -1
  107. package/dist/helpers/__tests__/git.test.d.ts +1 -0
  108. package/dist/helpers/__tests__/retry.test.d.ts +1 -0
  109. package/dist/helpers/__tests__/retry.test.js +98 -0
  110. package/dist/helpers/__tests__/tags.test.d.ts +1 -0
  111. package/dist/helpers/__tests__/upload.test.d.ts +1 -0
  112. package/dist/helpers/__tests__/user-provided-git.test.d.ts +1 -0
  113. package/dist/helpers/__tests__/utils.test.d.ts +1 -0
  114. package/dist/helpers/apikey.d.ts +14 -0
  115. package/dist/helpers/ci.d.ts +15 -0
  116. package/dist/helpers/ci.js +3 -3
  117. package/dist/helpers/errors.d.ts +2 -0
  118. package/dist/helpers/formatting.d.ts +5 -0
  119. package/dist/helpers/git.d.ts +2 -0
  120. package/dist/helpers/interfaces.d.ts +19 -0
  121. package/dist/helpers/metrics.d.ts +11 -0
  122. package/dist/helpers/retry.d.ts +2 -0
  123. package/dist/helpers/retry.js +38 -0
  124. package/dist/helpers/tags.d.ts +28 -0
  125. package/dist/helpers/upload.d.ts +44 -0
  126. package/dist/helpers/upload.js +4 -26
  127. package/dist/helpers/user-provided-git.d.ts +1 -0
  128. package/dist/helpers/utils.d.ts +31 -0
  129. package/dist/index.d.ts +1 -0
  130. package/package.json +4 -3
@@ -8,336 +8,152 @@ 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
- exports.removeUndefinedValues = exports.RunTestCommand = exports.DEFAULT_COMMAND_CONFIG = void 0;
16
- const chalk_1 = __importDefault(require("chalk"));
17
- const clipanion_1 = require("clipanion");
18
- const deep_extend_1 = __importDefault(require("deep-extend"));
19
- const utils_1 = require("../../helpers/utils");
12
+ exports.getDatadogHost = exports.getApiHelper = exports.getTestsList = exports.executeTests = void 0;
20
13
  const api_1 = require("./api");
21
- const interfaces_1 = require("./interfaces");
22
- const default_1 = require("./reporters/default");
23
- const junit_1 = require("./reporters/junit");
14
+ const errors_1 = require("./errors");
24
15
  const tunnel_1 = require("./tunnel");
25
- const utils_2 = require("./utils");
26
- exports.DEFAULT_COMMAND_CONFIG = {
27
- apiKey: '',
28
- appKey: '',
29
- configPath: 'datadog-ci.json',
30
- datadogSite: 'datadoghq.com',
31
- failOnCriticalErrors: false,
32
- failOnTimeout: true,
33
- files: ['{,!(node_modules)/**/}*.synthetics.json'],
34
- global: {},
35
- locations: [],
36
- pollingTimeout: 2 * 60 * 1000,
37
- proxy: { protocol: 'http' },
38
- publicIds: [],
39
- subdomain: 'app',
40
- tunnel: false,
41
- };
42
- class RunTestCommand extends clipanion_1.Command {
43
- constructor() {
44
- super(...arguments);
45
- this.config = JSON.parse(JSON.stringify(exports.DEFAULT_COMMAND_CONFIG)); // Deep copy to avoid mutation during unit tests
46
- }
47
- execute() {
48
- var _a;
49
- return __awaiter(this, void 0, void 0, function* () {
50
- const reporters = [new default_1.DefaultReporter(this)];
51
- if (this.jUnitReport) {
52
- reporters.push(new junit_1.JUnitReporter(this));
53
- }
54
- this.reporter = utils_2.getReporter(reporters);
55
- yield this.resolveConfig();
56
- const startTime = Date.now();
57
- const api = this.getApiHelper();
58
- const publicIdsFromCli = this.config.publicIds.map((id) => ({ suite: 'CLI Suite', config: this.config.global, id }));
59
- let testsToTrigger;
60
- let tunnel;
61
- const safeExit = (exitCode) => __awaiter(this, void 0, void 0, function* () {
62
- if (tunnel) {
63
- yield tunnel.stop();
64
- }
65
- return exitCode;
66
- });
67
- if (publicIdsFromCli.length) {
68
- testsToTrigger = publicIdsFromCli;
69
- }
70
- else {
71
- try {
72
- testsToTrigger = yield this.getTestsList(api);
73
- }
74
- catch (error) {
75
- this.reporter.error(`\n${chalk_1.default.bgRed.bold(' ERROR: unable to obtain test configurations with search query ')}\n${error.message}\n\n`);
76
- if (api_1.is5xxError(error) && !this.config.failOnCriticalErrors) {
77
- return safeExit(0);
78
- }
79
- return safeExit(1);
80
- }
81
- }
82
- if (!testsToTrigger.length) {
83
- this.reporter.log('No test suites to run.\n');
84
- return safeExit(0);
85
- }
86
- let testsToTriggerResult;
87
- try {
88
- testsToTriggerResult = yield utils_2.getTestsToTrigger(api, testsToTrigger, this.reporter);
89
- }
90
- catch (error) {
91
- this.reporter.error(`\n${chalk_1.default.bgRed.bold(' ERROR: unable to obtain test configurations ')}\n${error.message}\n\n`);
92
- if (api_1.is5xxError(error) && !this.config.failOnCriticalErrors) {
93
- return safeExit(0);
94
- }
95
- return safeExit(1);
96
- }
97
- const { tests, overriddenTestsToTrigger, summary } = testsToTriggerResult;
98
- // All tests have been skipped or are missing.
99
- if (!tests.length) {
100
- this.reporter.log('No test to run.\n');
101
- return safeExit(0);
102
- }
103
- const publicIdsToTrigger = tests.map(({ public_id }) => public_id);
104
- if (this.config.tunnel) {
105
- this.reporter.log('You are using tunnel option, the chosen location(s) will be overridden by a location in your account region.\n');
106
- let presignedURL;
107
- try {
108
- // Get the pre-signed URL to connect to the tunnel service
109
- presignedURL = (yield api.getPresignedURL(publicIdsToTrigger)).url;
110
- }
111
- catch (e) {
112
- this.reporter.error(`\n${chalk_1.default.bgRed.bold(' ERROR: unable to get tunnel configuration')}\n${e.message}\n\n`);
113
- if (api_1.is5xxError(e) && !this.config.failOnCriticalErrors) {
114
- return safeExit(0);
115
- }
116
- return safeExit(1);
117
- }
118
- // Open a tunnel to Datadog
119
- try {
120
- tunnel = new tunnel_1.Tunnel(presignedURL, publicIdsToTrigger, this.config.proxy, this.reporter);
121
- const tunnelInfo = yield tunnel.start();
122
- overriddenTestsToTrigger.forEach((testToTrigger) => {
123
- testToTrigger.tunnel = tunnelInfo;
124
- });
125
- }
126
- catch (e) {
127
- this.reporter.error(`\n${chalk_1.default.bgRed.bold(' ERROR: unable to start tunnel ')}\n${e.message}\n\n`);
128
- if (api_1.is5xxError(e) && !this.config.failOnCriticalErrors) {
129
- return safeExit(0);
130
- }
131
- return safeExit(1);
132
- }
133
- }
134
- let triggers;
135
- try {
136
- triggers = yield utils_2.runTests(api, overriddenTestsToTrigger);
137
- }
138
- catch (e) {
139
- this.reporter.error(`\n${chalk_1.default.bgRed.bold(' ERROR: unable to trigger tests ')}\n${e.message}\n\n`);
140
- if (api_1.is5xxError(e) && !this.config.failOnCriticalErrors) {
141
- return safeExit(0);
142
- }
143
- return safeExit(1);
144
- }
145
- if (!triggers.results) {
146
- throw new Error('No result to poll.');
147
- }
148
- const results = {};
149
- try {
150
- // Poll the results.
151
- const resultPolled = yield utils_2.waitForResults(api, triggers.results, this.config.pollingTimeout, testsToTrigger, tunnel, this.config.failOnCriticalErrors);
152
- Object.assign(results, resultPolled);
153
- }
154
- catch (error) {
155
- this.reporter.error(`\n${chalk_1.default.bgRed.bold(' ERROR: unable to poll test results ')}\n${error.message}\n\n`);
156
- if (api_1.is5xxError(error) && !this.config.failOnCriticalErrors) {
157
- return safeExit(0);
158
- }
159
- return safeExit(1);
160
- }
161
- // Sort tests to show success first then non blocking failures and finally blocking failures.
162
- tests.sort(this.sortTestsByOutcome(results));
163
- // Rendering the results.
164
- this.reporter.reportStart({ startTime });
165
- const locationNames = triggers.locations.reduce((mapping, location) => {
166
- mapping[location.id] = location.display_name;
167
- return mapping;
168
- }, {});
169
- let hasSucceeded = true; // Determine if all the tests have succeeded
170
- for (const test of tests) {
171
- const testResults = results[test.public_id];
172
- if (!this.config.failOnTimeout) {
173
- if (!summary.timedOut) {
174
- summary.timedOut = 0;
175
- }
176
- const hasTimeout = testResults.some((pollResult) => pollResult.result.error === interfaces_1.ERRORS.TIMEOUT);
177
- if (hasTimeout) {
178
- summary.timedOut++;
179
- }
180
- }
181
- if (!this.config.failOnCriticalErrors) {
182
- if (!summary.criticalErrors) {
183
- summary.criticalErrors = 0;
184
- }
185
- const hasCriticalErrors = testResults.some((pollResult) => utils_2.isCriticalError(pollResult.result));
186
- if (hasCriticalErrors) {
187
- summary.criticalErrors++;
188
- }
189
- }
190
- const passed = utils_2.hasTestSucceeded(testResults, this.config.failOnCriticalErrors, this.config.failOnTimeout);
191
- if (passed) {
192
- summary.passed++;
193
- }
194
- else {
195
- summary.failed++;
196
- if (((_a = test.options.ci) === null || _a === void 0 ? void 0 : _a.executionRule) !== interfaces_1.ExecutionRule.NON_BLOCKING) {
197
- hasSucceeded = false;
198
- }
199
- }
200
- this.reporter.testEnd(test, testResults, this.getAppBaseURL(), locationNames, this.config.failOnCriticalErrors, this.config.failOnTimeout);
201
- }
202
- this.reporter.runEnd(summary);
203
- return safeExit(hasSucceeded ? 0 : 1);
204
- });
16
+ const utils_1 = require("./utils");
17
+ const executeTests = (reporter, config) => __awaiter(void 0, void 0, void 0, function* () {
18
+ const api = exports.getApiHelper(config);
19
+ const publicIdsFromCli = config.publicIds.map((id) => ({ config: config.global, id }));
20
+ let testsToTrigger;
21
+ let tunnel;
22
+ const stopTunnel = () => __awaiter(void 0, void 0, void 0, function* () {
23
+ if (tunnel) {
24
+ yield tunnel.stop();
25
+ }
26
+ });
27
+ if (publicIdsFromCli.length) {
28
+ testsToTrigger = publicIdsFromCli;
205
29
  }
206
- getApiHelper() {
207
- if (!this.config.appKey || !this.config.apiKey) {
208
- if (!this.config.appKey) {
209
- this.reporter.error(`Missing ${chalk_1.default.red.bold('DATADOG_APP_KEY')} in your environment.\n`);
210
- }
211
- if (!this.config.apiKey) {
212
- this.reporter.error(`Missing ${chalk_1.default.red.bold('DATADOG_API_KEY')} in your environment.\n`);
213
- }
214
- throw new Error('API and/or Application keys are missing');
30
+ else {
31
+ try {
32
+ testsToTrigger = yield exports.getTestsList(api, config, reporter);
215
33
  }
216
- return api_1.apiConstructor({
217
- apiKey: this.config.apiKey,
218
- appKey: this.config.appKey,
219
- baseIntakeUrl: this.getDatadogHost(true),
220
- baseUrl: this.getDatadogHost(),
221
- proxyOpts: this.config.proxy,
222
- });
34
+ catch (error) {
35
+ const isCriticalError = api_1.is5xxError(error);
36
+ throw new (isCriticalError ? errors_1.CriticalError : errors_1.CiError)('UNAVAILABLE_TEST_CONFIG');
37
+ }
38
+ }
39
+ if (!testsToTrigger.length) {
40
+ throw new errors_1.CiError('NO_TESTS_TO_RUN');
41
+ }
42
+ let testsToTriggerResult;
43
+ try {
44
+ testsToTriggerResult = yield utils_1.getTestsToTrigger(api, testsToTrigger, reporter);
223
45
  }
224
- getAppBaseURL() {
225
- return `https://${this.config.subdomain}.${this.config.datadogSite}/`;
46
+ catch (error) {
47
+ const isCriticalError = api_1.is5xxError(error);
48
+ throw new (isCriticalError ? errors_1.CriticalError : errors_1.CiError)('UNAVAILABLE_TEST_CONFIG');
226
49
  }
227
- getDatadogHost(useIntake = false) {
228
- const apiPath = 'api/v1';
229
- let host = `https://api.${this.config.datadogSite}`;
230
- const hostOverride = process.env.DD_API_HOST_OVERRIDE;
231
- if (hostOverride) {
232
- host = hostOverride;
50
+ const { tests, overriddenTestsToTrigger, summary } = testsToTriggerResult;
51
+ // All tests have been skipped or are missing.
52
+ if (!tests.length) {
53
+ throw new errors_1.CiError('NO_TESTS_TO_RUN');
54
+ }
55
+ const publicIdsToTrigger = tests.map(({ public_id }) => public_id);
56
+ if (config.tunnel) {
57
+ let presignedURL;
58
+ try {
59
+ // Get the pre-signed URL to connect to the tunnel service
60
+ presignedURL = (yield api.getPresignedURL(publicIdsToTrigger)).url;
61
+ }
62
+ catch (error) {
63
+ const isCriticalError = api_1.is5xxError(error);
64
+ throw new (isCriticalError ? errors_1.CriticalError : errors_1.CiError)('UNAVAILABLE_TUNNEL_CONFIG');
233
65
  }
234
- else if (useIntake &&
235
- (this.config.datadogSite === 'datadoghq.com' || this.config.datadogSite === 'datad0g.com')) {
236
- host = `https://intake.synthetics.${this.config.datadogSite}`;
66
+ // Open a tunnel to Datadog
67
+ try {
68
+ tunnel = new tunnel_1.Tunnel(presignedURL, publicIdsToTrigger, config.proxy, reporter);
69
+ const tunnelInfo = yield tunnel.start();
70
+ overriddenTestsToTrigger.forEach((testToTrigger) => {
71
+ testToTrigger.tunnel = tunnelInfo;
72
+ });
73
+ }
74
+ catch (error) {
75
+ const isCriticalError = api_1.is5xxError(error);
76
+ yield stopTunnel();
77
+ throw new (isCriticalError ? errors_1.CriticalError : errors_1.CiError)('TUNNEL_START_FAILED');
237
78
  }
238
- return `${host}/${apiPath}`;
239
79
  }
240
- getTestsList(api) {
241
- var _a;
242
- return __awaiter(this, void 0, void 0, function* () {
243
- if (this.config.testSearchQuery) {
244
- const testSearchResults = yield api.searchTests(this.config.testSearchQuery);
245
- return testSearchResults.tests.map((test) => ({
246
- config: this.config.global,
247
- id: test.public_id,
248
- suite: `Query: ${this.testSearchQuery}`,
249
- }));
250
- }
251
- const suites = (yield Promise.all(this.config.files.map((glob) => utils_2.getSuites(glob, this.reporter))))
252
- .reduce((acc, val) => acc.concat(val), [])
253
- .filter((suite) => !!suite.content.tests);
254
- const configFromEnvironment = ((_a = this.config.locations) === null || _a === void 0 ? void 0 : _a.length) ? { locations: this.config.locations } : {};
255
- const testsToTrigger = suites
256
- .map((suite) => suite.content.tests.map((test) => ({
257
- config: Object.assign(Object.assign(Object.assign({}, this.config.global), configFromEnvironment), test.config),
258
- id: test.id,
259
- suite: suite.name,
260
- })))
261
- .reduce((acc, suiteTests) => acc.concat(suiteTests), []);
262
- return testsToTrigger;
263
- });
80
+ let triggers;
81
+ try {
82
+ triggers = yield utils_1.runTests(api, overriddenTestsToTrigger);
83
+ }
84
+ catch (error) {
85
+ const isCriticalError = api_1.is5xxError(error);
86
+ yield stopTunnel();
87
+ throw new (isCriticalError ? errors_1.CriticalError : errors_1.CiError)('TRIGGER_TESTS_FAILED');
88
+ }
89
+ if (!triggers.results) {
90
+ yield stopTunnel();
91
+ throw new errors_1.CriticalError('NO_RESULTS_TO_POLL');
92
+ }
93
+ const results = {};
94
+ try {
95
+ // Poll the results.
96
+ const resultPolled = yield utils_1.waitForResults(api, triggers.results, config.pollingTimeout, testsToTrigger, tunnel, config.failOnCriticalErrors);
97
+ Object.assign(results, resultPolled);
98
+ }
99
+ catch (error) {
100
+ const isCriticalError = api_1.is5xxError(error);
101
+ yield stopTunnel();
102
+ throw new (isCriticalError ? errors_1.CriticalError : errors_1.CiError)('POLL_RESULTS_FAILED');
264
103
  }
265
- resolveConfig() {
266
- var _a, _b;
267
- return __awaiter(this, void 0, void 0, function* () {
268
- // Default < file < ENV < CLI
269
- // Override with file config variables
270
- try {
271
- this.config = yield utils_1.parseConfigFile(this.config, (_a = this.configPath) !== null && _a !== void 0 ? _a : this.config.configPath);
272
- }
273
- catch (error) {
274
- if (this.configPath) {
275
- throw error;
276
- }
277
- }
278
- // Override with ENV variables
279
- this.config = deep_extend_1.default(this.config, exports.removeUndefinedValues({
280
- apiKey: process.env.DATADOG_API_KEY,
281
- appKey: process.env.DATADOG_APP_KEY,
282
- datadogSite: process.env.DATADOG_SITE,
283
- locations: (_b = process.env.DATADOG_SYNTHETICS_LOCATIONS) === null || _b === void 0 ? void 0 : _b.split(';'),
284
- subdomain: process.env.DATADOG_SUBDOMAIN,
285
- }));
286
- // Override with CLI parameters
287
- this.config = deep_extend_1.default(this.config, exports.removeUndefinedValues({
288
- apiKey: this.apiKey,
289
- appKey: this.appKey,
290
- configPath: this.configPath,
291
- datadogSite: this.datadogSite,
292
- failOnCriticalErrors: this.failOnCriticalErrors,
293
- failOnTimeout: this.failOnTimeout,
294
- files: this.files,
295
- publicIds: this.publicIds,
296
- subdomain: this.subdomain,
297
- testSearchQuery: this.testSearchQuery,
298
- tunnel: this.tunnel,
299
- }));
300
- if (typeof this.config.files === 'string') {
301
- this.reporter.log('[DEPRECATED] "files" should be an array of string instead of a string.\n');
302
- this.config.files = [this.config.files];
303
- }
304
- });
104
+ return { results, summary, tests, triggers };
105
+ });
106
+ exports.executeTests = executeTests;
107
+ const getTestsList = (api, config, reporter) => __awaiter(void 0, void 0, void 0, function* () {
108
+ var _a;
109
+ if (config.testSearchQuery) {
110
+ const testSearchResults = yield api.searchTests(config.testSearchQuery);
111
+ return testSearchResults.tests.map((test) => ({
112
+ config: config.global,
113
+ id: test.public_id,
114
+ suite: `Query: ${config.testSearchQuery}`,
115
+ }));
116
+ }
117
+ const suites = (yield Promise.all(config.files.map((glob) => utils_1.getSuites(glob, reporter))))
118
+ .reduce((acc, val) => acc.concat(val), [])
119
+ .filter((suite) => !!suite.content.tests);
120
+ const configFromEnvironment = ((_a = config.locations) === null || _a === void 0 ? void 0 : _a.length) ? { locations: config.locations } : {};
121
+ const testsToTrigger = suites
122
+ .map((suite) => suite.content.tests.map((test) => ({
123
+ config: Object.assign(Object.assign(Object.assign({}, config.global), configFromEnvironment), test.config),
124
+ id: test.id,
125
+ suite: suite.name,
126
+ })))
127
+ .reduce((acc, suiteTests) => acc.concat(suiteTests), []);
128
+ return testsToTrigger;
129
+ });
130
+ exports.getTestsList = getTestsList;
131
+ const getApiHelper = (config) => {
132
+ if (!config.appKey) {
133
+ throw new errors_1.CiError('MISSING_APP_KEY');
134
+ }
135
+ if (!config.apiKey) {
136
+ throw new errors_1.CiError('MISSING_API_KEY');
137
+ }
138
+ return api_1.apiConstructor({
139
+ apiKey: config.apiKey,
140
+ appKey: config.appKey,
141
+ baseIntakeUrl: exports.getDatadogHost(true, config),
142
+ baseUrl: exports.getDatadogHost(false, config),
143
+ proxyOpts: config.proxy,
144
+ });
145
+ };
146
+ exports.getApiHelper = getApiHelper;
147
+ const getDatadogHost = (useIntake = false, config) => {
148
+ const apiPath = 'api/v1';
149
+ let host = `https://api.${config.datadogSite}`;
150
+ const hostOverride = process.env.DD_API_HOST_OVERRIDE;
151
+ if (hostOverride) {
152
+ host = hostOverride;
305
153
  }
306
- sortTestsByOutcome(results) {
307
- return (t1, t2) => {
308
- var _a, _b;
309
- const success1 = utils_2.hasTestSucceeded(results[t1.public_id], this.config.failOnCriticalErrors, this.config.failOnTimeout);
310
- const success2 = utils_2.hasTestSucceeded(results[t2.public_id], this.config.failOnCriticalErrors, this.config.failOnTimeout);
311
- const isNonBlockingTest1 = ((_a = t1.options.ci) === null || _a === void 0 ? void 0 : _a.executionRule) === interfaces_1.ExecutionRule.NON_BLOCKING;
312
- const isNonBlockingTest2 = ((_b = t2.options.ci) === null || _b === void 0 ? void 0 : _b.executionRule) === interfaces_1.ExecutionRule.NON_BLOCKING;
313
- if (success1 === success2) {
314
- if (isNonBlockingTest1 === isNonBlockingTest2) {
315
- return 0;
316
- }
317
- return isNonBlockingTest1 ? -1 : 1;
318
- }
319
- return success1 ? -1 : 1;
320
- };
154
+ else if (useIntake && (config.datadogSite === 'datadoghq.com' || config.datadogSite === 'datad0g.com')) {
155
+ host = `https://intake.synthetics.${config.datadogSite}`;
321
156
  }
322
- }
323
- exports.RunTestCommand = RunTestCommand;
324
- const removeUndefinedValues = (object) => {
325
- const newObject = Object.assign({}, object);
326
- Object.keys(newObject).forEach((k) => newObject[k] === undefined && delete newObject[k]);
327
- return newObject;
157
+ return `${host}/${apiPath}`;
328
158
  };
329
- exports.removeUndefinedValues = removeUndefinedValues;
330
- RunTestCommand.addPath('synthetics', 'run-tests');
331
- RunTestCommand.addOption('apiKey', clipanion_1.Command.String('--apiKey'));
332
- RunTestCommand.addOption('appKey', clipanion_1.Command.String('--appKey'));
333
- RunTestCommand.addOption('failOnCriticalErrors', clipanion_1.Command.Boolean('--failOnCriticalErrors'));
334
- RunTestCommand.addOption('configPath', clipanion_1.Command.String('--config'));
335
- RunTestCommand.addOption('datadogSite', clipanion_1.Command.String('--datadogSite'));
336
- RunTestCommand.addOption('files', clipanion_1.Command.Array('-f,--files'));
337
- RunTestCommand.addOption('failOnTimeout', clipanion_1.Command.Boolean('--failOnTimeout'));
338
- RunTestCommand.addOption('publicIds', clipanion_1.Command.Array('-p,--public-id'));
339
- RunTestCommand.addOption('testSearchQuery', clipanion_1.Command.String('-s,--search'));
340
- RunTestCommand.addOption('subdomain', clipanion_1.Command.Boolean('--subdomain'));
341
- RunTestCommand.addOption('tunnel', clipanion_1.Command.Boolean('-t,--tunnel'));
342
- RunTestCommand.addOption('jUnitReport', clipanion_1.Command.String('-j,--jUnitReport'));
343
- RunTestCommand.addOption('runName', clipanion_1.Command.String('-n,--runName'));
159
+ exports.getDatadogHost = getDatadogHost;
@@ -0,0 +1,43 @@
1
+ import { ProxyConfiguration } from '../../helpers/utils';
2
+ import { MainReporter } from './interfaces';
3
+ export interface TunnelInfo {
4
+ host: string;
5
+ id: string;
6
+ privateKey: string;
7
+ }
8
+ export declare class Tunnel {
9
+ private url;
10
+ private testIDs;
11
+ private connected;
12
+ private forwardedSockets;
13
+ private FORWARDING_TIMEOUT;
14
+ private log;
15
+ private logError;
16
+ private logWarning;
17
+ private multiplexer?;
18
+ private privateKey;
19
+ private publicKey;
20
+ private sshConfig;
21
+ private ws;
22
+ constructor(url: string, testIDs: string[], proxy: ProxyConfiguration, reporter: MainReporter);
23
+ /**
24
+ * keepAlive will return a promise that tracks the state of the tunnel (and reject in case of error)
25
+ */
26
+ keepAlive(): Promise<void>;
27
+ /**
28
+ * start the tunnel:
29
+ * - get the pre-signed URL to connect to the tunnel service
30
+ * - Set up SSH
31
+ * - establish a WebSocket connection to the tunnel service
32
+ */
33
+ start(): Promise<TunnelInfo>;
34
+ /**
35
+ * stop the tunnel
36
+ */
37
+ stop(): Promise<void>;
38
+ private authenticateSSHConnection;
39
+ private forwardProxiedPacketsFromSSH;
40
+ private forwardWebSocketToSSH;
41
+ private getConnectionInfo;
42
+ private processSSHStream;
43
+ }
@@ -0,0 +1,26 @@
1
+ import { APIHelper, ConfigOverride, ExecutionRule, InternalTest, MainReporter, PollResult, Reporter, Result, Suite, Summary, TestPayload, Trigger, TriggerConfig, TriggerResponse, TriggerResult } from './interfaces';
2
+ import { Tunnel } from './tunnel';
3
+ export declare const handleConfig: (test: InternalTest, publicId: string, reporter: MainReporter, config?: ConfigOverride | undefined) => TestPayload;
4
+ export declare const getExecutionRule: (test: InternalTest, configOverride?: ConfigOverride | undefined) => ExecutionRule;
5
+ export declare const getStrictestExecutionRule: (configRule: ExecutionRule, testRule?: ExecutionRule | undefined) => ExecutionRule;
6
+ export declare const isCriticalError: (result: Result) => boolean;
7
+ export declare const hasResultPassed: (result: Result, failOnCriticalErrors: boolean, failOnTimeout: boolean) => boolean;
8
+ export declare const hasTestSucceeded: (results: PollResult[], failOnCriticalErrors: boolean, failOnTimeout: boolean) => boolean;
9
+ export declare const getSuites: (GLOB: string, reporter: MainReporter) => Promise<Suite[]>;
10
+ export declare const wait: (duration: number) => Promise<unknown>;
11
+ export declare const waitForResults: (api: APIHelper, triggerResponses: TriggerResponse[], defaultTimeout: number, triggerConfigs: TriggerConfig[], tunnel?: Tunnel | undefined, failOnCriticalErrors?: boolean | undefined) => Promise<{
12
+ [key: string]: PollResult[];
13
+ }>;
14
+ export declare const createTriggerResultMap: (triggerResponses: TriggerResponse[], defaultTimeout: number, triggerConfigs: TriggerConfig[]) => Map<string, TriggerResult>;
15
+ export declare const getResultDuration: (result: Result) => number;
16
+ export declare const getReporter: (reporters: Reporter[]) => MainReporter;
17
+ export declare const getTestsToTrigger: (api: APIHelper, triggerConfigs: TriggerConfig[], reporter: MainReporter) => Promise<{
18
+ tests: InternalTest[];
19
+ overriddenTestsToTrigger: TestPayload[];
20
+ summary: Summary;
21
+ }>;
22
+ export declare const runTests: (api: APIHelper, testsToTrigger: TestPayload[]) => Promise<Trigger>;
23
+ export declare const retry: <T, E extends Error>(func: () => Promise<T>, shouldRetryAfterWait: (retries: number, error: E) => number | undefined) => Promise<T>;
24
+ export declare const removeUndefinedValues: <T extends {
25
+ [key: string]: any;
26
+ }>(object: T) => T;
@@ -31,7 +31,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
31
31
  return (mod && mod.__esModule) ? mod : { "default": mod };
32
32
  };
33
33
  Object.defineProperty(exports, "__esModule", { value: true });
34
- exports.retry = exports.runTests = exports.getTestsToTrigger = exports.getReporter = exports.getResultDuration = exports.createTriggerResultMap = exports.waitForResults = exports.wait = exports.getSuites = exports.hasTestSucceeded = exports.hasResultPassed = exports.isCriticalError = exports.getStrictestExecutionRule = exports.getExecutionRule = exports.handleConfig = void 0;
34
+ exports.removeUndefinedValues = exports.retry = exports.runTests = exports.getTestsToTrigger = exports.getReporter = exports.getResultDuration = exports.createTriggerResultMap = exports.waitForResults = exports.wait = exports.getSuites = exports.hasTestSucceeded = exports.hasResultPassed = exports.isCriticalError = exports.getStrictestExecutionRule = exports.getExecutionRule = exports.handleConfig = void 0;
35
35
  const fs = __importStar(require("fs"));
36
36
  const path = __importStar(require("path"));
37
37
  const url_1 = require("url");
@@ -426,3 +426,13 @@ const retry = (func, shouldRetryAfterWait) => __awaiter(void 0, void 0, void 0,
426
426
  return trier();
427
427
  });
428
428
  exports.retry = retry;
429
+ const removeUndefinedValues = (object) => {
430
+ const newObject = Object.assign({}, object);
431
+ for (const [key, value] of Object.entries(newObject)) {
432
+ if (value === undefined) {
433
+ delete newObject[key];
434
+ }
435
+ }
436
+ return newObject;
437
+ };
438
+ exports.removeUndefinedValues = removeUndefinedValues;
@@ -0,0 +1,38 @@
1
+ /// <reference types="node" />
2
+ import { EventEmitter } from 'events';
3
+ import { default as WebSocketModule } from 'ws';
4
+ import { ProxyConfiguration } from '../../helpers/utils';
5
+ export declare class WebSocket extends EventEmitter {
6
+ private url;
7
+ private proxyOpts;
8
+ private firstMessage?;
9
+ private keepAliveWebsocket?;
10
+ private websocket?;
11
+ constructor(url: string, proxyOpts: ProxyConfiguration);
12
+ /**
13
+ * close will terminate the WebSocket connection
14
+ */
15
+ close(gracefullyClose?: boolean): Promise<void>;
16
+ /**
17
+ * connect will start a WebSocket connection
18
+ */
19
+ connect(): Promise<void>;
20
+ /**
21
+ * duplex will create a duplex stream for the WS connection
22
+ */
23
+ duplex(): import("stream").Duplex;
24
+ /**
25
+ * keepAlive will return a promise to keep track of the tunnel connection
26
+ */
27
+ keepAlive(): Promise<void>;
28
+ /**
29
+ * on allows to listen for WebSocket messages
30
+ */
31
+ on(event: 'message', listener: (data: WebSocketModule.Data) => void): this;
32
+ /**
33
+ * once allows to listen for a WebSocket message
34
+ */
35
+ once(event: 'message', listener: (data: WebSocketModule.Data) => void): this;
36
+ waitForFirstMessage(): Promise<WebSocketModule.Data>;
37
+ private establishWebsocketConnection;
38
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,6 @@
1
+ import type { AxiosPromise, AxiosRequestConfig, AxiosResponse } from 'axios';
2
+ import { Payload } from './interfaces';
3
+ export declare const reportCustomSpan: (request: (args: AxiosRequestConfig) => AxiosPromise<AxiosResponse>) => (customSpan: Payload, provider: string) => Promise<AxiosResponse<AxiosResponse<any>>>;
4
+ export declare const apiConstructor: (baseUrl: string, apiKey: string) => {
5
+ reportCustomSpan: (customSpan: Payload, provider: string) => Promise<AxiosResponse<AxiosResponse<any>>>;
6
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,23 @@
1
+ import { AxiosPromise, AxiosResponse } from 'axios';
2
+ export declare const CIRCLECI = "circleci";
3
+ export declare const JENKINS = "jenkins";
4
+ export declare const SUPPORTED_PROVIDERS: readonly ["circleci", "jenkins"];
5
+ export declare type Provider = typeof SUPPORTED_PROVIDERS[number];
6
+ export interface Payload {
7
+ command: string;
8
+ custom: {
9
+ id: string;
10
+ parent_id?: string;
11
+ };
12
+ data: Record<string, string>;
13
+ end_time: string;
14
+ error_message: string;
15
+ exit_code: number;
16
+ is_error: boolean;
17
+ name: string;
18
+ start_time: string;
19
+ tags: Partial<Record<string, string>>;
20
+ }
21
+ export interface APIHelper {
22
+ reportCustomSpan(customSpan: Payload, provider: Provider): AxiosPromise<AxiosResponse>;
23
+ }