[Drone] Add Drone build badge (#3240)
* Add drone build badge based on travis * Fix wrong mocked endpoint for done builder * Refactor service tester using helper method * Add missing failure status to red statuses * Remove extraneous invalid svg test from drone * Test on failure red status in build status spec * refactor(drone): use json service instead of svg * refactor(drone): remove status text and extraneous build path in test * refactor(drone): allow defining self-hosted drone instances * fix(drone): use proper urls in drone examples * fix(drone): add drone token authorization for self-hosted instances * refactor(drone): call render build status badge directly instead of render * refactor(drone): use server query parameter for self-hosted instances * fix(drone): separate url and query params in example * fix(drone): use actual build status message in examples * fix(drone): add missing message for status code 401 Co-Authored-By: byCedric <me@bycedric.com> * refactor(drone): remove color from drone tests * refactor(drone): remove extraneous comments from drone tests * refactor(drone): remove unused static preview method * refactor(drone): remove unused static render method * refactor(drone): reuse render build status badge helper in static previews * fix(drone): test inaccessible repos on new message
This commit is contained in:
committed by
Caleb Cartwright
parent
91d6dd6643
commit
483ecf24de
@@ -45,6 +45,7 @@ private:
|
||||
azure_devops_token: 'AZURE_DEVOPS_TOKEN'
|
||||
bintray_user: 'BINTRAY_USER'
|
||||
bintray_apikey: 'BINTRAY_API_KEY'
|
||||
drone_token: 'DRONE_TOKEN'
|
||||
gh_client_id: 'GH_CLIENT_ID'
|
||||
gh_client_secret: 'GH_CLIENT_SECRET'
|
||||
gh_token: 'GH_TOKEN'
|
||||
|
||||
@@ -43,6 +43,13 @@ An Azure DevOps Token (PAT) is required for accessing [private Azure DevOps proj
|
||||
The bintray API [requires authentication](https://bintray.com/docs/api/#_authentication)
|
||||
Create an account and obtain a token from the user profile page.
|
||||
|
||||
## Drone
|
||||
|
||||
- `DRONE_TOKEN` (yml: `drone_token`)
|
||||
|
||||
The self-hosted Drone API [requires authentication](https://0-8-0.docs.drone.io/api-authentication/)
|
||||
Login to your Drone instance and obtain a token from the user profile page.
|
||||
|
||||
## GitHub
|
||||
|
||||
- `GH_TOKEN` (yml: `gh_token`)
|
||||
|
||||
@@ -13,7 +13,13 @@ const greenStatuses = [
|
||||
|
||||
const orangeStatuses = ['partially succeeded', 'unstable', 'timeout']
|
||||
|
||||
const redStatuses = ['error', 'failed', 'failing', 'infrastructure_failure']
|
||||
const redStatuses = [
|
||||
'error',
|
||||
'failed',
|
||||
'failing',
|
||||
'failure',
|
||||
'infrastructure_failure',
|
||||
]
|
||||
|
||||
const otherStatuses = [
|
||||
'building',
|
||||
|
||||
@@ -58,6 +58,7 @@ test(renderBuildStatusBadge, () => {
|
||||
given({ status: 'error' }),
|
||||
given({ status: 'failed' }),
|
||||
given({ status: 'failing' }),
|
||||
given({ status: 'failure' }),
|
||||
given({ status: 'infrastructure_failure' }),
|
||||
]).assert('should be red', b => expect(b).to.include({ color: 'red' }))
|
||||
})
|
||||
|
||||
107
services/drone/drone-build.service.js
Normal file
107
services/drone/drone-build.service.js
Normal file
@@ -0,0 +1,107 @@
|
||||
'use strict'
|
||||
|
||||
const Joi = require('joi')
|
||||
const serverSecrets = require('../../lib/server-secrets')
|
||||
const { isBuildStatus, renderBuildStatusBadge } = require('../build-status')
|
||||
const { optionalUrl } = require('../validators')
|
||||
const { BaseJsonService } = require('..')
|
||||
|
||||
const DroneBuildSchema = Joi.object({
|
||||
status: Joi.alternatives()
|
||||
.try(isBuildStatus, Joi.equal('none'))
|
||||
.required(),
|
||||
}).required()
|
||||
|
||||
const queryParamSchema = Joi.object({
|
||||
server: optionalUrl,
|
||||
}).required()
|
||||
|
||||
module.exports = class DroneBuild extends BaseJsonService {
|
||||
static get category() {
|
||||
return 'build'
|
||||
}
|
||||
|
||||
static get route() {
|
||||
return {
|
||||
queryParamSchema,
|
||||
base: 'drone/build',
|
||||
pattern: ':user/:repo/:branch*',
|
||||
}
|
||||
}
|
||||
|
||||
static get defaultBadgeData() {
|
||||
return {
|
||||
label: 'build',
|
||||
}
|
||||
}
|
||||
|
||||
async handle({ user, repo, branch }, { server }) {
|
||||
const options = {
|
||||
qs: {
|
||||
ref: branch ? `refs/heads/${branch}` : undefined,
|
||||
},
|
||||
}
|
||||
if (serverSecrets.drone_token) {
|
||||
options.headers = {
|
||||
Authorization: `Bearer ${serverSecrets.drone_token}`,
|
||||
}
|
||||
}
|
||||
if (!server) {
|
||||
server = 'https://cloud.drone.io'
|
||||
}
|
||||
const json = await this._requestJson({
|
||||
options,
|
||||
schema: DroneBuildSchema,
|
||||
url: `${server}/api/repos/${user}/${repo}/builds/latest`,
|
||||
errorMessages: {
|
||||
401: 'repo not found or not authorized',
|
||||
},
|
||||
})
|
||||
return renderBuildStatusBadge({ status: json.status })
|
||||
}
|
||||
|
||||
static get examples() {
|
||||
return [
|
||||
{
|
||||
title: 'Drone (cloud)',
|
||||
pattern: ':user/:repo',
|
||||
namedParams: {
|
||||
user: 'drone',
|
||||
repo: 'drone',
|
||||
},
|
||||
staticPreview: renderBuildStatusBadge({ status: 'success' }),
|
||||
},
|
||||
{
|
||||
title: 'Drone (cloud) with branch',
|
||||
pattern: ':user/:repo/:branch',
|
||||
namedParams: {
|
||||
user: 'drone',
|
||||
repo: 'drone',
|
||||
branch: 'master',
|
||||
},
|
||||
staticPreview: renderBuildStatusBadge({ status: 'success' }),
|
||||
},
|
||||
{
|
||||
title: 'Drone (self-hosted)',
|
||||
pattern: ':user/:repo',
|
||||
queryParams: { server: 'https://drone.shields.io' },
|
||||
namedParams: {
|
||||
user: 'badges',
|
||||
repo: 'shields',
|
||||
},
|
||||
staticPreview: renderBuildStatusBadge({ status: 'success' }),
|
||||
},
|
||||
{
|
||||
title: 'Drone (self-hosted) with branch',
|
||||
pattern: ':user/:repo/:branch',
|
||||
queryParams: { server: 'https://drone.shields.io' },
|
||||
namedParams: {
|
||||
user: 'badges',
|
||||
repo: 'shields',
|
||||
branch: 'feat/awesome-thing',
|
||||
},
|
||||
staticPreview: renderBuildStatusBadge({ status: 'success' }),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
62
services/drone/drone-build.tester.js
Normal file
62
services/drone/drone-build.tester.js
Normal file
@@ -0,0 +1,62 @@
|
||||
'use strict'
|
||||
|
||||
const Joi = require('joi')
|
||||
const { isBuildStatus } = require('../build-status')
|
||||
const t = (module.exports = require('../tester').createServiceTester())
|
||||
const { mockDroneCreds, token, restore } = require('./drone-test-helpers')
|
||||
|
||||
t.create('cloud-hosted build status on default branch')
|
||||
.get('/drone/drone.json')
|
||||
.expectBadge({
|
||||
label: 'build',
|
||||
message: Joi.alternatives().try(isBuildStatus, Joi.equal('none')),
|
||||
})
|
||||
|
||||
t.create('cloud-hosted build status on named branch')
|
||||
.get('/drone/drone/master.json')
|
||||
.expectBadge({
|
||||
label: 'build',
|
||||
message: Joi.alternatives().try(isBuildStatus, Joi.equal('none')),
|
||||
})
|
||||
|
||||
t.create('cloud-hosted build status on unknown repo')
|
||||
.get('/this-repo/does-not-exist.json')
|
||||
.expectBadge({
|
||||
label: 'build',
|
||||
message: 'repo not found or not authorized',
|
||||
})
|
||||
|
||||
t.create('self-hosted build status on default branch')
|
||||
.before(mockDroneCreds)
|
||||
.get('/badges/shields.json?server=https://drone.shields.io')
|
||||
.intercept(nock =>
|
||||
nock('https://drone.shields.io/api/repos', {
|
||||
reqheaders: { authorization: `Bearer ${token}` },
|
||||
})
|
||||
.get('/badges/shields/builds/latest')
|
||||
.reply(200, { status: 'success' })
|
||||
)
|
||||
.finally(restore)
|
||||
.expectBadge({
|
||||
label: 'build',
|
||||
message: 'passing',
|
||||
})
|
||||
|
||||
t.create('self-hosted build status on named branch')
|
||||
.before(mockDroneCreds)
|
||||
.get(
|
||||
'/badges/shields/feat/awesome-thing.json?server=https://drone.shields.io'
|
||||
)
|
||||
.intercept(nock =>
|
||||
nock('https://drone.shields.io/api/repos', {
|
||||
reqheaders: { authorization: `Bearer ${token}` },
|
||||
})
|
||||
.get('/badges/shields/builds/latest')
|
||||
.query({ ref: 'refs/heads/feat/awesome-thing' })
|
||||
.reply(200, { status: 'success' })
|
||||
)
|
||||
.finally(restore)
|
||||
.expectBadge({
|
||||
label: 'build',
|
||||
message: 'passing',
|
||||
})
|
||||
21
services/drone/drone-test-helpers.js
Normal file
21
services/drone/drone-test-helpers.js
Normal file
@@ -0,0 +1,21 @@
|
||||
'use strict'
|
||||
|
||||
const sinon = require('sinon')
|
||||
const serverSecrets = require('../../lib/server-secrets')
|
||||
|
||||
const token = 'my-token'
|
||||
|
||||
function mockDroneCreds() {
|
||||
serverSecrets['drone_token'] = undefined
|
||||
sinon.stub(serverSecrets, 'drone_token').value(token)
|
||||
}
|
||||
|
||||
function restore() {
|
||||
sinon.restore()
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
token,
|
||||
mockDroneCreds,
|
||||
restore,
|
||||
}
|
||||
Reference in New Issue
Block a user