@teamscale/coverage-collector 0.0.1-beta.6 → 0.0.1-beta.60
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 +176 -0
- package/README.md +5 -10
- package/dist/package.json +34 -24
- package/dist/src/App.d.ts +39 -0
- package/dist/src/App.js +244 -0
- package/dist/src/main.d.ts +1 -30
- package/dist/src/main.js +2 -115
- package/dist/src/receiver/CollectingServer.d.ts +5 -3
- package/dist/src/receiver/CollectingServer.js +41 -23
- package/dist/src/receiver/Session.d.ts +24 -5
- package/dist/src/receiver/Session.js +80 -16
- package/dist/src/storage/DataStorage.d.ts +40 -9
- package/dist/src/storage/DataStorage.js +68 -23
- package/dist/src/upload/ArtifactoryUpload.d.ts +6 -0
- package/dist/src/upload/ArtifactoryUpload.js +66 -0
- package/dist/src/upload/CommonUpload.d.ts +16 -0
- package/dist/src/upload/CommonUpload.js +56 -0
- package/dist/src/upload/ProxyUpload.d.ts +6 -0
- package/dist/src/upload/ProxyUpload.js +30 -0
- package/dist/src/upload/TeamscaleUpload.d.ts +6 -0
- package/dist/src/upload/TeamscaleUpload.js +54 -0
- package/dist/src/utils/ConfigParameters.d.ts +36 -0
- package/dist/src/utils/ConfigParameters.js +107 -0
- package/dist/src/utils/PrettyFileLogger.d.ts +13 -0
- package/dist/src/utils/PrettyFileLogger.js +25 -0
- package/dist/src/utils/QueryParameters.d.ts +9 -0
- package/dist/src/utils/QueryParameters.js +19 -0
- package/dist/src/utils/StdConsoleLogger.d.ts +5 -0
- package/dist/src/utils/StdConsoleLogger.js +16 -0
- package/package.json +39 -30
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
3
|
if (k2 === undefined) k2 = k;
|
|
4
|
-
Object.
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
5
9
|
}) : (function(o, m, k, k2) {
|
|
6
10
|
if (k2 === undefined) k2 = k;
|
|
7
11
|
o[k2] = m[k];
|
|
@@ -18,10 +22,15 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
18
22
|
__setModuleDefault(result, mod);
|
|
19
23
|
return result;
|
|
20
24
|
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
21
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
29
|
exports.DataStorage = exports.ProjectCoverage = void 0;
|
|
23
30
|
const commons_1 = require("@cqse/commons");
|
|
24
31
|
const fs = __importStar(require("fs"));
|
|
32
|
+
const path_1 = __importDefault(require("path"));
|
|
33
|
+
const dat = __importStar(require("date-and-time"));
|
|
25
34
|
/**
|
|
26
35
|
* The coverage information received for one particular project.
|
|
27
36
|
*/
|
|
@@ -77,6 +86,10 @@ class DataStorage {
|
|
|
77
86
|
* @param logger - The logger to use.
|
|
78
87
|
*/
|
|
79
88
|
constructor(logger) {
|
|
89
|
+
/**
|
|
90
|
+
* Date format for the timestamp appended to the coverage files
|
|
91
|
+
*/
|
|
92
|
+
this.DATE_FORMAT = 'YYYY-MM-DD-HH-mm-ss.SSS';
|
|
80
93
|
this.coverageByProject = new Map();
|
|
81
94
|
this.logger = commons_1.Contract.requireDefined(logger);
|
|
82
95
|
this.timesUnmappedCoverage = 0;
|
|
@@ -89,7 +102,7 @@ class DataStorage {
|
|
|
89
102
|
* @param coveredOriginalLines - The lines covered in the file.
|
|
90
103
|
*/
|
|
91
104
|
putCoverage(project, sourceFilePath, coveredOriginalLines) {
|
|
92
|
-
const uniformPath =
|
|
105
|
+
const uniformPath = DataStorage.normalizeSourceFileName(sourceFilePath);
|
|
93
106
|
let projectCoverage = this.coverageByProject.get(project);
|
|
94
107
|
if (!projectCoverage) {
|
|
95
108
|
projectCoverage = new ProjectCoverage(project);
|
|
@@ -104,7 +117,7 @@ class DataStorage {
|
|
|
104
117
|
*
|
|
105
118
|
* @param sourceFile - The file name to normalize, produced by the instrumenter.
|
|
106
119
|
*/
|
|
107
|
-
normalizeSourceFileName(sourceFile) {
|
|
120
|
+
static normalizeSourceFileName(sourceFile) {
|
|
108
121
|
return (0, commons_1.removePrefix)('webpack:///', sourceFile.replace('\\', '/'));
|
|
109
122
|
}
|
|
110
123
|
/**
|
|
@@ -125,29 +138,55 @@ class DataStorage {
|
|
|
125
138
|
return projectCoverage === null || projectCoverage === void 0 ? void 0 : projectCoverage.getCoverage();
|
|
126
139
|
}
|
|
127
140
|
/**
|
|
128
|
-
*
|
|
141
|
+
* @inheritDoc
|
|
129
142
|
*/
|
|
130
|
-
dumpToSimpleCoverageFile(
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
143
|
+
dumpToSimpleCoverageFile(coverageFolder, date) {
|
|
144
|
+
const [lines, content] = this.toSimpleCoverage();
|
|
145
|
+
const coverageFolderTrimmed = coverageFolder.trim();
|
|
146
|
+
const finalFilePath = this.initCoverageFile(coverageFolderTrimmed, date);
|
|
147
|
+
fs.writeFileSync(finalFilePath, content, { flag: 'w', encoding: 'utf8' });
|
|
148
|
+
this.resetCoverage();
|
|
149
|
+
return [finalFilePath, lines];
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Set the collected coverage to 0 for all projects
|
|
153
|
+
* @private
|
|
154
|
+
*/
|
|
155
|
+
resetCoverage() {
|
|
156
|
+
this.coverageByProject = new Map();
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Appends the timestamp given with date to the coverageFolder (before the file ending if there is one)
|
|
160
|
+
* @param coverageFolder Path to the coverage file
|
|
161
|
+
* @param date Represents the timestamp to be appended with the format {@link DataStorage.DATE_FORMAT}
|
|
162
|
+
* @private
|
|
163
|
+
*/
|
|
164
|
+
initCoverageFile(coverageFolder, date) {
|
|
165
|
+
if (!fs.existsSync(coverageFolder)) {
|
|
166
|
+
fs.mkdirSync(coverageFolder);
|
|
167
|
+
}
|
|
168
|
+
const formattedDate = dat.format(date, this.DATE_FORMAT);
|
|
169
|
+
return path_1.default.join(coverageFolder, `coverage-${formattedDate}.simple`);
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Generate simple coverage format for the collected coverage
|
|
173
|
+
*/
|
|
174
|
+
toSimpleCoverage() {
|
|
175
|
+
const result = [];
|
|
176
|
+
commons_1.Contract.require(this.getProjects().length < 2, 'Only one project supported to be handled in parallel.');
|
|
177
|
+
for (const project of this.getProjects()) {
|
|
178
|
+
const projectCoverage = this.getCoverageBySourceFile(project);
|
|
179
|
+
if (!projectCoverage) {
|
|
180
|
+
return [0, ''];
|
|
181
|
+
}
|
|
182
|
+
for (const entry of projectCoverage) {
|
|
183
|
+
result.push(DataStorage.normalizeSourceFileName(entry.sourceFile));
|
|
184
|
+
for (const lineNo of entry.coveredLines) {
|
|
185
|
+
result.push(String(lineNo));
|
|
144
186
|
}
|
|
145
187
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
const [lines, content] = toSimpleCoverage();
|
|
149
|
-
fs.writeFileSync(filePath.trim(), content, { flag: 'w', encoding: 'utf8' });
|
|
150
|
-
return lines;
|
|
188
|
+
}
|
|
189
|
+
return [result.length, result.join('\n')];
|
|
151
190
|
}
|
|
152
191
|
/**
|
|
153
192
|
* {@inheritDoc IReadableStorage.getProjects}
|
|
@@ -155,5 +194,11 @@ class DataStorage {
|
|
|
155
194
|
getProjects() {
|
|
156
195
|
return Array.from(this.coverageByProject.keys());
|
|
157
196
|
}
|
|
197
|
+
/**
|
|
198
|
+
* {@inheritDoc IWritableStorage.discardCollectedCoverage}
|
|
199
|
+
*/
|
|
200
|
+
discardCollectedCoverage() {
|
|
201
|
+
this.coverageByProject.clear();
|
|
202
|
+
}
|
|
158
203
|
}
|
|
159
204
|
exports.DataStorage = DataStorage;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { ConfigParameters } from '../utils/ConfigParameters';
|
|
2
|
+
import Logger from 'bunyan';
|
|
3
|
+
/**
|
|
4
|
+
* Uploads a coverage file to artifactory with the provided configuration.
|
|
5
|
+
*/
|
|
6
|
+
export declare function uploadToArtifactory(config: ConfigParameters, logger: Logger, coverageFile: string, lines: number): Promise<void>;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.uploadToArtifactory = void 0;
|
|
7
|
+
const CommonUpload_1 = require("./CommonUpload");
|
|
8
|
+
const axios_1 = __importDefault(require("axios"));
|
|
9
|
+
const ProxyUpload_1 = require("./ProxyUpload");
|
|
10
|
+
/**
|
|
11
|
+
* Uploads a coverage file to artifactory with the provided configuration.
|
|
12
|
+
*/
|
|
13
|
+
async function uploadToArtifactory(config, logger, coverageFile, lines) {
|
|
14
|
+
if (!(config.artifactory_access_token || (config.artifactory_user && config.artifactory_password))) {
|
|
15
|
+
throw new CommonUpload_1.UploadError('API key or user name and password must be configured!');
|
|
16
|
+
}
|
|
17
|
+
if (lines === 0) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
logger.info('Preparing upload to Artifactory');
|
|
21
|
+
const form = (0, CommonUpload_1.prepareFormData)(coverageFile);
|
|
22
|
+
await performArtifactoryUpload(config, form, logger);
|
|
23
|
+
}
|
|
24
|
+
exports.uploadToArtifactory = uploadToArtifactory;
|
|
25
|
+
async function performArtifactoryUpload(config, form, logger) {
|
|
26
|
+
var _a;
|
|
27
|
+
if (!config.teamscale_commit) {
|
|
28
|
+
throw new CommonUpload_1.UploadError('The "--teamscale-commit" option must be set with a valid branch and timestamp.');
|
|
29
|
+
}
|
|
30
|
+
const branchAndTimestamp = config.teamscale_commit.split(':');
|
|
31
|
+
let url = `${(_a = config.artifactory_server_url) === null || _a === void 0 ? void 0 : _a.replace(/\/$/, '')}/uploads/${branchAndTimestamp[0]}/${branchAndTimestamp[1]}`;
|
|
32
|
+
if (config.teamscale_revision) {
|
|
33
|
+
url = url + `-${config.teamscale_revision}`;
|
|
34
|
+
}
|
|
35
|
+
url = url + `/${config.teamscale_partition}/simple`;
|
|
36
|
+
if (config.artifactory_path_suffix !== undefined) {
|
|
37
|
+
url = `${url}/${config.artifactory_path_suffix}`;
|
|
38
|
+
}
|
|
39
|
+
url = `${url}/report.simple`;
|
|
40
|
+
await (0, CommonUpload_1.performUpload)(url, form, prepareArtifactoryConfig(config, form), axios_1.default.put, logger);
|
|
41
|
+
}
|
|
42
|
+
function prepareArtifactoryConfig(config, form) {
|
|
43
|
+
var _a, _b;
|
|
44
|
+
const proxyConfig = (0, ProxyUpload_1.extractProxyOptions)(config);
|
|
45
|
+
if (config.artifactory_access_token) {
|
|
46
|
+
return {
|
|
47
|
+
headers: {
|
|
48
|
+
Accept: '*/*',
|
|
49
|
+
'X-JFrog-Art-Api': config.artifactory_access_token,
|
|
50
|
+
'Content-Type': `multipart/form-data; boundary=${form.getBoundary()}`
|
|
51
|
+
},
|
|
52
|
+
proxy: proxyConfig
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
auth: {
|
|
57
|
+
username: (_a = config.artifactory_user) !== null && _a !== void 0 ? _a : 'no username provided',
|
|
58
|
+
password: (_b = config.artifactory_password) !== null && _b !== void 0 ? _b : 'no password provided'
|
|
59
|
+
},
|
|
60
|
+
headers: {
|
|
61
|
+
Accept: '*/*',
|
|
62
|
+
'Content-Type': `multipart/form-data; boundary=${form.getBoundary()}`
|
|
63
|
+
},
|
|
64
|
+
proxy: proxyConfig
|
|
65
|
+
};
|
|
66
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import FormData from 'form-data';
|
|
2
|
+
import Logger from 'bunyan';
|
|
3
|
+
import { AxiosRequestConfig, AxiosResponse } from 'axios';
|
|
4
|
+
/**
|
|
5
|
+
* Error that is thrown when the upload failed
|
|
6
|
+
*/
|
|
7
|
+
export declare class UploadError extends Error {
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Prepares the form data from a given configuration file for the upload.
|
|
11
|
+
*/
|
|
12
|
+
export declare function prepareFormData(coverageFile: string): FormData;
|
|
13
|
+
/**
|
|
14
|
+
* Uploads a coverage file with the provided configuration.
|
|
15
|
+
*/
|
|
16
|
+
export declare function performUpload(url: string, form: FormData, config: AxiosRequestConfig<FormData>, uploadFunction: <T = unknown, R = AxiosResponse<T>, D = FormData>(url: string, data?: D, config?: AxiosRequestConfig<D>) => Promise<R>, logger: Logger): Promise<void>;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.performUpload = exports.prepareFormData = exports.UploadError = void 0;
|
|
7
|
+
const form_data_1 = __importDefault(require("form-data"));
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const axios_1 = __importDefault(require("axios"));
|
|
10
|
+
const util_1 = require("util");
|
|
11
|
+
/**
|
|
12
|
+
* Error that is thrown when the upload failed
|
|
13
|
+
*/
|
|
14
|
+
class UploadError extends Error {
|
|
15
|
+
}
|
|
16
|
+
exports.UploadError = UploadError;
|
|
17
|
+
/**
|
|
18
|
+
* Prepares the form data from a given configuration file for the upload.
|
|
19
|
+
*/
|
|
20
|
+
function prepareFormData(coverageFile) {
|
|
21
|
+
const form = new form_data_1.default();
|
|
22
|
+
form.append('report', fs_1.default.readFileSync(coverageFile), 'coverage.simple');
|
|
23
|
+
return form;
|
|
24
|
+
}
|
|
25
|
+
exports.prepareFormData = prepareFormData;
|
|
26
|
+
/**
|
|
27
|
+
* Uploads a coverage file with the provided configuration.
|
|
28
|
+
*/
|
|
29
|
+
async function performUpload(url, form, config, uploadFunction, logger) {
|
|
30
|
+
try {
|
|
31
|
+
const response = await uploadFunction(url, form, config);
|
|
32
|
+
logger.info(`Upload finished with code ${response.status}.`);
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
if (axios_1.default.isAxiosError(error)) {
|
|
36
|
+
if (error.response) {
|
|
37
|
+
const response = error.response;
|
|
38
|
+
if (response.status >= 400) {
|
|
39
|
+
throw new UploadError(`Upload failed with code ${response.status}: ${response.statusText}. Response Data: ${response.data}`);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
logger.info(`Upload with status code ${response.status} finished.`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
else if (error.request) {
|
|
46
|
+
throw new UploadError(`Upload request did not receive a response.`);
|
|
47
|
+
}
|
|
48
|
+
if (error.message) {
|
|
49
|
+
logger.debug(`Something went wrong when uploading data: ${error.message}. Details of the error: ${(0, util_1.inspect)(error)}`);
|
|
50
|
+
throw new UploadError(`Something went wrong when uploading data: ${error.message}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
throw new UploadError(`Something went wrong when uploading data: ${(0, util_1.inspect)(error)}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
exports.performUpload = performUpload;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { ConfigParameters } from "../utils/ConfigParameters";
|
|
2
|
+
import { AxiosProxyConfig } from "axios";
|
|
3
|
+
/**
|
|
4
|
+
* Creates an AxiosProxyConfig object if proxy variables are provided.
|
|
5
|
+
*/
|
|
6
|
+
export declare function extractProxyOptions(config: ConfigParameters): AxiosProxyConfig | undefined;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.extractProxyOptions = void 0;
|
|
7
|
+
const url_1 = __importDefault(require("url"));
|
|
8
|
+
/**
|
|
9
|
+
* Creates an AxiosProxyConfig object if proxy variables are provided.
|
|
10
|
+
*/
|
|
11
|
+
function extractProxyOptions(config) {
|
|
12
|
+
if (config.http_proxy) {
|
|
13
|
+
// Expected format: http://username:password@host:port/
|
|
14
|
+
// See https://nodejs.org/api/url.html#url-strings-and-url-objects for URL parsing
|
|
15
|
+
const proxyAddress = new url_1.default.URL(config.http_proxy);
|
|
16
|
+
const proxyConfig = {
|
|
17
|
+
protocol: proxyAddress.protocol.replace(':', ''),
|
|
18
|
+
host: proxyAddress.hostname,
|
|
19
|
+
port: +proxyAddress.port,
|
|
20
|
+
};
|
|
21
|
+
if (proxyAddress.username && proxyAddress.password) {
|
|
22
|
+
proxyConfig.auth = {
|
|
23
|
+
username: proxyAddress.username,
|
|
24
|
+
password: proxyAddress.password
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
return proxyConfig;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
exports.extractProxyOptions = extractProxyOptions;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { ConfigParameters } from '../utils/ConfigParameters';
|
|
2
|
+
import Logger from 'bunyan';
|
|
3
|
+
/**
|
|
4
|
+
* Uploads a coverage file to Teamscale with the provided configuration.
|
|
5
|
+
*/
|
|
6
|
+
export declare function uploadToTeamscale(config: ConfigParameters, logger: Logger, coverageFile: string, lines: number): Promise<void>;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.uploadToTeamscale = void 0;
|
|
7
|
+
const QueryParameters_1 = __importDefault(require("../utils/QueryParameters"));
|
|
8
|
+
const CommonUpload_1 = require("./CommonUpload");
|
|
9
|
+
const axios_1 = __importDefault(require("axios"));
|
|
10
|
+
const ProxyUpload_1 = require("./ProxyUpload");
|
|
11
|
+
/**
|
|
12
|
+
* Uploads a coverage file to Teamscale with the provided configuration.
|
|
13
|
+
*/
|
|
14
|
+
async function uploadToTeamscale(config, logger, coverageFile, lines) {
|
|
15
|
+
if (!(config.teamscale_access_token && config.teamscale_user && config.teamscale_server_url)) {
|
|
16
|
+
throw new CommonUpload_1.UploadError('API key and user name must be configured!');
|
|
17
|
+
}
|
|
18
|
+
if (lines === 0) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
logger.info('Preparing upload to Teamscale');
|
|
22
|
+
const form = (0, CommonUpload_1.prepareFormData)(coverageFile);
|
|
23
|
+
const queryParameters = prepareQueryParameters(config);
|
|
24
|
+
await performTeamscaleUpload(config, queryParameters, form, logger);
|
|
25
|
+
}
|
|
26
|
+
exports.uploadToTeamscale = uploadToTeamscale;
|
|
27
|
+
async function performTeamscaleUpload(config, parameters, form, logger) {
|
|
28
|
+
var _a;
|
|
29
|
+
await (0, CommonUpload_1.performUpload)(`${(_a = config.teamscale_server_url) === null || _a === void 0 ? void 0 : _a.replace(/\/$/, '')}/api/projects/${config.teamscale_project}/external-analysis/session/auto-create/report?${parameters.toString()}`, form, prepareTeamscaleConfig(config, form), axios_1.default.post, logger);
|
|
30
|
+
}
|
|
31
|
+
function prepareQueryParameters(config) {
|
|
32
|
+
const parameters = new QueryParameters_1.default();
|
|
33
|
+
parameters.addIfDefined('format', 'SIMPLE');
|
|
34
|
+
parameters.addIfDefined('message', config.teamscale_message);
|
|
35
|
+
parameters.addIfDefined('repository', config.teamscale_repository);
|
|
36
|
+
parameters.addIfDefined('t', config.teamscale_commit);
|
|
37
|
+
parameters.addIfDefined('revision', config.teamscale_revision);
|
|
38
|
+
parameters.addIfDefined('partition', config.teamscale_partition);
|
|
39
|
+
return parameters;
|
|
40
|
+
}
|
|
41
|
+
function prepareTeamscaleConfig(config, form) {
|
|
42
|
+
var _a, _b;
|
|
43
|
+
return {
|
|
44
|
+
auth: {
|
|
45
|
+
username: (_a = config.teamscale_user) !== null && _a !== void 0 ? _a : 'no username provided',
|
|
46
|
+
password: (_b = config.teamscale_access_token) !== null && _b !== void 0 ? _b : 'no password provided'
|
|
47
|
+
},
|
|
48
|
+
headers: {
|
|
49
|
+
Accept: '*/*',
|
|
50
|
+
'Content-Type': `multipart/form-data; boundary=${form.getBoundary()}`
|
|
51
|
+
},
|
|
52
|
+
proxy: (0, ProxyUpload_1.extractProxyOptions)(config)
|
|
53
|
+
};
|
|
54
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { ArgumentParser } from 'argparse';
|
|
2
|
+
/**
|
|
3
|
+
* The command line parameters the profiler can be configured with.
|
|
4
|
+
*
|
|
5
|
+
* ATTENTION: We use snake_case here because ArgParse creates
|
|
6
|
+
* the parameters that way---as in Python from which ArgParse stems.
|
|
7
|
+
*/
|
|
8
|
+
export type ConfigParameters = {
|
|
9
|
+
dump_to_folder: string;
|
|
10
|
+
log_to_file: string;
|
|
11
|
+
keep_coverage_files: boolean;
|
|
12
|
+
log_level: string;
|
|
13
|
+
dump_after_mins: number;
|
|
14
|
+
port: number;
|
|
15
|
+
json_log: boolean;
|
|
16
|
+
teamscale_server_url?: string;
|
|
17
|
+
teamscale_access_token?: string;
|
|
18
|
+
teamscale_project?: string;
|
|
19
|
+
teamscale_user?: string;
|
|
20
|
+
teamscale_partition?: string;
|
|
21
|
+
teamscale_revision?: string;
|
|
22
|
+
teamscale_commit?: string;
|
|
23
|
+
teamscale_repository?: string;
|
|
24
|
+
teamscale_message?: string;
|
|
25
|
+
artifactory_server_url?: string;
|
|
26
|
+
artifactory_user?: string;
|
|
27
|
+
artifactory_password?: string;
|
|
28
|
+
artifactory_access_token?: string;
|
|
29
|
+
artifactory_path_suffix?: string;
|
|
30
|
+
enable_control_port?: number;
|
|
31
|
+
http_proxy?: string;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Construct the object for parsing the command line arguments.
|
|
35
|
+
*/
|
|
36
|
+
export declare function buildParameterParser(): ArgumentParser;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildParameterParser = void 0;
|
|
4
|
+
const argparse_1 = require("argparse");
|
|
5
|
+
const package_json_1 = require("../../package.json");
|
|
6
|
+
/**
|
|
7
|
+
* Construct the object for parsing the command line arguments.
|
|
8
|
+
*/
|
|
9
|
+
function buildParameterParser() {
|
|
10
|
+
var _a;
|
|
11
|
+
const parser = new argparse_1.ArgumentParser({
|
|
12
|
+
description: 'Collector of the Teamscale JavaScript Profiler. Collects coverage information from a' +
|
|
13
|
+
'(headless) Web browser that executes code instrumented with our instrumenter.'
|
|
14
|
+
});
|
|
15
|
+
parser.add_argument('-v', '--version', { action: 'version', version: package_json_1.version });
|
|
16
|
+
parser.add_argument('-p', '--port', { help: 'The port to receive coverage information on.', default: 54678 });
|
|
17
|
+
parser.add_argument('-f', '--dump-to-folder', {
|
|
18
|
+
help: 'Target folder for coverage files.',
|
|
19
|
+
default: 'coverage'
|
|
20
|
+
});
|
|
21
|
+
parser.add_argument('-k', '--keep-coverage-files', {
|
|
22
|
+
help: 'Whether to keep the coverage files on disk after a successful upload to Teamsacle',
|
|
23
|
+
action: 'store_true',
|
|
24
|
+
default: false
|
|
25
|
+
});
|
|
26
|
+
parser.add_argument('-l', '--log-to-file', { help: 'Log file', default: 'logs/collector-combined.log' });
|
|
27
|
+
parser.add_argument('-e', '--log-level', { help: 'Log level', default: 'info' });
|
|
28
|
+
parser.add_argument('-c', '--enable-control-port', {
|
|
29
|
+
help: 'Enables the remote control API on the specified port (<=0 means "disabled").',
|
|
30
|
+
default: 0
|
|
31
|
+
});
|
|
32
|
+
parser.add_argument('-t', '--dump-after-mins', {
|
|
33
|
+
help: 'Dump the coverage information to the target file every N minutes.',
|
|
34
|
+
default: 360
|
|
35
|
+
});
|
|
36
|
+
parser.add_argument('-d', '--debug', {
|
|
37
|
+
help: 'Print received coverage information to the terminal?',
|
|
38
|
+
default: false
|
|
39
|
+
});
|
|
40
|
+
parser.add_argument('-j', '--json-log', {
|
|
41
|
+
help: 'Additional JSON-like log file format.',
|
|
42
|
+
action: 'store_true'
|
|
43
|
+
});
|
|
44
|
+
// Parameters for the upload to Teamscale
|
|
45
|
+
parser.add_argument('-u', '--teamscale-server-url', {
|
|
46
|
+
help: 'Upload the coverage to the given Teamscale server URL, for example, https://teamscale.dev.example.com:8080/production.',
|
|
47
|
+
default: process.env.TEAMSCALE_SERVER_URL
|
|
48
|
+
});
|
|
49
|
+
parser.add_argument('--teamscale-access-token', {
|
|
50
|
+
help: 'The API key to use for uploading to Teamscale.',
|
|
51
|
+
default: process.env.TEAMSCALE_ACCESS_TOKEN
|
|
52
|
+
});
|
|
53
|
+
parser.add_argument('--teamscale-project', {
|
|
54
|
+
help: 'The project ID to upload coverage to.',
|
|
55
|
+
default: process.env.TEAMSCALE_PROJECT
|
|
56
|
+
});
|
|
57
|
+
parser.add_argument('--teamscale-user', {
|
|
58
|
+
help: 'The user for uploading coverage to Teamscale.',
|
|
59
|
+
default: process.env.TEAMSCALE_USER
|
|
60
|
+
});
|
|
61
|
+
parser.add_argument('--teamscale-partition', {
|
|
62
|
+
help: 'The partition to upload coverage to.',
|
|
63
|
+
default: process.env.TEAMSCALE_PARTITION
|
|
64
|
+
});
|
|
65
|
+
parser.add_argument('--teamscale-revision', {
|
|
66
|
+
help: 'The revision (commit hash, version id) to upload coverage for.',
|
|
67
|
+
default: process.env.TEAMSCALE_REVISION
|
|
68
|
+
});
|
|
69
|
+
parser.add_argument('--teamscale-commit', {
|
|
70
|
+
help: 'The branch and timestamp to upload coverage for, separated by colon.',
|
|
71
|
+
default: process.env.TEAMSCALE_COMMIT
|
|
72
|
+
});
|
|
73
|
+
parser.add_argument('--teamscale-repository', {
|
|
74
|
+
help: 'The repository to upload coverage for. Optional: Only needed when uploading via revision to a project that has more than one connector.',
|
|
75
|
+
default: process.env.TEAMSCALE_REPOSITORY
|
|
76
|
+
});
|
|
77
|
+
parser.add_argument('--teamscale-message', {
|
|
78
|
+
help: 'The commit message shown within Teamscale for the coverage upload. Default is "JavaScript coverage upload".',
|
|
79
|
+
default: (_a = process.env.TEAMSCALE_MESSAGE) !== null && _a !== void 0 ? _a : 'JavaScript coverage upload'
|
|
80
|
+
});
|
|
81
|
+
parser.add_argument('--artifactory-server-url', {
|
|
82
|
+
help: 'Upload the coverage to the given Artifactory server URL. The URL may include a subpath on the artifactory server, e.g. https://artifactory.acme.com/my-repo/my/subpath',
|
|
83
|
+
default: process.env.ARTIFACTORY_SERVER_URL
|
|
84
|
+
});
|
|
85
|
+
parser.add_argument('--artifactory-user', {
|
|
86
|
+
help: 'The user for uploading coverage to Artifactory. Only needed when not using the --artifactory-access-token option',
|
|
87
|
+
default: process.env.ARTIFACTORY_USER
|
|
88
|
+
});
|
|
89
|
+
parser.add_argument('--artifactory-password', {
|
|
90
|
+
help: 'The password for uploading coverage to Artifactory. Only needed when not using the --artifactory-access-token option',
|
|
91
|
+
default: process.env.ARTIFACTORY_PASSWORD
|
|
92
|
+
});
|
|
93
|
+
parser.add_argument('--artifactory-access-token', {
|
|
94
|
+
help: 'The access_token for uploading coverage to Artifactory.',
|
|
95
|
+
default: process.env.ARTIFACTORY_ACCESS_TOKEN
|
|
96
|
+
});
|
|
97
|
+
parser.add_argument('--artifactory-path-suffix', {
|
|
98
|
+
help: '(optional): The path within the storage location between the default path and the uploaded artifact.',
|
|
99
|
+
default: process.env.ARTIFACTORY_PATH_SUFFIX
|
|
100
|
+
});
|
|
101
|
+
parser.add_argument('--http-proxy', {
|
|
102
|
+
help: '(optional): The HTTP/HTTPS proxy address that should be used in the format: http://host:port/ or http://username:password@host:port/.',
|
|
103
|
+
default: process.env.HTTP_PROXY
|
|
104
|
+
});
|
|
105
|
+
return parser;
|
|
106
|
+
}
|
|
107
|
+
exports.buildParameterParser = buildParameterParser;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import 'dotenv/config';
|
|
3
|
+
import { WriteStream } from 'fs';
|
|
4
|
+
/**
|
|
5
|
+
* Class for log4j-like logger. Stream output shows less information than the
|
|
6
|
+
* standard JSON format of the bunyan logger and therefore has better readability.
|
|
7
|
+
*/
|
|
8
|
+
export declare class PrettyFileLogger {
|
|
9
|
+
outputStream: WriteStream;
|
|
10
|
+
constructor(outputStream: WriteStream);
|
|
11
|
+
write(rec: any): void;
|
|
12
|
+
end(): void;
|
|
13
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.PrettyFileLogger = void 0;
|
|
7
|
+
const bunyan_1 = __importDefault(require("bunyan"));
|
|
8
|
+
require("dotenv/config");
|
|
9
|
+
/**
|
|
10
|
+
* Class for log4j-like logger. Stream output shows less information than the
|
|
11
|
+
* standard JSON format of the bunyan logger and therefore has better readability.
|
|
12
|
+
*/
|
|
13
|
+
class PrettyFileLogger {
|
|
14
|
+
constructor(outputStream) {
|
|
15
|
+
this.outputStream = outputStream;
|
|
16
|
+
}
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
18
|
+
write(rec) {
|
|
19
|
+
this.outputStream.write(`[${rec.time.toISOString()}] ${bunyan_1.default.nameFromLevel[rec.level].toUpperCase()}: ${rec.msg}\n`);
|
|
20
|
+
}
|
|
21
|
+
end() {
|
|
22
|
+
this.outputStream.close();
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
exports.PrettyFileLogger = PrettyFileLogger;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
/**
|
|
4
|
+
* Helper class for building HTTP query parameter strings.
|
|
5
|
+
*/
|
|
6
|
+
class QueryParameters extends URLSearchParams {
|
|
7
|
+
/**
|
|
8
|
+
* Adds a parameter if the value is defined.
|
|
9
|
+
*/
|
|
10
|
+
addIfDefined(key, value) {
|
|
11
|
+
if (value) {
|
|
12
|
+
this.append(key, value);
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
this.delete(key);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
exports.default = QueryParameters;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.StdConsoleLogger = void 0;
|
|
7
|
+
const bunyan_1 = __importDefault(require("bunyan"));
|
|
8
|
+
require("dotenv/config");
|
|
9
|
+
/** Class for console logger. Doesn't print all information to ensure better readability. */
|
|
10
|
+
class StdConsoleLogger {
|
|
11
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
12
|
+
write(rec) {
|
|
13
|
+
console.log(`[${rec.time.toISOString()}] ${bunyan_1.default.nameFromLevel[rec.level].toUpperCase()}: ${rec.msg}`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
exports.StdConsoleLogger = StdConsoleLogger;
|