Skip to content

Commit

Permalink
refactor [circleci] integration (#1927)
Browse files Browse the repository at this point in the history
* refactor [circleci] integration
  • Loading branch information
chris48s authored Aug 28, 2018
1 parent edea36d commit e294dc4
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 124 deletions.
18 changes: 0 additions & 18 deletions lib/all-badge-examples.js
Original file line number Diff line number Diff line change
Expand Up @@ -167,24 +167,6 @@ const allBadgeExamples = [
title: 'Codeship',
previewUri: '/codeship/d6c1ddd0-16a3-0132-5f85-2e35c05e22b1/master.svg',
},
{
title: 'CircleCI',
previewUri: '/circleci/project/github/RedSparr0w/node-csgo-parser.svg',
},
{
title: 'CircleCI branch',
previewUri:
'/circleci/project/github/RedSparr0w/node-csgo-parser/master.svg',
},
{
title: 'CircleCI token',
previewUri:
'/circleci/project/github/RedSparr0w/node-csgo-parser/master.svg',
urlPattern:
'/circleci/token/:token/project/github/RedSparr0w/node-csgo-parser/master.svg',
exampleUri:
'/circleci/token/b90b5c49e59a4c67ba3a92f7992587ac7a0408c2/project/github/RedSparr0w/node-csgo-parser/master.svg',
},
{
title: 'Visual Studio Team services',
previewUri:
Expand Down
80 changes: 0 additions & 80 deletions server.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
const dom = require('xmldom').DOMParser;
const jp = require('jsonpath');
const path = require('path');
const queryString = require('query-string');
const xpath = require('xpath');
const yaml = require('js-yaml');
const Raven = require('raven');
Expand Down Expand Up @@ -118,85 +117,6 @@ loadServiceClasses().forEach(
{ camp, handleRequest: cache, githubApiProvider },
{ handleInternalErrors: config.handleInternalErrors }));

// CircleCI build integration.
// https://circleci.com/api/v1/project/BrightFlair/PHP.Gt?circle-token=0a5143728784b263d9f0238b8d595522689b3af2&limit=1&filter=completed
camp.route(/^\/circleci\/(?:token\/(\w+))?[+/]?project\/(?:(github|bitbucket)\/)?([^/]+\/[^/]+)(?:\/(.*))?\.(svg|png|gif|jpg|json)$/,
cache(function(data, match, sendBadge, request) {
const token = match[1];
const type = match[2] || 'github'; // github OR bitbucket
const userRepo = match[3]; // eg, `RedSparr0w/node-csgo-parser`.
const branch = match[4];
const format = match[5];

// Base API URL
let apiUrl = 'https://circleci.com/api/v1.1/project/' + type + '/' + userRepo;

// Query Params
var queryParams = {};
queryParams['limit'] = 1;
queryParams['filter'] = 'completed';

// Custom Banch if present
if (branch != null) {
apiUrl += "/tree/" + branch;
}

// Append Token to Query Params if present
if (token) {
queryParams['circle-token'] = token;
}

// Apprend query params to API URL
apiUrl += '?' + queryString.stringify(queryParams);

const badgeData = getBadgeData('build', data);
request(apiUrl, { json:true }, function(err, res, data) {
if (checkErrorResponse(badgeData, err, res, { 404: 'project not found' })) {
sendBadge(format, badgeData);
return;
}
try {
if (data.message !== undefined){
badgeData.text[1] = data.message;
sendBadge(format, badgeData);
return;
}

let passCount = 0;
let status;
for (let i=0; i<data.length; i++) {
status = data[i].status;
if (['success', 'fixed'].includes(status)) {
passCount++;
} else if (status === 'failed') {
badgeData.colorscheme = 'red';
badgeData.text[1] = 'failed';
sendBadge(format, badgeData);
return;
} else if (['no_tests', 'scheduled', 'not_run'].includes(status)) {
badgeData.colorscheme = 'yellow';
badgeData.text[1] = status.replace('_', ' ');
sendBadge(format, badgeData);
return;
} else {
badgeData.text[1] = status.replace('_', ' ');
sendBadge(format, badgeData);
return;
}
}

if (passCount === data.length) {
badgeData.colorscheme = 'brightgreen';
badgeData.text[1] = 'passing';
}
sendBadge(format, badgeData);
} catch(e) {
badgeData.text[1] = 'invalid';
sendBadge(format, badgeData);
}
});
}));

// User defined sources - JSON response
camp.route(/^\/badge\/dynamic\/(json|xml|yaml)\.(svg|png|gif|jpg|json)$/,
cache({
Expand Down
89 changes: 89 additions & 0 deletions services/circleci/circleci.service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
'use strict'

const Joi = require('joi')
const BaseJsonService = require('../base-json')

const circleSchema = Joi.array()
.items(Joi.object({ status: Joi.string().required() }))
.min(1)
.max(1)
.required()

module.exports = class CircleCi extends BaseJsonService {
async fetch({ token, vcsType, userRepo, branch }) {
let url = `https://circleci.com/api/v1.1/project/${vcsType}/${userRepo}`
if (branch != null) {
url += `/tree/${branch}`
}
const query = { filter: 'completed', limit: 1 }
if (token) {
query['circle-token'] = token
}
return this._requestJson({
url,
schema: circleSchema,
options: { qs: query },
errorMessages: { 404: 'project not found' },
})
}

static render({ status }) {
if (['success', 'fixed'].includes(status)) {
return { message: 'passing', color: 'brightgreen' }
} else if (status === 'failed') {
return { message: 'failed', color: 'red' }
} else if (['no_tests', 'scheduled', 'not_run'].includes(status)) {
return { message: status.replace('_', ' '), color: 'yellow' }
} else {
return { message: status.replace('_', ' '), color: 'lightgrey' }
}
}

async handle({ token, vcsType, userRepo, branch }) {
const json = await this.fetch({ token, vcsType, userRepo, branch })
return this.constructor.render({ status: json[0].status })
}

// Metadata
static get defaultBadgeData() {
return { label: 'build' }
}

static get category() {
return 'build'
}

static get url() {
return {
base: 'circleci',
format:
'(?:token/(w+))?[+/]?project/(?:(github|bitbucket)/)?([^/]+/[^/]+)(?:/(.*))?',
capture: ['token', 'vcsType', 'userRepo', 'branch'],
}
}

static get examples() {
return [
{
title: 'CircleCI (all branches)',
exampleUrl: 'project/github/RedSparr0w/node-csgo-parser',
urlPattern: 'project/:vcsType/:owner/:repo',
staticExample: this.render({ status: 'success' }),
},
{
title: 'CircleCI branch',
exampleUrl: 'project/github/RedSparr0w/node-csgo-parser/master',
urlPattern: 'project/:vcsType/:owner/:repo/:branch',
staticExample: this.render({ status: 'success' }),
},
{
title: 'CircleCI token',
urlPattern:
'circleci/token/:token/project/:vcsType/:owner/:repo/:branch',
exampleUrl:
'circleci/token/b90b5c49e59a4c67ba3a92f7992587ac7a0408c2/project/github/RedSparr0w/node-csgo-parser/master',
staticExample: this.render({ status: 'success' }),
},
]
}
}
34 changes: 8 additions & 26 deletions services/circleci/circleci.tester.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

const Joi = require('joi')
const ServiceTester = require('../service-tester')
const { invalidJSON } = require('../response-fixtures')
const { isBuildStatus } = require('../test-validators')

const t = new ServiceTester({ id: 'circleci', title: 'Circle CI' })
Expand Down Expand Up @@ -35,17 +34,6 @@ t.create('circle ci (connection error)')
.networkOff()
.expectJSON({ name: 'build', value: 'inaccessible' })

t.create('circle ci (unexpected response)')
.get('/project/github/RedSparr0w/node-csgo-parser.json')
.intercept(nock =>
nock('https://circleci.com')
.get(
'/api/v1.1/project/github/RedSparr0w/node-csgo-parser?filter=completed&limit=1'
)
.reply(invalidJSON)
)
.expectJSON({ name: 'build', value: 'invalid' })

t.create('circle ci (no response data)')
.get('/project/github/RedSparr0w/node-csgo-parser.json')
.intercept(nock =>
Expand All @@ -55,9 +43,10 @@ t.create('circle ci (no response data)')
)
.reply(200)
)
.expectJSON({ name: 'build', value: 'invalid' })
.expectJSON({ name: 'build', value: 'unparseable json response' })

t.create('circle ci (multiple pipelines, pass)')
// we're passing &limit=1 so we expect exactly one array element
t.create('circle ci (invalid json)')
.get('/project/github/RedSparr0w/node-csgo-parser.json?style=_shields_test')
.intercept(nock =>
nock('https://circleci.com')
Expand All @@ -66,15 +55,8 @@ t.create('circle ci (multiple pipelines, pass)')
)
.reply(200, [{ status: 'success' }, { status: 'fixed' }])
)
.expectJSON({ name: 'build', value: 'passing', colorB: '#4c1' })

t.create('circle ci (multiple pipelines, fail)')
.get('/project/github/RedSparr0w/node-csgo-parser.json?style=_shields_test')
.intercept(nock =>
nock('https://circleci.com')
.get(
'/api/v1.1/project/github/RedSparr0w/node-csgo-parser?filter=completed&limit=1'
)
.reply(200, [{ status: 'success' }, { status: 'failed' }])
)
.expectJSON({ name: 'build', value: 'failed', colorB: '#e05d44' })
.expectJSON({
name: 'build',
value: 'invalid json response',
colorB: '#9f9f9f',
})

0 comments on commit e294dc4

Please sign in to comment.