@datadog/datadog-ci 0.17.9 → 0.17.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE-3rdparty.csv +2 -0
- package/README.md +9 -2
- package/dist/{commands/dependencies/index.d.ts → cli.d.ts} +0 -0
- package/dist/cli.js +34 -0
- package/dist/commands/{dsyms/index.d.ts → dependencies/cli.d.ts} +0 -0
- package/dist/commands/dependencies/{index.js → cli.js} +0 -0
- package/dist/commands/dependencies/upload.d.ts +1 -1
- package/dist/commands/{git-metadata/index.d.ts → dsyms/cli.d.ts} +0 -0
- package/dist/commands/dsyms/{index.js → cli.js} +0 -0
- package/dist/commands/dsyms/upload.d.ts +1 -1
- package/dist/commands/{junit/index.d.ts → git-metadata/cli.d.ts} +0 -0
- package/dist/commands/git-metadata/{index.js → cli.js} +0 -0
- package/dist/commands/git-metadata/upload.d.ts +1 -1
- package/dist/commands/git-metadata/upload.js +6 -1
- package/dist/commands/{lambda/index.d.ts → junit/cli.d.ts} +0 -0
- package/dist/commands/junit/{index.js → cli.js} +0 -0
- package/dist/commands/junit/upload.d.ts +1 -1
- package/dist/commands/junit/upload.js +1 -1
- package/dist/commands/lambda/__tests__/fixtures.d.ts +5 -1
- package/dist/commands/lambda/__tests__/fixtures.js +13 -2
- package/dist/commands/lambda/__tests__/functions/commons.test.js +400 -0
- package/dist/commands/lambda/__tests__/functions/instrument.test.js +229 -117
- package/dist/commands/lambda/__tests__/functions/uninstrument.test.js +80 -7
- package/dist/commands/lambda/__tests__/instrument.test.js +542 -111
- package/dist/commands/{sourcemaps/index.d.ts → lambda/__tests__/prompt.test.d.ts} +0 -0
- package/dist/commands/lambda/__tests__/prompt.test.js +216 -0
- package/dist/commands/lambda/__tests__/uninstrument.test.js +381 -17
- package/dist/commands/{trace/index.d.ts → lambda/cli.d.ts} +0 -0
- package/dist/commands/lambda/{index.js → cli.js} +0 -0
- package/dist/commands/lambda/constants.d.ts +27 -6
- package/dist/commands/lambda/constants.js +63 -6
- package/dist/commands/lambda/functions/commons.d.ts +49 -4
- package/dist/commands/lambda/functions/commons.js +198 -7
- package/dist/commands/lambda/functions/instrument.d.ts +5 -14
- package/dist/commands/lambda/functions/instrument.js +63 -80
- package/dist/commands/lambda/functions/uninstrument.d.ts +3 -2
- package/dist/commands/lambda/functions/uninstrument.js +23 -11
- package/dist/commands/lambda/instrument.d.ts +2 -1
- package/dist/commands/lambda/instrument.js +112 -58
- package/dist/commands/lambda/interfaces.d.ts +4 -1
- package/dist/commands/lambda/loggroup.js +3 -1
- package/dist/commands/lambda/prompt.d.ts +9 -0
- package/dist/commands/lambda/prompt.js +187 -0
- package/dist/commands/lambda/uninstrument.d.ts +2 -0
- package/dist/commands/lambda/uninstrument.js +107 -30
- package/dist/commands/sourcemaps/cli.d.ts +1 -0
- package/dist/commands/sourcemaps/{index.js → cli.js} +0 -0
- package/dist/commands/sourcemaps/upload.d.ts +1 -1
- package/dist/commands/synthetics/__tests__/cli.test.js +36 -13
- package/dist/commands/synthetics/__tests__/fixtures.js +1 -0
- package/dist/commands/synthetics/__tests__/run-test.test.js +48 -2
- package/dist/commands/synthetics/__tests__/utils.test.js +11 -4
- package/dist/commands/synthetics/__tests__/websocket.test.js +3 -3
- package/dist/commands/synthetics/cli.d.ts +1 -26
- package/dist/commands/synthetics/cli.js +2 -227
- package/dist/commands/synthetics/command.d.ts +27 -0
- package/dist/commands/synthetics/command.js +236 -0
- package/dist/commands/synthetics/index.d.ts +5 -1
- package/dist/commands/synthetics/index.js +31 -2
- package/dist/commands/synthetics/interfaces.d.ts +8 -3
- package/dist/commands/synthetics/interfaces.js +7 -3
- package/dist/commands/synthetics/reporters/default.js +5 -1
- package/dist/commands/synthetics/run-test.js +3 -1
- package/dist/commands/synthetics/utils.d.ts +3 -3
- package/dist/commands/synthetics/utils.js +17 -8
- package/dist/commands/trace/api.js +1 -1
- package/dist/commands/trace/cli.d.ts +1 -0
- package/dist/commands/trace/{index.js → cli.js} +0 -0
- package/dist/commands/trace/trace.d.ts +1 -1
- package/dist/helpers/__tests__/ci.test.js +97 -136
- package/dist/helpers/__tests__/user-provided-git.test.js +81 -27
- package/dist/helpers/__tests__/utils.test.js +4 -0
- package/dist/helpers/ci.js +54 -95
- package/dist/helpers/interfaces.d.ts +28 -2
- package/dist/helpers/user-provided-git.d.ts +2 -1
- package/dist/helpers/user-provided-git.js +29 -5
- package/dist/helpers/utils.d.ts +4 -0
- package/dist/helpers/utils.js +18 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +23 -31
- package/package.json +10 -8
|
@@ -1,229 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
-
};
|
|
14
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const clipanion_1 = require("clipanion");
|
|
18
|
-
const deep_extend_1 = __importDefault(require("deep-extend"));
|
|
19
|
-
const utils_1 = require("../../helpers/utils");
|
|
20
|
-
const errors_1 = require("./errors");
|
|
21
|
-
const interfaces_1 = require("./interfaces");
|
|
22
|
-
const default_1 = require("./reporters/default");
|
|
23
|
-
const junit_1 = require("./reporters/junit");
|
|
24
|
-
const run_test_1 = require("./run-test");
|
|
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
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
49
|
-
const reporters = [new default_1.DefaultReporter(this)];
|
|
50
|
-
this.reporter = utils_2.getReporter(reporters);
|
|
51
|
-
if (this.jUnitReport) {
|
|
52
|
-
reporters.push(new junit_1.JUnitReporter(this));
|
|
53
|
-
}
|
|
54
|
-
yield this.resolveConfig();
|
|
55
|
-
const startTime = Date.now();
|
|
56
|
-
if (this.config.tunnel) {
|
|
57
|
-
this.reporter.log('You are using tunnel option, the chosen location(s) will be overridden by a location in your account region.\n');
|
|
58
|
-
}
|
|
59
|
-
let results;
|
|
60
|
-
let summary;
|
|
61
|
-
let tests;
|
|
62
|
-
let triggers;
|
|
63
|
-
try {
|
|
64
|
-
;
|
|
65
|
-
({ results, summary, tests, triggers } = yield run_test_1.executeTests(this.reporter, this.config));
|
|
66
|
-
}
|
|
67
|
-
catch (error) {
|
|
68
|
-
if (error instanceof errors_1.CiError) {
|
|
69
|
-
this.reportCiError(error, this.reporter);
|
|
70
|
-
if (error instanceof errors_1.CriticalError && this.config.failOnCriticalErrors) {
|
|
71
|
-
return 1;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
return 0;
|
|
75
|
-
}
|
|
76
|
-
return this.renderResults(results, summary, tests, triggers, startTime);
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
getAppBaseURL() {
|
|
80
|
-
return `https://${this.config.subdomain}.${this.config.datadogSite}/`;
|
|
81
|
-
}
|
|
82
|
-
renderResults(results, summary, tests, triggers, startTime) {
|
|
83
|
-
var _a, _b, _c, _d;
|
|
84
|
-
// Sort tests to show success first then non blocking failures and finally blocking failures.
|
|
85
|
-
tests.sort(this.sortTestsByOutcome(results));
|
|
86
|
-
// Rendering the results.
|
|
87
|
-
(_a = this.reporter) === null || _a === void 0 ? void 0 : _a.reportStart({ startTime });
|
|
88
|
-
const locationNames = triggers.locations.reduce((mapping, location) => {
|
|
89
|
-
mapping[location.id] = location.display_name;
|
|
90
|
-
return mapping;
|
|
91
|
-
}, {});
|
|
92
|
-
let hasSucceeded = true; // Determine if all the tests have succeeded
|
|
93
|
-
for (const test of tests) {
|
|
94
|
-
const testResults = results[test.public_id];
|
|
95
|
-
if (!this.config.failOnTimeout) {
|
|
96
|
-
if (!summary.timedOut) {
|
|
97
|
-
summary.timedOut = 0;
|
|
98
|
-
}
|
|
99
|
-
const hasTimeout = testResults.some((pollResult) => pollResult.result.error === interfaces_1.ERRORS.TIMEOUT);
|
|
100
|
-
if (hasTimeout) {
|
|
101
|
-
summary.timedOut++;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
if (!this.config.failOnCriticalErrors) {
|
|
105
|
-
if (!summary.criticalErrors) {
|
|
106
|
-
summary.criticalErrors = 0;
|
|
107
|
-
}
|
|
108
|
-
const hasCriticalErrors = testResults.some((pollResult) => utils_2.isCriticalError(pollResult.result));
|
|
109
|
-
if (hasCriticalErrors) {
|
|
110
|
-
summary.criticalErrors++;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
const passed = utils_2.hasTestSucceeded(testResults, this.config.failOnCriticalErrors, this.config.failOnTimeout);
|
|
114
|
-
if (passed) {
|
|
115
|
-
summary.passed++;
|
|
116
|
-
}
|
|
117
|
-
else {
|
|
118
|
-
summary.failed++;
|
|
119
|
-
if (((_b = test.options.ci) === null || _b === void 0 ? void 0 : _b.executionRule) !== interfaces_1.ExecutionRule.NON_BLOCKING) {
|
|
120
|
-
hasSucceeded = false;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
(_c = this.reporter) === null || _c === void 0 ? void 0 : _c.testEnd(test, testResults, this.getAppBaseURL(), locationNames, this.config.failOnCriticalErrors, this.config.failOnTimeout);
|
|
124
|
-
}
|
|
125
|
-
(_d = this.reporter) === null || _d === void 0 ? void 0 : _d.runEnd(summary);
|
|
126
|
-
return hasSucceeded ? 0 : 1;
|
|
127
|
-
}
|
|
128
|
-
reportCiError(error, reporter) {
|
|
129
|
-
switch (error.code) {
|
|
130
|
-
case 'NO_RESULTS_TO_POLL':
|
|
131
|
-
reporter.log('No results to poll.\n');
|
|
132
|
-
break;
|
|
133
|
-
case 'NO_TESTS_TO_RUN':
|
|
134
|
-
reporter.log('No test to run.\n');
|
|
135
|
-
break;
|
|
136
|
-
case 'MISSING_APP_KEY':
|
|
137
|
-
reporter.error(`Missing ${chalk_1.default.red.bold('DATADOG_APP_KEY')} in your environment.\n`);
|
|
138
|
-
break;
|
|
139
|
-
case 'MISSING_API_KEY':
|
|
140
|
-
reporter.error(`Missing ${chalk_1.default.red.bold('DATADOG_API_KEY')} in your environment.\n`);
|
|
141
|
-
break;
|
|
142
|
-
case 'POLL_RESULTS_FAILED':
|
|
143
|
-
reporter.error(`\n${chalk_1.default.bgRed.bold(' ERROR: unable to poll test results ')}\n${error.message}\n\n`);
|
|
144
|
-
break;
|
|
145
|
-
case 'TUNNEL_START_FAILED':
|
|
146
|
-
reporter.error(`\n${chalk_1.default.bgRed.bold(' ERROR: unable to start tunnel')}\n${error.message}\n\n`);
|
|
147
|
-
break;
|
|
148
|
-
case 'TRIGGER_TESTS_FAILED':
|
|
149
|
-
reporter.error(`\n${chalk_1.default.bgRed.bold(' ERROR: unable to trigger tests')}\n${error.message}\n\n`);
|
|
150
|
-
break;
|
|
151
|
-
case 'UNAVAILABLE_TEST_CONFIG':
|
|
152
|
-
reporter.error(`\n${chalk_1.default.bgRed.bold(' ERROR: unable to obtain test configurations with search query ')}\n${error.message}\n\n`);
|
|
153
|
-
break;
|
|
154
|
-
case 'UNAVAILABLE_TUNNEL_CONFIG':
|
|
155
|
-
reporter.error(`\n${chalk_1.default.bgRed.bold(' ERROR: unable to get tunnel configuration')}\n${error.message}\n\n`);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
resolveConfig() {
|
|
159
|
-
var _a, _b;
|
|
160
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
161
|
-
// Default < file < ENV < CLI
|
|
162
|
-
// Override with file config variables
|
|
163
|
-
try {
|
|
164
|
-
this.config = yield utils_1.parseConfigFile(this.config, (_a = this.configPath) !== null && _a !== void 0 ? _a : this.config.configPath);
|
|
165
|
-
}
|
|
166
|
-
catch (error) {
|
|
167
|
-
if (this.configPath) {
|
|
168
|
-
throw error;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
// Override with ENV variables
|
|
172
|
-
this.config = deep_extend_1.default(this.config, utils_2.removeUndefinedValues({
|
|
173
|
-
apiKey: process.env.DATADOG_API_KEY,
|
|
174
|
-
appKey: process.env.DATADOG_APP_KEY,
|
|
175
|
-
datadogSite: process.env.DATADOG_SITE,
|
|
176
|
-
locations: (_b = process.env.DATADOG_SYNTHETICS_LOCATIONS) === null || _b === void 0 ? void 0 : _b.split(';'),
|
|
177
|
-
subdomain: process.env.DATADOG_SUBDOMAIN,
|
|
178
|
-
}));
|
|
179
|
-
// Override with CLI parameters
|
|
180
|
-
this.config = deep_extend_1.default(this.config, utils_2.removeUndefinedValues({
|
|
181
|
-
apiKey: this.apiKey,
|
|
182
|
-
appKey: this.appKey,
|
|
183
|
-
configPath: this.configPath,
|
|
184
|
-
datadogSite: this.datadogSite,
|
|
185
|
-
failOnCriticalErrors: this.failOnCriticalErrors,
|
|
186
|
-
failOnTimeout: this.failOnTimeout,
|
|
187
|
-
files: this.files,
|
|
188
|
-
publicIds: this.publicIds,
|
|
189
|
-
subdomain: this.subdomain,
|
|
190
|
-
testSearchQuery: this.testSearchQuery,
|
|
191
|
-
tunnel: this.tunnel,
|
|
192
|
-
}));
|
|
193
|
-
if (typeof this.config.files === 'string') {
|
|
194
|
-
this.reporter.log('[DEPRECATED] "files" should be an array of string instead of a string.\n');
|
|
195
|
-
this.config.files = [this.config.files];
|
|
196
|
-
}
|
|
197
|
-
});
|
|
198
|
-
}
|
|
199
|
-
sortTestsByOutcome(results) {
|
|
200
|
-
return (t1, t2) => {
|
|
201
|
-
var _a, _b;
|
|
202
|
-
const success1 = utils_2.hasTestSucceeded(results[t1.public_id], this.config.failOnCriticalErrors, this.config.failOnTimeout);
|
|
203
|
-
const success2 = utils_2.hasTestSucceeded(results[t2.public_id], this.config.failOnCriticalErrors, this.config.failOnTimeout);
|
|
204
|
-
const isNonBlockingTest1 = ((_a = t1.options.ci) === null || _a === void 0 ? void 0 : _a.executionRule) === interfaces_1.ExecutionRule.NON_BLOCKING;
|
|
205
|
-
const isNonBlockingTest2 = ((_b = t2.options.ci) === null || _b === void 0 ? void 0 : _b.executionRule) === interfaces_1.ExecutionRule.NON_BLOCKING;
|
|
206
|
-
if (success1 === success2) {
|
|
207
|
-
if (isNonBlockingTest1 === isNonBlockingTest2) {
|
|
208
|
-
return 0;
|
|
209
|
-
}
|
|
210
|
-
return isNonBlockingTest1 ? -1 : 1;
|
|
211
|
-
}
|
|
212
|
-
return success1 ? -1 : 1;
|
|
213
|
-
};
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
exports.RunTestCommand = RunTestCommand;
|
|
217
|
-
RunTestCommand.addPath('synthetics', 'run-tests');
|
|
218
|
-
RunTestCommand.addOption('apiKey', clipanion_1.Command.String('--apiKey'));
|
|
219
|
-
RunTestCommand.addOption('appKey', clipanion_1.Command.String('--appKey'));
|
|
220
|
-
RunTestCommand.addOption('failOnCriticalErrors', clipanion_1.Command.Boolean('--failOnCriticalErrors'));
|
|
221
|
-
RunTestCommand.addOption('configPath', clipanion_1.Command.String('--config'));
|
|
222
|
-
RunTestCommand.addOption('datadogSite', clipanion_1.Command.String('--datadogSite'));
|
|
223
|
-
RunTestCommand.addOption('files', clipanion_1.Command.Array('-f,--files'));
|
|
224
|
-
RunTestCommand.addOption('failOnTimeout', clipanion_1.Command.Boolean('--failOnTimeout'));
|
|
225
|
-
RunTestCommand.addOption('publicIds', clipanion_1.Command.Array('-p,--public-id'));
|
|
226
|
-
RunTestCommand.addOption('testSearchQuery', clipanion_1.Command.String('-s,--search'));
|
|
227
|
-
RunTestCommand.addOption('subdomain', clipanion_1.Command.Boolean('--subdomain'));
|
|
228
|
-
RunTestCommand.addOption('tunnel', clipanion_1.Command.Boolean('-t,--tunnel'));
|
|
229
|
-
RunTestCommand.addOption('runName', clipanion_1.Command.String('-n,--runName'));
|
|
3
|
+
const command_1 = require("./command");
|
|
4
|
+
module.exports = [command_1.RunTestCommand];
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Command } from 'clipanion';
|
|
2
|
+
import { CommandConfig } from './interfaces';
|
|
3
|
+
export declare const DEFAULT_COMMAND_CONFIG: CommandConfig;
|
|
4
|
+
export declare class RunTestCommand extends Command {
|
|
5
|
+
jUnitReport?: string;
|
|
6
|
+
runName?: string;
|
|
7
|
+
private apiKey?;
|
|
8
|
+
private appKey?;
|
|
9
|
+
private config;
|
|
10
|
+
private configPath?;
|
|
11
|
+
private datadogSite?;
|
|
12
|
+
private failOnCriticalErrors?;
|
|
13
|
+
private failOnTimeout?;
|
|
14
|
+
private files?;
|
|
15
|
+
private publicIds?;
|
|
16
|
+
private reporter?;
|
|
17
|
+
private subdomain?;
|
|
18
|
+
private testSearchQuery?;
|
|
19
|
+
private tunnel?;
|
|
20
|
+
private variableStrings?;
|
|
21
|
+
execute(): Promise<1 | 0>;
|
|
22
|
+
private getAppBaseURL;
|
|
23
|
+
private renderResults;
|
|
24
|
+
private reportCiError;
|
|
25
|
+
private resolveConfig;
|
|
26
|
+
private sortTestsByOutcome;
|
|
27
|
+
}
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
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");
|
|
20
|
+
const errors_1 = require("./errors");
|
|
21
|
+
const interfaces_1 = require("./interfaces");
|
|
22
|
+
const default_1 = require("./reporters/default");
|
|
23
|
+
const junit_1 = require("./reporters/junit");
|
|
24
|
+
const run_test_1 = require("./run-test");
|
|
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
|
+
variableStrings: [],
|
|
42
|
+
};
|
|
43
|
+
class RunTestCommand extends clipanion_1.Command {
|
|
44
|
+
constructor() {
|
|
45
|
+
super(...arguments);
|
|
46
|
+
this.config = JSON.parse(JSON.stringify(exports.DEFAULT_COMMAND_CONFIG)); // Deep copy to avoid mutation during unit tests
|
|
47
|
+
}
|
|
48
|
+
execute() {
|
|
49
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
50
|
+
const reporters = [new default_1.DefaultReporter(this)];
|
|
51
|
+
this.reporter = utils_2.getReporter(reporters);
|
|
52
|
+
if (this.jUnitReport) {
|
|
53
|
+
reporters.push(new junit_1.JUnitReporter(this));
|
|
54
|
+
}
|
|
55
|
+
yield this.resolveConfig();
|
|
56
|
+
const startTime = Date.now();
|
|
57
|
+
if (this.config.tunnel) {
|
|
58
|
+
this.reporter.log('You are using tunnel option, the chosen location(s) will be overridden by a location in your account region.\n');
|
|
59
|
+
}
|
|
60
|
+
let results;
|
|
61
|
+
let summary;
|
|
62
|
+
let tests;
|
|
63
|
+
let triggers;
|
|
64
|
+
try {
|
|
65
|
+
;
|
|
66
|
+
({ results, summary, tests, triggers } = yield run_test_1.executeTests(this.reporter, this.config));
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
if (error instanceof errors_1.CiError) {
|
|
70
|
+
this.reportCiError(error, this.reporter);
|
|
71
|
+
if (error instanceof errors_1.CriticalError && this.config.failOnCriticalErrors) {
|
|
72
|
+
return 1;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return 0;
|
|
76
|
+
}
|
|
77
|
+
return this.renderResults(results, summary, tests, triggers, startTime);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
getAppBaseURL() {
|
|
81
|
+
return `https://${this.config.subdomain}.${this.config.datadogSite}/`;
|
|
82
|
+
}
|
|
83
|
+
renderResults(results, summary, tests, triggers, startTime) {
|
|
84
|
+
var _a, _b, _c, _d;
|
|
85
|
+
// Sort tests to show success first then non blocking failures and finally blocking failures.
|
|
86
|
+
tests.sort(this.sortTestsByOutcome(results));
|
|
87
|
+
// Rendering the results.
|
|
88
|
+
(_a = this.reporter) === null || _a === void 0 ? void 0 : _a.reportStart({ startTime });
|
|
89
|
+
const locationNames = triggers.locations.reduce((mapping, location) => {
|
|
90
|
+
mapping[location.id] = location.display_name;
|
|
91
|
+
return mapping;
|
|
92
|
+
}, {});
|
|
93
|
+
let hasSucceeded = true; // Determine if all the tests have succeeded
|
|
94
|
+
for (const test of tests) {
|
|
95
|
+
const testResults = results[test.public_id];
|
|
96
|
+
if (!this.config.failOnTimeout) {
|
|
97
|
+
if (!summary.timedOut) {
|
|
98
|
+
summary.timedOut = 0;
|
|
99
|
+
}
|
|
100
|
+
const hasTimeout = testResults.some((pollResult) => pollResult.result.error === interfaces_1.ERRORS.TIMEOUT);
|
|
101
|
+
if (hasTimeout) {
|
|
102
|
+
summary.timedOut++;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (!this.config.failOnCriticalErrors) {
|
|
106
|
+
if (!summary.criticalErrors) {
|
|
107
|
+
summary.criticalErrors = 0;
|
|
108
|
+
}
|
|
109
|
+
const hasCriticalErrors = testResults.some((pollResult) => utils_2.isCriticalError(pollResult.result));
|
|
110
|
+
if (hasCriticalErrors) {
|
|
111
|
+
summary.criticalErrors++;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const passed = utils_2.hasTestSucceeded(testResults, this.config.failOnCriticalErrors, this.config.failOnTimeout);
|
|
115
|
+
if (passed) {
|
|
116
|
+
summary.passed++;
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
summary.failed++;
|
|
120
|
+
if (((_b = test.options.ci) === null || _b === void 0 ? void 0 : _b.executionRule) !== interfaces_1.ExecutionRule.NON_BLOCKING) {
|
|
121
|
+
hasSucceeded = false;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
(_c = this.reporter) === null || _c === void 0 ? void 0 : _c.testEnd(test, testResults, this.getAppBaseURL(), locationNames, this.config.failOnCriticalErrors, this.config.failOnTimeout);
|
|
125
|
+
}
|
|
126
|
+
(_d = this.reporter) === null || _d === void 0 ? void 0 : _d.runEnd(summary);
|
|
127
|
+
return hasSucceeded ? 0 : 1;
|
|
128
|
+
}
|
|
129
|
+
reportCiError(error, reporter) {
|
|
130
|
+
switch (error.code) {
|
|
131
|
+
case 'NO_RESULTS_TO_POLL':
|
|
132
|
+
reporter.log('No results to poll.\n');
|
|
133
|
+
break;
|
|
134
|
+
case 'NO_TESTS_TO_RUN':
|
|
135
|
+
reporter.log('No test to run.\n');
|
|
136
|
+
break;
|
|
137
|
+
case 'MISSING_APP_KEY':
|
|
138
|
+
reporter.error(`Missing ${chalk_1.default.red.bold('DATADOG_APP_KEY')} in your environment.\n`);
|
|
139
|
+
break;
|
|
140
|
+
case 'MISSING_API_KEY':
|
|
141
|
+
reporter.error(`Missing ${chalk_1.default.red.bold('DATADOG_API_KEY')} in your environment.\n`);
|
|
142
|
+
break;
|
|
143
|
+
case 'POLL_RESULTS_FAILED':
|
|
144
|
+
reporter.error(`\n${chalk_1.default.bgRed.bold(' ERROR: unable to poll test results ')}\n${error.message}\n\n`);
|
|
145
|
+
break;
|
|
146
|
+
case 'TUNNEL_START_FAILED':
|
|
147
|
+
reporter.error(`\n${chalk_1.default.bgRed.bold(' ERROR: unable to start tunnel')}\n${error.message}\n\n`);
|
|
148
|
+
break;
|
|
149
|
+
case 'TRIGGER_TESTS_FAILED':
|
|
150
|
+
reporter.error(`\n${chalk_1.default.bgRed.bold(' ERROR: unable to trigger tests')}\n${error.message}\n\n`);
|
|
151
|
+
break;
|
|
152
|
+
case 'UNAVAILABLE_TEST_CONFIG':
|
|
153
|
+
reporter.error(`\n${chalk_1.default.bgRed.bold(' ERROR: unable to obtain test configurations with search query ')}\n${error.message}\n\n`);
|
|
154
|
+
break;
|
|
155
|
+
case 'UNAVAILABLE_TUNNEL_CONFIG':
|
|
156
|
+
reporter.error(`\n${chalk_1.default.bgRed.bold(' ERROR: unable to get tunnel configuration')}\n${error.message}\n\n`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
resolveConfig() {
|
|
160
|
+
var _a, _b;
|
|
161
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
162
|
+
// Default < file < ENV < CLI
|
|
163
|
+
// Override with file config variables
|
|
164
|
+
try {
|
|
165
|
+
this.config = yield utils_1.parseConfigFile(this.config, (_a = this.configPath) !== null && _a !== void 0 ? _a : this.config.configPath);
|
|
166
|
+
}
|
|
167
|
+
catch (error) {
|
|
168
|
+
if (this.configPath) {
|
|
169
|
+
throw error;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
// Override with ENV variables
|
|
173
|
+
this.config = deep_extend_1.default(this.config, utils_1.removeUndefinedValues({
|
|
174
|
+
apiKey: process.env.DATADOG_API_KEY,
|
|
175
|
+
appKey: process.env.DATADOG_APP_KEY,
|
|
176
|
+
datadogSite: process.env.DATADOG_SITE,
|
|
177
|
+
locations: (_b = process.env.DATADOG_SYNTHETICS_LOCATIONS) === null || _b === void 0 ? void 0 : _b.split(';'),
|
|
178
|
+
subdomain: process.env.DATADOG_SUBDOMAIN,
|
|
179
|
+
}));
|
|
180
|
+
// Override with CLI parameters
|
|
181
|
+
this.config = deep_extend_1.default(this.config, utils_1.removeUndefinedValues({
|
|
182
|
+
apiKey: this.apiKey,
|
|
183
|
+
appKey: this.appKey,
|
|
184
|
+
configPath: this.configPath,
|
|
185
|
+
datadogSite: this.datadogSite,
|
|
186
|
+
failOnCriticalErrors: this.failOnCriticalErrors,
|
|
187
|
+
failOnTimeout: this.failOnTimeout,
|
|
188
|
+
files: this.files,
|
|
189
|
+
publicIds: this.publicIds,
|
|
190
|
+
subdomain: this.subdomain,
|
|
191
|
+
testSearchQuery: this.testSearchQuery,
|
|
192
|
+
tunnel: this.tunnel,
|
|
193
|
+
}));
|
|
194
|
+
// Override with Global CLI parameters
|
|
195
|
+
this.config.global = deep_extend_1.default(this.config.global, utils_1.removeUndefinedValues({
|
|
196
|
+
variables: utils_2.parseVariablesFromCli(this.variableStrings, (log) => { var _a; return (_a = this.reporter) === null || _a === void 0 ? void 0 : _a.log(log); }),
|
|
197
|
+
}));
|
|
198
|
+
if (typeof this.config.files === 'string') {
|
|
199
|
+
this.reporter.log('[DEPRECATED] "files" should be an array of string instead of a string.\n');
|
|
200
|
+
this.config.files = [this.config.files];
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
sortTestsByOutcome(results) {
|
|
205
|
+
return (t1, t2) => {
|
|
206
|
+
var _a, _b;
|
|
207
|
+
const success1 = utils_2.hasTestSucceeded(results[t1.public_id], this.config.failOnCriticalErrors, this.config.failOnTimeout);
|
|
208
|
+
const success2 = utils_2.hasTestSucceeded(results[t2.public_id], this.config.failOnCriticalErrors, this.config.failOnTimeout);
|
|
209
|
+
const isNonBlockingTest1 = ((_a = t1.options.ci) === null || _a === void 0 ? void 0 : _a.executionRule) === interfaces_1.ExecutionRule.NON_BLOCKING;
|
|
210
|
+
const isNonBlockingTest2 = ((_b = t2.options.ci) === null || _b === void 0 ? void 0 : _b.executionRule) === interfaces_1.ExecutionRule.NON_BLOCKING;
|
|
211
|
+
if (success1 === success2) {
|
|
212
|
+
if (isNonBlockingTest1 === isNonBlockingTest2) {
|
|
213
|
+
return 0;
|
|
214
|
+
}
|
|
215
|
+
return isNonBlockingTest1 ? -1 : 1;
|
|
216
|
+
}
|
|
217
|
+
return success1 ? -1 : 1;
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
exports.RunTestCommand = RunTestCommand;
|
|
222
|
+
RunTestCommand.addPath('synthetics', 'run-tests');
|
|
223
|
+
RunTestCommand.addOption('apiKey', clipanion_1.Command.String('--apiKey'));
|
|
224
|
+
RunTestCommand.addOption('appKey', clipanion_1.Command.String('--appKey'));
|
|
225
|
+
RunTestCommand.addOption('configPath', clipanion_1.Command.String('--config'));
|
|
226
|
+
RunTestCommand.addOption('datadogSite', clipanion_1.Command.String('--datadogSite'));
|
|
227
|
+
RunTestCommand.addOption('failOnCriticalErrors', clipanion_1.Command.Boolean('--failOnCriticalErrors'));
|
|
228
|
+
RunTestCommand.addOption('failOnTimeout', clipanion_1.Command.Boolean('--failOnTimeout'));
|
|
229
|
+
RunTestCommand.addOption('files', clipanion_1.Command.Array('-f,--files'));
|
|
230
|
+
RunTestCommand.addOption('jUnitReport', clipanion_1.Command.String('-j,--jUnitReport'));
|
|
231
|
+
RunTestCommand.addOption('publicIds', clipanion_1.Command.Array('-p,--public-id'));
|
|
232
|
+
RunTestCommand.addOption('runName', clipanion_1.Command.String('-n,--runName'));
|
|
233
|
+
RunTestCommand.addOption('subdomain', clipanion_1.Command.Boolean('--subdomain'));
|
|
234
|
+
RunTestCommand.addOption('testSearchQuery', clipanion_1.Command.String('-s,--search'));
|
|
235
|
+
RunTestCommand.addOption('tunnel', clipanion_1.Command.Boolean('-t,--tunnel'));
|
|
236
|
+
RunTestCommand.addOption('variableStrings', clipanion_1.Command.Array('-v,--variable'));
|
|
@@ -1,4 +1,33 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
5
|
+
}) : (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
o[k2] = m[k];
|
|
8
|
+
}));
|
|
9
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
10
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
11
|
+
}) : function(o, v) {
|
|
12
|
+
o["default"] = v;
|
|
13
|
+
});
|
|
14
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
15
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
16
|
+
};
|
|
17
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
18
|
+
if (mod && mod.__esModule) return mod;
|
|
19
|
+
var result = {};
|
|
20
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
21
|
+
__setModuleDefault(result, mod);
|
|
22
|
+
return result;
|
|
23
|
+
};
|
|
2
24
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
|
|
4
|
-
|
|
25
|
+
exports.utils = exports.executeTests = exports.DefaultReporter = exports.CiError = void 0;
|
|
26
|
+
var errors_1 = require("./errors");
|
|
27
|
+
Object.defineProperty(exports, "CiError", { enumerable: true, get: function () { return errors_1.CiError; } });
|
|
28
|
+
__exportStar(require("./interfaces"), exports);
|
|
29
|
+
var default_1 = require("./reporters/default");
|
|
30
|
+
Object.defineProperty(exports, "DefaultReporter", { enumerable: true, get: function () { return default_1.DefaultReporter; } });
|
|
31
|
+
var run_test_1 = require("./run-test");
|
|
32
|
+
Object.defineProperty(exports, "executeTests", { enumerable: true, get: function () { return run_test_1.executeTests; } });
|
|
33
|
+
exports.utils = __importStar(require("./utils"));
|
|
@@ -179,12 +179,16 @@ export declare enum Operator {
|
|
|
179
179
|
doesNotContain = "doesNotContain",
|
|
180
180
|
is = "is",
|
|
181
181
|
isNot = "isNot",
|
|
182
|
+
isInLessThan = "isInLessThan",
|
|
183
|
+
isInMoreThan = "isInMoreThan",
|
|
182
184
|
lessThan = "lessThan",
|
|
185
|
+
lessThanOrEqual = "lessThanOrEqual",
|
|
186
|
+
moreThan = "moreThan",
|
|
187
|
+
moreThanOrEqual = "moreThanOrEqual",
|
|
183
188
|
matches = "matches",
|
|
184
189
|
doesNotMatch = "doesNotMatch",
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
isInLessThan = "isInLessThan"
|
|
190
|
+
validatesJSONPath = "validatesJSONPath",
|
|
191
|
+
validatesXPath = "validatesXPath"
|
|
188
192
|
}
|
|
189
193
|
export interface User {
|
|
190
194
|
email: string;
|
|
@@ -335,6 +339,7 @@ export interface SyntheticsCIConfig {
|
|
|
335
339
|
subdomain: string;
|
|
336
340
|
testSearchQuery?: string;
|
|
337
341
|
tunnel: boolean;
|
|
342
|
+
variableStrings: string[];
|
|
338
343
|
}
|
|
339
344
|
export interface CommandConfig extends SyntheticsCIConfig {
|
|
340
345
|
failOnTimeout: boolean;
|
|
@@ -13,12 +13,16 @@ var Operator;
|
|
|
13
13
|
Operator["doesNotContain"] = "doesNotContain";
|
|
14
14
|
Operator["is"] = "is";
|
|
15
15
|
Operator["isNot"] = "isNot";
|
|
16
|
+
Operator["isInLessThan"] = "isInLessThan";
|
|
17
|
+
Operator["isInMoreThan"] = "isInMoreThan";
|
|
16
18
|
Operator["lessThan"] = "lessThan";
|
|
19
|
+
Operator["lessThanOrEqual"] = "lessThanOrEqual";
|
|
20
|
+
Operator["moreThan"] = "moreThan";
|
|
21
|
+
Operator["moreThanOrEqual"] = "moreThanOrEqual";
|
|
17
22
|
Operator["matches"] = "matches";
|
|
18
23
|
Operator["doesNotMatch"] = "doesNotMatch";
|
|
19
|
-
Operator["
|
|
20
|
-
Operator["
|
|
21
|
-
Operator["isInLessThan"] = "isInLessThan";
|
|
24
|
+
Operator["validatesJSONPath"] = "validatesJSONPath";
|
|
25
|
+
Operator["validatesXPath"] = "validatesXPath";
|
|
22
26
|
})(Operator = exports.Operator || (exports.Operator = {}));
|
|
23
27
|
var ExecutionRule;
|
|
24
28
|
(function (ExecutionRule) {
|
|
@@ -50,9 +50,13 @@ const readableOperation = {
|
|
|
50
50
|
[interfaces_1.Operator.lessThan]: 'should be less than',
|
|
51
51
|
[interfaces_1.Operator.matches]: 'should match',
|
|
52
52
|
[interfaces_1.Operator.doesNotMatch]: 'should not match',
|
|
53
|
-
[interfaces_1.Operator.validates]: 'will expire in less than',
|
|
54
53
|
[interfaces_1.Operator.isInLessThan]: 'will expire in less than',
|
|
55
54
|
[interfaces_1.Operator.isInMoreThan]: 'will expire in more than',
|
|
55
|
+
[interfaces_1.Operator.lessThanOrEqual]: 'should be less than or equal to',
|
|
56
|
+
[interfaces_1.Operator.moreThan]: 'should be more than',
|
|
57
|
+
[interfaces_1.Operator.moreThanOrEqual]: 'should be less than or equal to',
|
|
58
|
+
[interfaces_1.Operator.validatesJSONPath]: 'assert on JSONPath extracted value',
|
|
59
|
+
[interfaces_1.Operator.validatesXPath]: 'assert on XPath extracted value',
|
|
56
60
|
};
|
|
57
61
|
const renderApiError = (errorCode, errorMessage, color) => {
|
|
58
62
|
if (errorCode === 'INCORRECT_ASSERTION') {
|
|
@@ -98,9 +98,11 @@ const executeTests = (reporter, config) => __awaiter(void 0, void 0, void 0, fun
|
|
|
98
98
|
}
|
|
99
99
|
catch (error) {
|
|
100
100
|
const isCriticalError = api_1.is5xxError(error);
|
|
101
|
-
yield stopTunnel();
|
|
102
101
|
throw new (isCriticalError ? errors_1.CriticalError : errors_1.CiError)('POLL_RESULTS_FAILED');
|
|
103
102
|
}
|
|
103
|
+
finally {
|
|
104
|
+
yield stopTunnel();
|
|
105
|
+
}
|
|
104
106
|
return { results, summary, tests, triggers };
|
|
105
107
|
});
|
|
106
108
|
exports.executeTests = executeTests;
|
|
@@ -22,6 +22,6 @@ export declare const getTestsToTrigger: (api: APIHelper, triggerConfigs: Trigger
|
|
|
22
22
|
}>;
|
|
23
23
|
export declare const runTests: (api: APIHelper, testsToTrigger: TestPayload[]) => Promise<Trigger>;
|
|
24
24
|
export declare const retry: <T, E extends Error>(func: () => Promise<T>, shouldRetryAfterWait: (retries: number, error: E) => number | undefined) => Promise<T>;
|
|
25
|
-
export declare const
|
|
26
|
-
[key: string]:
|
|
27
|
-
}
|
|
25
|
+
export declare const parseVariablesFromCli: (variableArguments: string[] | undefined, logFunction: (log: string) => void) => {
|
|
26
|
+
[key: string]: string;
|
|
27
|
+
} | undefined;
|