refactor [Scrutinizer] (#3266)

* refactor(Scrutinizer): migrated to new BaseJsonService

* refactor(ScrutinizerCoverage): updated color scale to match

* refactor(Scrutinizer): switched to multiple classes to handle dif. git hosts

* refactor(Scrutinizer): finished migrating to new service arch.

* fix(Scrutinizer): fixed branch check logic

* refactor(Scrutinizer): inline transforms based on PR feedback

* refactor(ScrutinizerCoverage): change handling of no coverage scenario
This commit is contained in:
Caleb Cartwright
2019-04-26 21:50:28 -05:00
committed by GitHub
parent 01d745122b
commit 07a8d2ecbe
12 changed files with 809 additions and 309 deletions

View File

@@ -0,0 +1,42 @@
'use strict'
const { BaseJsonService, NotFound } = require('..')
module.exports = class ScrutinizerBase extends BaseJsonService {
// https://scrutinizer-ci.com/docs/api/#repository-details
async fetch({ schema, vcs, slug }) {
return this._requestJson({
schema,
url: `https://scrutinizer-ci.com/api/repositories/${vcs}/${slug}`,
errorMessages: {
401: 'not authorized to access project',
404: 'project not found',
},
})
}
transformBranchInfo({ json, wantedBranch }) {
if (!wantedBranch) {
return json.applications[json.default_branch]
}
const branch = json.applications[wantedBranch]
if (!branch) {
throw new NotFound({ prettyMessage: ' branch not found' })
}
return branch
}
transformBranchInfoMetricValue({ json, branch, metric }) {
const {
index: {
_embedded: {
project: { metric_values: metricValues },
},
},
} = this.transformBranchInfo({ json, wantedBranch: branch })
return { value: metricValues[metric] }
}
}

View File

@@ -0,0 +1,128 @@
'use strict'
const Joi = require('joi')
const { isBuildStatus, renderBuildStatusBadge } = require('../build-status')
const ScrutinizerBase = require('./scrutinizer-base')
const schema = Joi.object({
default_branch: Joi.string().required(),
applications: Joi.object()
.pattern(
/^/,
Joi.object({
build_status: Joi.object({
status: Joi.alternatives().try(isBuildStatus, Joi.equal('unknown')),
}).required(),
})
)
.required(),
}).required()
class ScrutinizerBuildBase extends ScrutinizerBase {
static get category() {
return 'build'
}
static get defaultBadgeData() {
return {
label: 'build',
}
}
async makeBadge({ vcs, slug, branch }) {
const json = await this.fetch({ schema, vcs, slug })
const {
build_status: { status },
} = this.transformBranchInfo({ json, wantedBranch: branch })
return renderBuildStatusBadge({ status })
}
}
class ScrutinizerBuild extends ScrutinizerBuildBase {
static get route() {
return {
base: 'scrutinizer/build',
pattern: ':vcs(g|b)/:user/:repo/:branch*',
}
}
static get examples() {
return [
{
title: 'Scrutinizer build (GitHub/Bitbucket)',
pattern: ':vcs(g|b)/:user/:repo/:branch?',
namedParams: {
vcs: 'g',
user: 'filp',
repo: 'whoops',
branch: 'master',
},
staticPreview: renderBuildStatusBadge({ status: 'passing' }),
},
]
}
async handle({ vcs, user, repo, branch }) {
return this.makeBadge({
vcs,
slug: `${user}/${repo}`,
branch,
})
}
}
class ScrutinizerGitLabBuild extends ScrutinizerBuildBase {
static get route() {
return {
base: 'scrutinizer/build/gl',
pattern: ':instance/:user/:repo/:branch*',
}
}
static get examples() {
// There are no known anonymous accessible Scrutinizer reports available for GitLab repos.
// The example used is valid, but the project will not be accessible if Shields users try to use it.
// https://gitlab.propertywindow.nl/propertywindow/client
// https://scrutinizer-ci.com/gl/propertywindow/propertywindow/client/badges/quality-score.png?b=master&s=dfae6992a48184cc2333b4c349cec0447f0d67c2
return [
{
title: 'Scrutinizer build (GitLab)',
pattern: ':instance/:user/:repo/:branch?',
namedParams: {
instance: 'propertywindow',
user: 'propertywindow',
repo: 'client',
branch: 'master',
},
staticPreview: renderBuildStatusBadge({ status: 'passing' }),
},
]
}
async handle({ instance, user, repo, branch }) {
return this.makeBadge({
vcs: 'gl',
slug: `${instance}/${user}/${repo}`,
branch,
})
}
}
class ScrutinizerPlainGitBuild extends ScrutinizerBuildBase {
static get route() {
return {
base: 'scrutinizer/build/gp',
pattern: ':slug/:branch*',
}
}
async handle({ slug, branch }) {
return this.makeBadge({ vcs: 'gp', slug, branch })
}
}
module.exports = [
ScrutinizerBuild,
ScrutinizerGitLabBuild,
ScrutinizerPlainGitBuild,
]

View File

@@ -0,0 +1,74 @@
'use strict'
const Joi = require('joi')
const { isBuildStatus } = require('../build-status')
const { ServiceTester } = require('../tester')
const t = (module.exports = new ServiceTester({
id: 'ScrutinizerBuild',
title: 'ScrutinizerBuild',
pathPrefix: '/scrutinizer/build',
}))
t.create('build (GitHub)')
.get('/g/filp/whoops.json')
.expectBadge({
label: 'build',
message: Joi.alternatives().try(isBuildStatus, Joi.equal('unknown')),
})
t.create('build (Bitbucket)')
.get('/b/atlassian/python-bitbucket.json')
.expectBadge({
label: 'build',
message: Joi.alternatives().try(isBuildStatus, Joi.equal('unknown')),
})
t.create('build (branch)')
.get('/g/phpmyadmin/phpmyadmin/master.json')
.expectBadge({
label: 'build',
message: Joi.alternatives().try(isBuildStatus, Joi.equal('unknown')),
})
t.create('build - unknown status')
.get('/g/filp/whoops.json')
.intercept(nock =>
nock('https://scrutinizer-ci.com')
.get('/api/repositories/g/filp/whoops')
.reply(200, {
default_branch: 'master',
applications: {
master: {
build_status: {
status: 'unknown',
},
},
},
})
)
.expectBadge({
label: 'build',
message: 'unknown',
color: 'lightgrey',
})
t.create('build private project')
.get('/gl/propertywindow/propertywindow/client.json')
.expectBadge({
label: 'build',
message: 'not authorized to access project',
})
t.create('build nonexistent project')
.get('/gp/foo.json')
.expectBadge({
label: 'build',
message: 'project not found',
})
t.create('build nonexistent branch')
.get('/g/phpmyadmin/phpmyadmin/super-fake/not-real-branch.json')
.expectBadge({
label: 'build',
message: 'branch not found',
})

View File

@@ -0,0 +1,160 @@
'use strict'
const Joi = require('joi')
const { NotFound } = require('..')
const { colorScale } = require('../color-formatters')
const ScrutinizerBase = require('./scrutinizer-base')
const schema = Joi.object({
default_branch: Joi.string().required(),
applications: Joi.object()
.pattern(
/^/,
Joi.object({
index: Joi.object({
_embedded: Joi.object({
project: Joi.object({
metric_values: Joi.object({
'scrutinizer.test_coverage': Joi.number().positive(),
}).required(),
}).required(),
}).required(),
}).required(),
})
)
.required(),
}).required()
// https://scrutinizer-ci.com/g/filp/whoops/code-structure/master/code-coverage
// < 40% - red
// 40-60% (inclusive) - yellow
// > 60% brightgreen
const scale = colorScale([40, 61], ['red', 'yellow', 'brightgreen'])
class ScrutinizerCoverageBase extends ScrutinizerBase {
static get category() {
return 'coverage'
}
static get defaultBadgeData() {
return {
label: 'coverage',
}
}
static render({ coverage }) {
return {
message: `${coverage.toFixed(0)}%`,
color: scale(coverage),
}
}
transform({ json, branch }) {
const { value: rawCoverage } = this.transformBranchInfoMetricValue({
json,
branch,
metric: 'scrutinizer.test_coverage',
})
if (!rawCoverage) {
throw new NotFound({ prettyMessage: 'coverage not found' })
}
return { coverage: rawCoverage * 100 }
}
async makeBadge({ vcs, slug, branch }) {
const json = await this.fetch({ schema, vcs, slug })
const { coverage } = this.transform({ json, branch })
return this.constructor.render({ coverage })
}
}
class ScrutinizerCoverage extends ScrutinizerCoverageBase {
static get route() {
return {
base: 'scrutinizer/coverage',
pattern: ':vcs(g|b)/:user/:repo/:branch*',
}
}
static get examples() {
return [
{
title: 'Scrutinizer coverage (GitHub/BitBucket)',
pattern: ':vcs(g|b)/:user/:repo/:branch?',
namedParams: {
vcs: 'g',
user: 'filp',
repo: 'whoops',
branch: 'master',
},
staticPreview: this.render({ coverage: 86 }),
},
]
}
async handle({ vcs, user, repo, branch }) {
return this.makeBadge({
vcs,
slug: `${user}/${repo}`,
branch,
})
}
}
class ScrutinizerCoverageGitLab extends ScrutinizerCoverageBase {
static get route() {
return {
base: 'scrutinizer/coverage/gl',
pattern: ':instance/:user/:repo/:branch*',
}
}
static get examples() {
// There are no known anonymous accessible Scrutinizer reports available for GitLab repos.
// The example used is valid, but the project will not be accessible if Shields users try to use it.
// https://gitlab.propertywindow.nl/propertywindow/client
// https://scrutinizer-ci.com/gl/propertywindow/propertywindow/client/badges/quality-score.png?b=master&s=dfae6992a48184cc2333b4c349cec0447f0d67c2
return [
{
title: 'Scrutinizer coverage (GitLab)',
pattern: ':instance/:user/:repo/:branch?',
namedParams: {
instance: 'propertywindow',
user: 'propertywindow',
repo: 'client',
branch: 'master',
},
staticPreview: this.render({ coverage: 94 }),
},
]
}
async handle({ instance, user, repo, branch }) {
return this.makeBadge({
vcs: 'gl',
slug: `${instance}/${user}/${repo}`,
branch,
})
}
}
class ScrutinizerCoveragePlainGit extends ScrutinizerCoverageBase {
static get route() {
return {
base: 'scrutinizer/coverage/gp',
pattern: ':slug/:branch*',
}
}
async handle({ slug, branch }) {
return this.makeBadge({ vcs: 'gp', slug, branch })
}
}
module.exports = [
ScrutinizerCoverage,
ScrutinizerCoverageGitLab,
ScrutinizerCoveragePlainGit,
]

View File

@@ -0,0 +1,54 @@
'use strict'
const { expect } = require('chai')
const { test, given } = require('sazerac')
const { NotFound } = require('..')
const [ScrutinizerCoverage] = require('./scrutinizer-coverage.service')
describe('ScrutinizerCoverage', function() {
test(ScrutinizerCoverage.render, () => {
given({ coverage: 39 }).expect({
message: '39%',
color: 'red',
})
given({ coverage: 40 }).expect({
message: '40%',
color: 'yellow',
})
given({ coverage: 60 }).expect({
message: '60%',
color: 'yellow',
})
given({ coverage: 61 }).expect({
message: '61%',
color: 'brightgreen',
})
})
context('transform()', function() {
it('throws NotFound error when there is no coverage data', function() {
try {
ScrutinizerCoverage.prototype.transform({
branch: 'master',
json: {
applications: {
master: {
index: {
_embedded: {
project: {
metric_values: {},
},
},
},
},
},
},
})
expect.fail('Expected to throw')
} catch (e) {
expect(e).to.be.an.instanceof(NotFound)
expect(e.prettyMessage).to.equal('coverage not found')
}
})
})
})

View File

@@ -0,0 +1,44 @@
'use strict'
const { isIntegerPercentage } = require('../test-validators')
const { ServiceTester } = require('../tester')
const t = (module.exports = new ServiceTester({
id: 'ScrutinizerCoverage',
title: 'ScrutinizerCoverage',
pathPrefix: '/scrutinizer/coverage',
}))
t.create('code coverage (GitHub)')
.get('/g/filp/whoops.json')
.expectBadge({
label: 'coverage',
message: isIntegerPercentage,
})
t.create('code coverage branch (GitHub)')
.get('/g/PHPMailer/PHPMailer/master.json')
.expectBadge({
label: 'coverage',
message: isIntegerPercentage,
})
t.create('code coverage (Bitbucket)')
.get('/b/atlassian/python-bitbucket.json')
.expectBadge({
label: 'coverage',
message: isIntegerPercentage,
})
t.create('code coverage private project')
.get('/gl/propertywindow/propertywindow/client.json')
.expectBadge({
label: 'coverage',
message: 'not authorized to access project',
})
t.create('code coverage nonexistent project')
.get('/gp/foo.json')
.expectBadge({
label: 'coverage',
message: 'project not found',
})

View File

@@ -0,0 +1,148 @@
'use strict'
const Joi = require('joi')
const { colorScale } = require('../color-formatters')
const ScrutinizerBase = require('./scrutinizer-base')
const schema = Joi.object({
default_branch: Joi.string().required(),
applications: Joi.object()
.pattern(
/^/,
Joi.object({
index: Joi.object({
_embedded: Joi.object({
project: Joi.object({
metric_values: Joi.object({
'scrutinizer.quality': Joi.number().positive(),
}).required(),
}).required(),
}).required(),
}).required(),
})
)
.required(),
}).required()
const scale = colorScale(
[4, 5, 7, 9],
['red', 'orange', 'yellow', 'green', 'brightgreen']
)
class ScrutinizerQualityBase extends ScrutinizerBase {
static get category() {
return 'analysis'
}
static get defaultBadgeData() {
return {
label: 'code quality',
}
}
static render({ score }) {
return {
message: `${Math.round(score * 100) / 100}`,
color: scale(score),
}
}
async makeBadge({ vcs, slug, branch }) {
const json = await this.fetch({ schema, vcs, slug })
const { value: score } = this.transformBranchInfoMetricValue({
json,
branch,
metric: 'scrutinizer.quality',
})
return this.constructor.render({ score })
}
}
class ScrutinizerQuality extends ScrutinizerQualityBase {
static get route() {
return {
base: 'scrutinizer/quality',
pattern: ':vcs(g|b)/:user/:repo/:branch*',
}
}
static get examples() {
return [
{
title: 'Scrutinizer code quality (GitHub/Bitbucket)',
pattern: ':vcs(g|b)/:user/:repo/:branch?',
namedParams: {
vcs: 'g',
user: 'filp',
repo: 'whoops',
branch: 'master',
},
staticPreview: this.render({ score: 8.26 }),
},
]
}
async handle({ vcs, user, repo, branch }) {
return this.makeBadge({
vcs,
slug: `${user}/${repo}`,
branch,
})
}
}
class ScrutinizerQualityGitLab extends ScrutinizerQualityBase {
static get route() {
return {
base: 'scrutinizer/quality/gl',
pattern: ':instance/:user/:repo/:branch*',
}
}
static get examples() {
// There are no known anonymous accessible Scrutinizer reports available for GitLab repos.
// The example used is valid, but the project will not be accessible if Shields users try to use it.
// https://gitlab.propertywindow.nl/propertywindow/client
// https://scrutinizer-ci.com/gl/propertywindow/propertywindow/client/badges/quality-score.png?b=master&s=dfae6992a48184cc2333b4c349cec0447f0d67c2
return [
{
title: 'Scrutinizer coverage (GitLab)',
pattern: ':instance/:user/:repo/:branch?',
namedParams: {
instance: 'propertywindow',
user: 'propertywindow',
repo: 'client',
branch: 'master',
},
staticPreview: this.render({ score: 10.0 }),
},
]
}
async handle({ instance, user, repo, branch }) {
return this.makeBadge({
vcs: 'gl',
slug: `${instance}/${user}/${repo}`,
branch,
})
}
}
class ScrutinizerQualityPlainGit extends ScrutinizerQualityBase {
static get route() {
return {
base: 'scrutinizer/quality/gp',
pattern: ':slug/:branch*',
}
}
async handle({ slug, branch }) {
return this.makeBadge({ vcs: 'gp', slug, branch })
}
}
module.exports = [
ScrutinizerQuality,
ScrutinizerQualityGitLab,
ScrutinizerQualityPlainGit,
]

View File

@@ -0,0 +1,46 @@
'use strict'
const Joi = require('joi')
const { ServiceTester } = require('../tester')
const t = (module.exports = new ServiceTester({
id: 'ScrutinizerQuality',
title: 'ScrutinizerQuality',
pathPrefix: '/scrutinizer/quality',
}))
const isQualityNumber = Joi.number().positive()
t.create('code quality (GitHub)')
.get('/g/filp/whoops.json')
.expectBadge({
label: 'code quality',
message: isQualityNumber,
})
t.create('code quality branch (GitHub)')
.get('/g/PHPMailer/PHPMailer/master.json')
.expectBadge({
label: 'code quality',
message: isQualityNumber,
})
t.create('code quality (Bitbucket)')
.get('/b/atlassian/python-bitbucket.json')
.expectBadge({
label: 'code quality',
message: isQualityNumber,
})
t.create('code quality private project')
.get('/gl/propertywindow/propertywindow/client.json')
.expectBadge({
label: 'code quality',
message: 'not authorized to access project',
})
t.create('code quality nonexistent project')
.get('/gp/foo.json')
.expectBadge({
label: 'code quality',
message: 'project not found',
})

View File

@@ -0,0 +1,42 @@
'use strict'
const { redirector } = require('..')
const commonAttrs = {
category: 'analysis',
dateAdded: new Date('2019-04-24'),
}
module.exports = [
redirector({
route: {
base: 'scrutinizer',
pattern: ':vcs(g|b)/:user/:repo/:branch*',
},
transformPath: ({ vcs, user, repo, branch }) =>
`/scrutinizer/quality/${vcs}/${user}/${repo}${
branch ? `/${branch}` : ''
}`,
...commonAttrs,
}),
redirector({
route: {
base: 'scrutinizer/gl',
pattern: ':instance/:user/:repo/:branch*',
},
transformPath: ({ instance, user, repo, branch }) =>
`/scrutinizer/quality/gl/${instance}/${user}/${repo}${
branch ? `/${branch}` : ''
}`,
...commonAttrs,
}),
redirector({
route: {
base: 'scrutinizer/gp',
pattern: ':slug/:branch*',
},
transformPath: ({ slug, branch }) =>
`/scrutinizer/quality/gp/${slug}${branch ? `/${branch}` : ''}`,
...commonAttrs,
}),
]

View File

@@ -0,0 +1,71 @@
'use strict'
const { ServiceTester } = require('../tester')
const t = (module.exports = new ServiceTester({
id: 'ScrutinizerQualityRedirect',
title: 'ScrutinizerQualityRedirect',
pathPrefix: '/scrutinizer',
}))
t.create('scrutinizer quality GitHub')
.get('/g/doctrine/orm.svg', {
followRedirect: false,
})
.expectStatus(301)
.expectHeader('Location', '/scrutinizer/quality/g/doctrine/orm.svg')
t.create('scrutinizer quality GitHub (branch)')
.get('/g/doctrine/orm/develop.svg', {
followRedirect: false,
})
.expectStatus(301)
.expectHeader('Location', '/scrutinizer/quality/g/doctrine/orm/develop.svg')
t.create('scrutinizer quality Bitbucket')
.get('/b/doctrine/orm.svg', {
followRedirect: false,
})
.expectStatus(301)
.expectHeader('Location', '/scrutinizer/quality/b/doctrine/orm.svg')
t.create('scrutinizer quality Bitbucket (branch)')
.get('/b/atlassian/python-bitbucket/develop.svg', {
followRedirect: false,
})
.expectStatus(301)
.expectHeader(
'Location',
'/scrutinizer/quality/b/atlassian/python-bitbucket/develop.svg'
)
t.create('scrutinizer quality GitLab')
.get('/gl/gitlab-com/foo/bar.svg', {
followRedirect: false,
})
.expectStatus(301)
.expectHeader('Location', '/scrutinizer/quality/gl/gitlab-com/foo/bar.svg')
t.create('scrutinizer quality GitLab (branch)')
.get('/gl/gitlab-com/foo/bar/develop.svg', {
followRedirect: false,
})
.expectStatus(301)
.expectHeader(
'Location',
'/scrutinizer/quality/gl/gitlab-com/foo/bar/develop.svg'
)
t.create('scrutinizer quality Plain Git')
.get('/gp/bar.svg', {
followRedirect: false,
})
.expectStatus(301)
.expectHeader('Location', '/scrutinizer/quality/gp/bar.svg')
t.create('scrutinizer quality Plain Git (branch)')
.get('/gp/bar/develop.svg', {
followRedirect: false,
})
.expectStatus(301)
.expectHeader('Location', '/scrutinizer/quality/gp/bar/develop.svg')

View File

@@ -1,208 +0,0 @@
'use strict'
const LegacyService = require('../legacy-service')
const { makeBadgeData: getBadgeData } = require('../../lib/badge-data')
const { checkErrorResponse } = require('../../lib/error-helper')
const {
coveragePercentage: coveragePercentageColor,
} = require('../color-formatters')
class ScrutinizerBuild extends LegacyService {
static get category() {
return 'build'
}
static get route() {
return {
base: 'scrutinizer/build',
pattern: ':vcsType/:user/:repo',
}
}
static get examples() {
return [
{
title: 'Scrutinizer build',
namedParams: {
vcsType: 'g',
user: 'filp',
repo: 'whoops',
},
staticPreview: {
label: 'build',
message: 'passing',
color: 'brightgreen',
},
},
]
}
static registerLegacyRouteHandler({ camp, cache }) {}
}
class ScrutinizerCoverage extends LegacyService {
static get category() {
return 'coverage'
}
static get route() {
return {
base: 'scrutinizer/coverage',
pattern: ':vcsType/:user/:repo/:branch*',
}
}
static get examples() {
return [
{
title: 'Scrutinizer coverage',
pattern: ':vcsType/:user/:repo',
namedParams: {
vcsType: 'g',
user: 'filp',
repo: 'whoops',
},
staticPreview: {
label: 'coverage',
message: '56%',
color: 'yellow',
},
},
{
title: 'Scrutinizer coverage (branch)',
pattern: ':vcsType/:user/:repo/:branch',
namedParams: {
vcsType: 'g',
user: 'doctrine',
repo: 'orm',
branch: 'master',
},
staticPreview: {
label: 'coverage',
message: '73%',
color: 'yellow',
},
},
]
}
static registerLegacyRouteHandler({ camp, cache }) {}
}
// This legacy service should be rewritten to use e.g. BaseJsonService.
//
// Tips for rewriting:
// https://github.com/badges/shields/blob/master/doc/rewriting-services.md
//
// Do not base new services on this code.
class Scrutinizer extends LegacyService {
static get category() {
return 'analysis'
}
static get route() {
return {
base: 'scrutinizer',
pattern: ':vcsType/:user/:repo',
}
}
static get examples() {
return [
{
title: 'Scrutinizer code quality',
namedParams: {
vcsType: 'g',
user: 'filp',
repo: 'whoops',
},
staticPreview: {
label: 'code quality',
message: '8.26',
color: 'green',
},
},
]
}
static registerLegacyRouteHandler({ camp, cache }) {
camp.route(
/^\/scrutinizer(?:\/(build|coverage))?\/([^/]+\/[^/]+\/[^/]+|gp\/[^/])(?:\/(.+))?\.(svg|png|gif|jpg|json)$/,
cache((data, match, sendBadge, request) => {
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, {}, (err, res, buffer) => {
if (
checkErrorResponse(badgeData, err, res, {
404: 'project or branch not found',
})
) {
sendBadge(format, badgeData)
return
}
try {
const parsedData = JSON.parse(buffer)
// Which branch are we dealing with?
if (branch === undefined) {
branch = parsedData.default_branch
}
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 {
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'
sendBadge(format, badgeData)
}
})
})
)
}
}
module.exports = {
ScrutinizerBuild,
ScrutinizerCoverage,
Scrutinizer,
}

View File

@@ -1,101 +0,0 @@
'use strict'
const Joi = require('joi')
const { isBuildStatus } = require('../build-status')
const { ServiceTester } = require('../tester')
const { isIntegerPercentage } = require('../test-validators')
const t = (module.exports = new ServiceTester({
id: 'scrutinizer',
title: 'Scrutinizer',
}))
t.create('code quality')
.get('/g/filp/whoops.json')
.expectBadge({
label: 'code quality',
message: Joi.number().positive(),
})
t.create('code quality (branch)')
.get('/g/phpmyadmin/phpmyadmin/master.json')
.expectBadge({
label: 'code quality',
message: Joi.number().positive(),
})
t.create('code coverage')
.get('/coverage/g/filp/whoops.json')
.expectBadge({
label: 'coverage',
message: isIntegerPercentage,
})
t.create('code coverage (branch)')
.get('/coverage/g/PHPMailer/PHPMailer/master.json')
.expectBadge({
label: 'coverage',
message: isIntegerPercentage,
})
t.create('build')
.get('/build/g/filp/whoops.json')
.expectBadge({
label: 'build',
message: Joi.alternatives().try(isBuildStatus, Joi.equal('unknown')),
})
t.create('build (branch)')
.get('/build/g/phpmyadmin/phpmyadmin/master.json')
.expectBadge({
label: 'build',
message: Joi.alternatives().try(isBuildStatus, Joi.equal('unknown')),
})
t.create('project not found')
.get('/build/g/does-not-exist/does-not-exist.json')
.expectBadge({
label: 'build',
message: 'project or branch not found',
})
t.create('code coverage unknown')
.get('/coverage/g/phpmyadmin/phpmyadmin/master.json')
.expectBadge({
label: 'coverage',
message: '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"}')
)
.expectBadge({
label: 'coverage',
message: 'invalid',
})
t.create('build - unknown')
.get('/build/g/filp/whoops.json')
.intercept(nock =>
nock('https://scrutinizer-ci.com')
.get('/api/repositories/g/filp/whoops')
.reply(200, {
default_branch: 'master',
applications: {
master: {
build_status: {
status: 'unknown',
},
},
},
})
)
.expectBadge({
label: 'build',
message: 'unknown',
color: 'lightgrey',
})