diff --git a/lib/all-badge-examples.js b/lib/all-badge-examples.js index e01e7d3e50..a6802e92dd 100644 --- a/lib/all-badge-examples.js +++ b/lib/all-badge-examples.js @@ -276,7 +276,7 @@ const allBadgeExamples = [ }, { title: 'Scrutinizer branch', - previewUri: '/scrutinizer/coverage/g/phpmyadmin/phpmyadmin/master.svg' + previewUri: '/scrutinizer/coverage/g/doctrine/doctrine2/master.svg' }, { title: 'Scrutinizer Build', diff --git a/server.js b/server.js index c478a88975..0a3f595d94 100644 --- a/server.js +++ b/server.js @@ -2569,136 +2569,65 @@ cache(function(data, match, sendBadge, request) { })); // Scrutinizer coverage integration. -camp.route(/^\/scrutinizer\/coverage\/(.*)\.(svg|png|gif|jpg|json)$/, +camp.route(/^\/scrutinizer(?:\/(build|coverage))?\/([^/]+\/[^/]+\/[^/]+|gp\/[^/])(?:\/(.+))?\.(svg|png|gif|jpg|json)$/, cache(function(data, match, sendBadge, request) { - var repo = match[1]; // eg, g/phpmyadmin/phpmyadmin - var format = match[2]; - // The repo may contain a branch, which would be unsuitable. - var repoParts = repo.split('/'); - var branch = null; - // Normally, there are 2 slashes in `repo` when the branch isn't specified. - var slashesInRepo = 2; - if (repoParts[0] === 'gp') { slashesInRepo = 1; } - if ((repoParts.length - 1) > slashesInRepo) { - branch = repoParts.slice(slashesInRepo + 1).join('/'); - repo = repoParts.slice(0, slashesInRepo + 1).join('/'); - } - var apiUrl = 'https://scrutinizer-ci.com/api/repositories/' + repo; - var badgeData = getBadgeData('coverage', data); + const type = match[1] ? match[1] : 'code quality'; + const repo = match[2]; // eg, g/phpmyadmin/phpmyadmin + let branch = match[3]; + const format = match[4]; + const apiUrl = `https://scrutinizer-ci.com/api/repositories/${repo}`; + const badgeData = getBadgeData(type, data); request(apiUrl, {}, function(err, res, buffer) { - if (err !== null) { - badgeData.text[1] = 'inaccessible'; + if (checkErrorResponse(badgeData, err, res, 'project or branch not found')) { sendBadge(format, badgeData); return; } try { - var data = JSON.parse(buffer); + const parsedData = JSON.parse(buffer); // Which branch are we dealing with? - if (branch === null) { branch = data.default_branch; } - var percentage = data.applications[branch].index._embedded - .project.metric_values['scrutinizer.test_coverage'] * 100; - badgeData.text[1] = percentage.toFixed(0) + '%'; - badgeData.colorscheme = coveragePercentageColor(percentage); - sendBadge(format, badgeData); - } catch(e) { - badgeData.text[1] = 'invalid'; - sendBadge(format, badgeData); - } - }); -})); - -// Scrutinizer build integration. -camp.route(/^\/scrutinizer\/build\/(.*)\.(svg|png|gif|jpg|json)$/, -cache(function(data, match, sendBadge, request) { - var repo = match[1]; // eg, g/phpmyadmin/phpmyadmin - var format = match[2]; - // The repo may contain a branch, which would be unsuitable. - var repoParts = repo.split('/'); - var branch = null; - // Normally, there are 2 slashes in `repo` when the branch isn't specified. - var slashesInRepo = 2; - if (repoParts[0] === 'gp') { slashesInRepo = 1; } - if ((repoParts.length - 1) > slashesInRepo) { - branch = repoParts.slice(slashesInRepo + 1).join('/'); - repo = repoParts.slice(0, slashesInRepo + 1).join('/'); - } - var apiUrl = 'https://scrutinizer-ci.com/api/repositories/' + repo; - var badgeData = getBadgeData('build', data); - request(apiUrl, {}, function(err, res, buffer) { - if (err !== null) { - badgeData.text[1] = 'inaccessible'; - sendBadge(format, badgeData); - return; - } - try { - var data = JSON.parse(buffer); - // Which branch are we dealing with? - if (branch === null) { branch = data.default_branch; } - var status = data.applications[branch].build_status.status; - badgeData.text[1] = status; - if (status === 'passed') { - badgeData.colorscheme = 'brightgreen'; - badgeData.text[1] = 'passing'; - } else if (status === 'failed' || status === 'error') { - badgeData.colorscheme = 'red'; - } else if (status === 'pending') { - badgeData.colorscheme = 'orange'; - } else if (status === 'unknown') { - badgeData.colorscheme = 'gray'; + if (branch === undefined) { + branch = parsedData.default_branch; } - sendBadge(format, badgeData); - - } catch(e) { - badgeData.text[1] = 'invalid'; - sendBadge(format, badgeData); - } - }); -})); - -// Scrutinizer integration. -camp.route(/^\/scrutinizer\/(.*)\.(svg|png|gif|jpg|json)$/, -cache(function(data, match, sendBadge, request) { - var repo = match[1]; // eg, g/phpmyadmin/phpmyadmin - var format = match[2]; - // The repo may contain a branch, which would be unsuitable. - var repoParts = repo.split('/'); - var branch = null; - // Normally, there are 2 slashes in `repo` when the branch isn't specified. - var slashesInRepo = 2; - if (repoParts[0] === 'gp') { slashesInRepo = 1; } - if ((repoParts.length - 1) > slashesInRepo) { - branch = repoParts.slice(slashesInRepo + 1).join('/'); - repo = repoParts.slice(0, slashesInRepo + 1).join('/'); - } - var apiUrl = 'https://scrutinizer-ci.com/api/repositories/' + repo; - var badgeData = getBadgeData('code quality', data); - request(apiUrl, {}, function(err, res, buffer) { - if (err !== null) { - badgeData.text[1] = 'inaccessible'; - sendBadge(format, badgeData); - return; - } - try { - var data = JSON.parse(buffer); - // Which branch are we dealing with? - if (branch === null) { branch = data.default_branch; } - var score = data.applications[branch].index._embedded - .project.metric_values['scrutinizer.quality']; - score = Math.round(score * 100) / 100; - badgeData.text[1] = score; - badgeData.colorscheme = 'blue'; - if (score > 9) { - badgeData.colorscheme = 'brightgreen'; - } else if (score > 7) { - badgeData.colorscheme = 'green'; - } else if (score > 5) { - badgeData.colorscheme = 'yellow'; - } else if (score > 4) { - badgeData.colorscheme = 'orange'; + if (type === 'coverage') { + const percentage = parsedData.applications[branch].index._embedded + .project.metric_values['scrutinizer.test_coverage'] * 100; + if (isNaN(percentage)) { + badgeData.text[1] = 'unknown'; + badgeData.colorscheme = 'gray'; + } else { + badgeData.text[1] = percentage.toFixed(0) + '%'; + badgeData.colorscheme = coveragePercentageColor(percentage); + } + } else if (type === 'build') { + const status = parsedData.applications[branch].build_status.status; + badgeData.text[1] = status; + if (status === 'passed') { + badgeData.colorscheme = 'brightgreen'; + badgeData.text[1] = 'passing'; + } else if (status === 'failed' || status === 'error') { + badgeData.colorscheme = 'red'; + } else if (status === 'pending') { + badgeData.colorscheme = 'orange'; + } else if (status === 'unknown') { + badgeData.colorscheme = 'gray'; + } } else { - badgeData.colorscheme = 'red'; + let score = parsedData.applications[branch].index._embedded + .project.metric_values['scrutinizer.quality']; + score = Math.round(score * 100) / 100; + badgeData.text[1] = score; + if (score > 9) { + badgeData.colorscheme = 'brightgreen'; + } else if (score > 7) { + badgeData.colorscheme = 'green'; + } else if (score > 5) { + badgeData.colorscheme = 'yellow'; + } else if (score > 4) { + badgeData.colorscheme = 'orange'; + } else { + badgeData.colorscheme = 'red'; + } } - sendBadge(format, badgeData); } catch(e) { badgeData.text[1] = 'invalid'; diff --git a/services/scrutinizer/scrutinizer.tester.js b/services/scrutinizer/scrutinizer.tester.js new file mode 100644 index 0000000000..c1ea54d4ed --- /dev/null +++ b/services/scrutinizer/scrutinizer.tester.js @@ -0,0 +1,77 @@ +'use strict'; + +const Joi = require('joi'); +const ServiceTester = require('../service-tester'); +const { + isIntegerPercentage +} = require('../test-validators'); +const isBuildStatus = Joi.string().regex(/^(passing|failed|error|pending|unknown)$/); + +const t = new ServiceTester({ id: 'scrutinizer', title: 'Scrutinizer' }); +module.exports = t; + +t.create('code quality') + .get('/g/filp/whoops.json') + .expectJSONTypes(Joi.object().keys({ + name: 'code quality', + value: Joi.number().positive(), + })); + +t.create('code quality (branch)') + .get('/g/phpmyadmin/phpmyadmin/master.json') + .expectJSONTypes(Joi.object().keys({ + name: 'code quality', + value: Joi.number().positive(), + })); + +t.create('code coverage') + .get('/coverage/g/filp/whoops.json') + .expectJSONTypes(Joi.object().keys({ + name: 'coverage', + value: isIntegerPercentage, + })); + +t.create('code coverage (branch)') + .get('/coverage/g/doctrine/doctrine2/master.json') + .expectJSONTypes(Joi.object().keys({ + name: 'coverage', + value: isIntegerPercentage, + })); + +t.create('build') + .get('/build/g/filp/whoops.json') + .expectJSONTypes(Joi.object().keys({ + name: 'build', + value: isBuildStatus, + })); + +t.create('build (branch)') + .get('/build/g/phpmyadmin/phpmyadmin/master.json') + .expectJSONTypes(Joi.object().keys({ + name: 'build', + value: isBuildStatus, + })); + +t.create('project not found') + .get('/build/g/does-not-exist/does-not-exist.json') + .expectJSON({ + name: 'build', + value: 'project or branch not found', + }); + +t.create('code coverage unknown') + .get('/coverage/g/phpmyadmin/phpmyadmin/master.json') + .expectJSON({ + name: 'coverage', + value: 'unknown', + }); + +t.create('unexpected response data') + .get('/coverage/g/filp/whoops.json') + .intercept(nock => nock('https://scrutinizer-ci.com') + .get('/api/repositories/g/filp/whoops') + .reply(200, '{"unexpected":"data"}')) + .expectJSON({ + name: 'coverage', + value: 'invalid', + });