screwdriver-coverage-sonar 3.1.5 → 3.3.2
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/commands.txt +1 -1
- package/index.js +82 -76
- package/package.json +11 -12
package/commands.txt
CHANGED
|
@@ -15,7 +15,7 @@ SCANNER_PATH="/opt/sd/sonarscanner-cli-${TARGET_OS}/bin/sonar-scanner"
|
|
|
15
15
|
if [ -s ${SCANNER_PATH} ] && [ -x ${SCANNER_PATH} ]; then eval $SCANNER_PATH $SCANNER_OPTION || true; exit 0; fi
|
|
16
16
|
if unzip -version > /dev/null 2>&1; then SD_UNZIP_CMD_WRAPPER="eval"; else SD_UNZIP_CMD_WRAPPER="sd-step exec --pkg-version 6.0 core/unzip"; fi
|
|
17
17
|
export SD_UNZIP_CMD_WRAPPER
|
|
18
|
-
SD_SONAR_SCANNER_CLI_VERSION=4.
|
|
18
|
+
SD_SONAR_SCANNER_CLI_VERSION=4.6.2.2472
|
|
19
19
|
$SD_CURL_CMD_WRAPPER "curl -o ./sonarscanner.zip -L https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${SD_SONAR_SCANNER_CLI_VERSION}-${TARGET_OS}.zip"
|
|
20
20
|
$SD_UNZIP_CMD_WRAPPER "unzip -q ./sonarscanner.zip"
|
|
21
21
|
SCANNER_PATH="sonar-scanner-${SD_SONAR_SCANNER_CLI_VERSION}-${TARGET_OS}/bin/sonar-scanner"
|
package/index.js
CHANGED
|
@@ -6,7 +6,7 @@ const fs = require('fs');
|
|
|
6
6
|
const joi = require('joi');
|
|
7
7
|
const hoek = require('@hapi/hoek');
|
|
8
8
|
const path = require('path');
|
|
9
|
-
const request = require('request
|
|
9
|
+
const request = require('screwdriver-request');
|
|
10
10
|
const uuidv4 = require('uuid/v4');
|
|
11
11
|
const logger = require('screwdriver-logger');
|
|
12
12
|
const CoverageBase = require('screwdriver-coverage-base');
|
|
@@ -28,14 +28,10 @@ let sonarGitAppName;
|
|
|
28
28
|
*/
|
|
29
29
|
function createProject(projectKey) {
|
|
30
30
|
return request({
|
|
31
|
-
json: true,
|
|
32
31
|
method: 'POST',
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
username: adminToken
|
|
37
|
-
}
|
|
38
|
-
}).catch((err) => {
|
|
32
|
+
url: `${sonarHost}/api/projects/create?project=${projectKey}&name=${projectKey}`,
|
|
33
|
+
username: adminToken
|
|
34
|
+
}).catch(err => {
|
|
39
35
|
if (err.statusCode === 400 && err.message.includes('already exists')) {
|
|
40
36
|
return {};
|
|
41
37
|
}
|
|
@@ -53,14 +49,10 @@ function createProject(projectKey) {
|
|
|
53
49
|
*/
|
|
54
50
|
function createUser(username, password) {
|
|
55
51
|
return request({
|
|
56
|
-
json: true,
|
|
57
52
|
method: 'POST',
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
username: adminToken
|
|
62
|
-
}
|
|
63
|
-
}).catch((err) => {
|
|
53
|
+
url: `${sonarHost}/api/users/create?login=${username}&name=${username}&password=${password}`,
|
|
54
|
+
username: adminToken
|
|
55
|
+
}).catch(err => {
|
|
64
56
|
if (err.statusCode === 400 && err.message.includes('already exists')) {
|
|
65
57
|
return {};
|
|
66
58
|
}
|
|
@@ -82,12 +74,9 @@ function configureGitApp(projectKey, projectName) {
|
|
|
82
74
|
|
|
83
75
|
// Check if binding exists
|
|
84
76
|
return request({
|
|
85
|
-
json: true,
|
|
86
77
|
method: 'GET',
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
username: adminToken
|
|
90
|
-
}
|
|
78
|
+
url: `${sonarHost}/api/alm_settings/get_binding?project=${componentId}`,
|
|
79
|
+
username: adminToken
|
|
91
80
|
}).catch(() => {
|
|
92
81
|
// if binding does not exist, add it
|
|
93
82
|
logger.info(`Binding does not exist for Sonar project ${projectKey}, adding`);
|
|
@@ -96,19 +85,14 @@ function configureGitApp(projectKey, projectName) {
|
|
|
96
85
|
return Promise.resolve();
|
|
97
86
|
}
|
|
98
87
|
|
|
99
|
-
// eslint-disable-next-line max-len
|
|
100
88
|
const parameters = `almSetting=${gitAppEncoded}&project=${componentId}&repository=${projectName}&summaryCommentEnabled=true`;
|
|
101
89
|
|
|
102
90
|
return request({
|
|
103
|
-
json: true,
|
|
104
91
|
method: 'POST',
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
109
|
-
}).catch((error) => {
|
|
92
|
+
url: `${sonarHost}/api/alm_settings/set_github_binding?${parameters}`,
|
|
93
|
+
username: adminToken
|
|
94
|
+
}).catch(error => {
|
|
110
95
|
// if cannot configure app, do not throw err
|
|
111
|
-
// eslint-disable-next-line max-len
|
|
112
96
|
logger.error(`Failed to configure Git App ${gitApp} for Sonar project ${projectKey}: ${error.message}`);
|
|
113
97
|
|
|
114
98
|
return Promise.resolve();
|
|
@@ -126,14 +110,10 @@ function configureGitApp(projectKey, projectName) {
|
|
|
126
110
|
function grantUserPermission(username, projectKey) {
|
|
127
111
|
// Always return 204 even with duplicate calls
|
|
128
112
|
return request({
|
|
129
|
-
json: true,
|
|
130
113
|
method: 'POST',
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
username: adminToken
|
|
135
|
-
}
|
|
136
|
-
}).catch((err) => {
|
|
114
|
+
url: `${sonarHost}/api/permissions/add_user?login=${username}&permission=scan&projectKey=${projectKey}`,
|
|
115
|
+
username: adminToken
|
|
116
|
+
}).catch(err => {
|
|
137
117
|
throw new Error(`Failed to grant user ${username} permission: ${err.message}`);
|
|
138
118
|
});
|
|
139
119
|
}
|
|
@@ -148,13 +128,10 @@ function generateToken(username) {
|
|
|
148
128
|
const tokenName = uuidv4();
|
|
149
129
|
|
|
150
130
|
return request({
|
|
151
|
-
json: true,
|
|
152
131
|
method: 'POST',
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
}
|
|
157
|
-
}).catch((err) => {
|
|
132
|
+
url: `${sonarHost}/api/user_tokens/generate?login=${username}&name=${tokenName}`,
|
|
133
|
+
username: adminToken
|
|
134
|
+
}).catch(err => {
|
|
158
135
|
throw new Error(`Failed to generate user ${username} token: ${err.message}`);
|
|
159
136
|
});
|
|
160
137
|
}
|
|
@@ -178,7 +155,6 @@ function getMetrics({ projectKey, startTime, endTime, prNum, sonarEnterprise: en
|
|
|
178
155
|
const parsedEndTime = endTime.replace(/\.(.*)/, timezoneOffset);
|
|
179
156
|
const from = encodeURIComponent(parsedStartTime);
|
|
180
157
|
const to = encodeURIComponent(parsedEndTime);
|
|
181
|
-
// eslint-disable-next-line max-len
|
|
182
158
|
let coverageUrl = `${sonarHost}/api/measures/search_history?component=${componentId}&metrics=tests,test_errors,test_failures,coverage&from=${from}&to=${to}&ps=1`;
|
|
183
159
|
|
|
184
160
|
if (enterpriseEnabled && prNum) {
|
|
@@ -186,18 +162,15 @@ function getMetrics({ projectKey, startTime, endTime, prNum, sonarEnterprise: en
|
|
|
186
162
|
}
|
|
187
163
|
|
|
188
164
|
return request({
|
|
189
|
-
json: true,
|
|
190
165
|
method: 'GET',
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
username: adminToken
|
|
194
|
-
}
|
|
166
|
+
url: coverageUrl,
|
|
167
|
+
username: adminToken
|
|
195
168
|
})
|
|
196
|
-
.then(
|
|
169
|
+
.then(result => {
|
|
197
170
|
const measures = {};
|
|
198
171
|
|
|
199
172
|
// measures in result is an array, covert it to an object with metric name as key
|
|
200
|
-
(hoek.reach(result, 'measures') || []).forEach(
|
|
173
|
+
(hoek.reach(result, 'body.measures') || []).forEach(measure => {
|
|
201
174
|
measures[measure.metric] = measure;
|
|
202
175
|
});
|
|
203
176
|
|
|
@@ -219,12 +192,13 @@ function getMetrics({ projectKey, startTime, endTime, prNum, sonarEnterprise: en
|
|
|
219
192
|
|
|
220
193
|
return metrics;
|
|
221
194
|
})
|
|
222
|
-
.catch(
|
|
195
|
+
.catch(err => {
|
|
223
196
|
// if there is no coverage measurement target, 404 and 'Component key not found' are returned and this is not an error
|
|
224
197
|
if (err.statusCode !== 404 || !/Component key '.*' not found/.test(err.message)) {
|
|
225
198
|
// if cannot get coverage, do not throw err
|
|
226
|
-
|
|
227
|
-
|
|
199
|
+
logger.error(
|
|
200
|
+
`Failed to get coverage and tests percentage for Sonar project ${projectKey}: ${err.message}`
|
|
201
|
+
);
|
|
228
202
|
}
|
|
229
203
|
|
|
230
204
|
return {
|
|
@@ -251,16 +225,24 @@ function getMetrics({ projectKey, startTime, endTime, prNum, sonarEnterprise: en
|
|
|
251
225
|
* @param {String} [config.prParentJobId] Screwdriver PR parent job ID
|
|
252
226
|
* @return {Object} Sonar project key, project name, and username
|
|
253
227
|
*/
|
|
254
|
-
function getProjectData({
|
|
255
|
-
|
|
228
|
+
function getProjectData({
|
|
229
|
+
scope,
|
|
230
|
+
enterpriseEnabled,
|
|
231
|
+
jobId: buildJobId,
|
|
232
|
+
jobName: buildJobName,
|
|
233
|
+
pipelineId,
|
|
234
|
+
pipelineName,
|
|
235
|
+
projectKey,
|
|
236
|
+
prParentJobId,
|
|
237
|
+
prNum
|
|
238
|
+
}) {
|
|
256
239
|
let jobId = buildJobId;
|
|
257
240
|
let jobName = buildJobName;
|
|
258
241
|
|
|
259
242
|
// Determine scope based on projectKey
|
|
260
243
|
if (projectKey) {
|
|
261
244
|
const [projectScope, id] = projectKey.split(':');
|
|
262
|
-
const projectName = projectScope === 'pipeline' ?
|
|
263
|
-
pipelineName : `${pipelineName}:${jobName}`;
|
|
245
|
+
const projectName = projectScope === 'pipeline' ? pipelineName : `${pipelineName}:${jobName}`;
|
|
264
246
|
const username = `user-${projectScope}-${id}`;
|
|
265
247
|
|
|
266
248
|
return {
|
|
@@ -272,7 +254,7 @@ function getProjectData({ scope, enterpriseEnabled, jobId: buildJobId,
|
|
|
272
254
|
}
|
|
273
255
|
|
|
274
256
|
// Use user-configured scope; otherwise figure out default scope: pipeline scope for enterprise edition, job scope for everything else
|
|
275
|
-
const userScope =
|
|
257
|
+
const userScope = scope && scope !== 'undefined' ? scope : undefined;
|
|
276
258
|
const coverageScope = userScope || (enterpriseEnabled ? 'pipeline' : 'job');
|
|
277
259
|
|
|
278
260
|
if (coverageScope === 'pipeline') {
|
|
@@ -316,14 +298,30 @@ class CoverageSonar extends CoverageBase {
|
|
|
316
298
|
constructor(config) {
|
|
317
299
|
super();
|
|
318
300
|
|
|
319
|
-
this.config = joi.attempt(
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
301
|
+
this.config = joi.attempt(
|
|
302
|
+
config,
|
|
303
|
+
joi
|
|
304
|
+
.object()
|
|
305
|
+
.keys({
|
|
306
|
+
sdApiUrl: joi
|
|
307
|
+
.string()
|
|
308
|
+
.uri()
|
|
309
|
+
.required(),
|
|
310
|
+
sdUiUrl: joi
|
|
311
|
+
.string()
|
|
312
|
+
.uri()
|
|
313
|
+
.required(),
|
|
314
|
+
sonarHost: joi
|
|
315
|
+
.string()
|
|
316
|
+
.uri()
|
|
317
|
+
.required(),
|
|
318
|
+
adminToken: joi.string().required(),
|
|
319
|
+
sonarEnterprise: joi.boolean().default(false),
|
|
320
|
+
sonarGitAppName: joi.string().default(DEFAULT_GIT_APP_NAME)
|
|
321
|
+
})
|
|
322
|
+
.unknown(true),
|
|
323
|
+
'Invalid config for sonar coverage plugin'
|
|
324
|
+
);
|
|
327
325
|
|
|
328
326
|
sdCoverageAuthUrl = `${this.config.sdApiUrl}/v4/coverage/token`;
|
|
329
327
|
adminToken = this.config.adminToken;
|
|
@@ -331,8 +329,7 @@ class CoverageSonar extends CoverageBase {
|
|
|
331
329
|
sonarEnterprise = this.config.sonarEnterprise;
|
|
332
330
|
sonarGitAppName = this.config.sonarGitAppName;
|
|
333
331
|
|
|
334
|
-
this.uploadCommands = COMMANDS
|
|
335
|
-
.replace('$SD_SONAR_HOST', sonarHost)
|
|
332
|
+
this.uploadCommands = COMMANDS.replace('$SD_SONAR_HOST', sonarHost)
|
|
336
333
|
.replace('$SD_UI_URL', this.config.sdUiUrl)
|
|
337
334
|
.replace('$SD_SONAR_ENTERPRISE', sonarEnterprise)
|
|
338
335
|
.split('\n');
|
|
@@ -354,8 +351,7 @@ class CoverageSonar extends CoverageBase {
|
|
|
354
351
|
* @return {Promise} An access token that build can use
|
|
355
352
|
* to talk to coverage server
|
|
356
353
|
*/
|
|
357
|
-
_getAccessToken({ scope, username, projectKey, projectName, jobName, pipelineName,
|
|
358
|
-
buildCredentials }) {
|
|
354
|
+
_getAccessToken({ scope, username, projectKey, projectName, jobName, pipelineName, buildCredentials }) {
|
|
359
355
|
const { jobId, pipelineId, prParentJobId } = buildCredentials;
|
|
360
356
|
let projectData = { username, projectKey, projectName };
|
|
361
357
|
|
|
@@ -379,7 +375,7 @@ class CoverageSonar extends CoverageBase {
|
|
|
379
375
|
.then(() => createUser(projectData.username, password))
|
|
380
376
|
.then(() => grantUserPermission(projectData.username, projectData.projectKey))
|
|
381
377
|
.then(() => generateToken(projectData.username))
|
|
382
|
-
.then(res => res.token);
|
|
378
|
+
.then(res => res.body.token);
|
|
383
379
|
}
|
|
384
380
|
|
|
385
381
|
/**
|
|
@@ -402,8 +398,18 @@ class CoverageSonar extends CoverageBase {
|
|
|
402
398
|
* - project url
|
|
403
399
|
* - Sonar env vars
|
|
404
400
|
*/
|
|
405
|
-
_getInfo({
|
|
406
|
-
|
|
401
|
+
_getInfo({
|
|
402
|
+
scope,
|
|
403
|
+
jobId,
|
|
404
|
+
jobName,
|
|
405
|
+
startTime,
|
|
406
|
+
endTime,
|
|
407
|
+
pipelineId,
|
|
408
|
+
pipelineName,
|
|
409
|
+
prNum,
|
|
410
|
+
projectKey: coverageProjectKey,
|
|
411
|
+
prParentJobId
|
|
412
|
+
}) {
|
|
407
413
|
const { projectScope, projectKey, projectName, username } = getProjectData({
|
|
408
414
|
enterpriseEnabled: sonarEnterprise,
|
|
409
415
|
jobId,
|
|
@@ -418,7 +424,6 @@ class CoverageSonar extends CoverageBase {
|
|
|
418
424
|
|
|
419
425
|
const infoObject = {
|
|
420
426
|
envVars: {
|
|
421
|
-
// eslint-disable-next-line max-len
|
|
422
427
|
SD_SONAR_AUTH_URL: `${sdCoverageAuthUrl}?projectKey=${projectKey}&projectName=${projectName}&username=${username}&scope=${projectScope}`,
|
|
423
428
|
SD_SONAR_HOST: sonarHost,
|
|
424
429
|
SD_SONAR_ENTERPRISE: sonarEnterprise,
|
|
@@ -429,8 +434,8 @@ class CoverageSonar extends CoverageBase {
|
|
|
429
434
|
|
|
430
435
|
// Only get coverage percentage if the steps are finished
|
|
431
436
|
if (projectKey && startTime && endTime) {
|
|
432
|
-
return getMetrics({ projectKey, startTime, endTime, prNum, sonarEnterprise })
|
|
433
|
-
|
|
437
|
+
return getMetrics({ projectKey, startTime, endTime, prNum, sonarEnterprise }).then(
|
|
438
|
+
({ coverage, tests }) => {
|
|
434
439
|
const componentId = encodeURIComponent(projectKey);
|
|
435
440
|
let projectUrl = `${this.config.sonarHost}/dashboard?id=${componentId}`;
|
|
436
441
|
|
|
@@ -443,7 +448,8 @@ class CoverageSonar extends CoverageBase {
|
|
|
443
448
|
infoObject.projectUrl = projectUrl;
|
|
444
449
|
|
|
445
450
|
return Promise.resolve(infoObject);
|
|
446
|
-
}
|
|
451
|
+
}
|
|
452
|
+
);
|
|
447
453
|
}
|
|
448
454
|
|
|
449
455
|
return Promise.resolve(infoObject);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "screwdriver-coverage-sonar",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.2",
|
|
4
4
|
"description": "SonarQube implementation of the coverage-base class",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -30,24 +30,23 @@
|
|
|
30
30
|
"Tiffany Kyi <tiffanykyi@gmail.com>"
|
|
31
31
|
],
|
|
32
32
|
"devDependencies": {
|
|
33
|
-
"chai": "^
|
|
34
|
-
"eslint": "^
|
|
35
|
-
"eslint-config-screwdriver": "^
|
|
33
|
+
"chai": "^4.2.0",
|
|
34
|
+
"eslint": "^7.31.0",
|
|
35
|
+
"eslint-config-screwdriver": "^5.0.1",
|
|
36
36
|
"mocha": "^8.3.2",
|
|
37
37
|
"mocha-multi-reporters": "^1.5.1",
|
|
38
38
|
"mocha-sonarqube-reporter": "^1.0.2",
|
|
39
39
|
"mockery": "^2.1.0",
|
|
40
40
|
"nyc": "^15.0.0",
|
|
41
|
-
"sinon": "^
|
|
41
|
+
"sinon": "^9.0.0"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@hapi/hoek": "^9.0
|
|
45
|
-
"joi": "^17.
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"screwdriver-
|
|
49
|
-
"
|
|
50
|
-
"uuid": "^3.2.1"
|
|
44
|
+
"@hapi/hoek": "^9.2.0",
|
|
45
|
+
"joi": "^17.4.1",
|
|
46
|
+
"screwdriver-coverage-base": "^2.1.0",
|
|
47
|
+
"screwdriver-logger": "^1.1.0",
|
|
48
|
+
"screwdriver-request": "^1.0.1",
|
|
49
|
+
"uuid": "^3.4.0"
|
|
51
50
|
},
|
|
52
51
|
"release": {
|
|
53
52
|
"debug": false,
|