[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'
|
azure_devops_token: 'AZURE_DEVOPS_TOKEN'
|
||||||
bintray_user: 'BINTRAY_USER'
|
bintray_user: 'BINTRAY_USER'
|
||||||
bintray_apikey: 'BINTRAY_API_KEY'
|
bintray_apikey: 'BINTRAY_API_KEY'
|
||||||
|
drone_token: 'DRONE_TOKEN'
|
||||||
gh_client_id: 'GH_CLIENT_ID'
|
gh_client_id: 'GH_CLIENT_ID'
|
||||||
gh_client_secret: 'GH_CLIENT_SECRET'
|
gh_client_secret: 'GH_CLIENT_SECRET'
|
||||||
gh_token: 'GH_TOKEN'
|
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)
|
The bintray API [requires authentication](https://bintray.com/docs/api/#_authentication)
|
||||||
Create an account and obtain a token from the user profile page.
|
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
|
## GitHub
|
||||||
|
|
||||||
- `GH_TOKEN` (yml: `gh_token`)
|
- `GH_TOKEN` (yml: `gh_token`)
|
||||||
|
|||||||
@@ -13,7 +13,13 @@ const greenStatuses = [
|
|||||||
|
|
||||||
const orangeStatuses = ['partially succeeded', 'unstable', 'timeout']
|
const orangeStatuses = ['partially succeeded', 'unstable', 'timeout']
|
||||||
|
|
||||||
const redStatuses = ['error', 'failed', 'failing', 'infrastructure_failure']
|
const redStatuses = [
|
||||||
|
'error',
|
||||||
|
'failed',
|
||||||
|
'failing',
|
||||||
|
'failure',
|
||||||
|
'infrastructure_failure',
|
||||||
|
]
|
||||||
|
|
||||||
const otherStatuses = [
|
const otherStatuses = [
|
||||||
'building',
|
'building',
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ test(renderBuildStatusBadge, () => {
|
|||||||
given({ status: 'error' }),
|
given({ status: 'error' }),
|
||||||
given({ status: 'failed' }),
|
given({ status: 'failed' }),
|
||||||
given({ status: 'failing' }),
|
given({ status: 'failing' }),
|
||||||
|
given({ status: 'failure' }),
|
||||||
given({ status: 'infrastructure_failure' }),
|
given({ status: 'infrastructure_failure' }),
|
||||||
]).assert('should be red', b => expect(b).to.include({ color: 'red' }))
|
]).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