Move [Jenkins] job url to query parameter (#4390)

This commit is contained in:
Pierre-Yves B
2019-11-30 18:33:35 +00:00
committed by GitHub
parent c2876e8d16
commit 92b1380db0
15 changed files with 317 additions and 97 deletions

View File

@@ -1,12 +1,14 @@
'use strict'
const { buildRedirectUrl } = require('./jenkins-common')
const { redirector } = require('..')
const commonProps = {
category: 'build',
transformPath: ({ protocol, host, job }) =>
`/jenkins/build/${protocol}/${host}/${job}`,
dateAdded: new Date('2019-04-20'),
transformPath: () => '/jenkins/build',
transformQueryParams: ({ protocol, host, job }) => ({
jobUrl: buildRedirectUrl({ protocol, host, job }),
}),
}
module.exports = [
@@ -15,6 +17,7 @@ module.exports = [
base: 'jenkins-ci/s',
pattern: ':protocol(http|https)/:host/:job+',
},
dateAdded: new Date('2019-04-20'),
...commonProps,
}),
redirector({
@@ -22,6 +25,15 @@ module.exports = [
base: 'jenkins/s',
pattern: ':protocol(http|https)/:host/:job+',
},
dateAdded: new Date('2019-04-20'),
...commonProps,
}),
redirector({
route: {
base: 'jenkins/build',
pattern: ':protocol(http|https)/:host/:job+',
},
dateAdded: new Date('2019-11-29'),
...commonProps,
}),
]

View File

@@ -8,22 +8,38 @@ const t = (module.exports = new ServiceTester({
pathPrefix: '/',
}))
t.create('jenkins ci')
t.create('old jenkins ci prefix + job url in path')
.get('jenkins-ci/s/https/updates.jenkins-ci.org/job/foo.svg', {
followRedirect: false,
})
.expectStatus(301)
.expectHeader(
'Location',
'/jenkins/build/https/updates.jenkins-ci.org/job/foo.svg'
`/jenkins/build.svg?jobUrl=${encodeURIComponent(
'https://updates.jenkins-ci.org/job/foo'
)}`
)
t.create('jenkins shorthand')
t.create('old jenkins shorthand prefix + job url in path')
.get('jenkins/s/https/updates.jenkins-ci.org/job/foo.svg', {
followRedirect: false,
})
.expectStatus(301)
.expectHeader(
'Location',
'/jenkins/build/https/updates.jenkins-ci.org/job/foo.svg'
`/jenkins/build.svg?jobUrl=${encodeURIComponent(
'https://updates.jenkins-ci.org/job/foo'
)}`
)
t.create('new jenkins build prefix + job url in path')
.get('jenkins/build/https/updates.jenkins-ci.org/job/foo.svg', {
followRedirect: false,
})
.expectStatus(301)
.expectHeader(
'Location',
`/jenkins/build.svg?jobUrl=${encodeURIComponent(
'https://updates.jenkins-ci.org/job/foo'
)}`
)

View File

@@ -40,8 +40,8 @@ module.exports = class JenkinsBuild extends JenkinsBase {
static get route() {
return {
base: 'jenkins/build',
pattern: ':protocol(http|https)/:host/:job+',
base: 'jenkins',
pattern: 'build',
queryParamSchema,
}
}
@@ -50,11 +50,9 @@ module.exports = class JenkinsBuild extends JenkinsBase {
return [
{
title: 'Jenkins',
namedParams: {
protocol: 'https',
host: 'jenkins.qa.ubuntu.com',
job:
'view/Precise/view/All%20Precise/job/precise-desktop-amd64_default',
namedParams: {},
queryParams: {
jobUrl: 'https://wso2.org/jenkins/view/All%20Builds/job/archetypes',
},
staticPreview: renderBuildStatusBadge({ status: 'passing' }),
},
@@ -82,9 +80,9 @@ module.exports = class JenkinsBuild extends JenkinsBase {
return { status: colorStatusMap[json.color] }
}
async handle({ protocol, host, job }, { disableStrictSSL }) {
async handle(namedParams, { jobUrl, disableStrictSSL }) {
const json = await this.fetch({
url: buildUrl({ protocol, host, job, lastCompletedBuild: false }),
url: buildUrl({ jobUrl, lastCompletedBuild: false }),
schema,
qs: buildTreeParamQueryString('color'),
disableStrictSSL,

View File

@@ -74,11 +74,15 @@ describe('JenkinsBuild', function() {
.reply(200, { color: 'blue' })
expect(
await JenkinsBuild.invoke(defaultContext, config, {
protocol: 'https',
host: 'jenkins.ubuntu.com',
job: 'server/job/curtin-vmtest-daily-x',
})
await JenkinsBuild.invoke(
defaultContext,
config,
{},
{
jobUrl:
'https://jenkins.ubuntu.com/server/job/curtin-vmtest-daily-x',
}
)
).to.deep.equal({
label: undefined,
message: 'passing',

View File

@@ -10,13 +10,17 @@ const isJenkinsBuildStatus = Joi.alternatives(
)
t.create('build job not found')
.get('/https/ci.eclipse.org/jgit/job/does-not-exist.json')
.get('/build.json?jobUrl=https://ci.eclipse.org/jgit/job/does-not-exist')
.expectBadge({ label: 'build', message: 'instance or job not found' })
t.create('build found (view)')
.get('/https/wso2.org/jenkins/view/All%20Builds/job/archetypes.json')
.get(
`/build.json?jobUrl=${encodeURIComponent(
'https://wso2.org/jenkins/view/All Builds/job/archetypes'
)}`
)
.expectBadge({ label: 'build', message: isJenkinsBuildStatus })
t.create('build found (job)')
.get('/https/ci.eclipse.org/jgit/job/jgit.json')
.get('/build.json?jobUrl=https://ci.eclipse.org/jgit/job/jgit')
.expectBadge({ label: 'build', message: isJenkinsBuildStatus })

View File

@@ -1,26 +1,29 @@
'use strict'
const Joi = require('@hapi/joi')
const { optionalUrl } = require('../validators')
const queryParamSchema = Joi.object({
disableStrictSSL: Joi.equal(''),
jobUrl: optionalUrl,
}).required()
const buildUrl = ({
protocol,
host,
job,
lastCompletedBuild = true,
plugin,
}) => {
const buildRedirectUrl = ({ protocol, host, job }) => {
const jobPrefix = job.indexOf('/') > -1 ? '' : 'job/'
return `${protocol}://${host}/${jobPrefix}${job}/${
lastCompletedBuild ? 'lastCompletedBuild/' : ''
}${plugin ? `${plugin}/` : ''}api/json`
return `${protocol}://${host}/${jobPrefix}${job}`
}
const buildUrl = ({ jobUrl, lastCompletedBuild = true, plugin }) => {
const lastCompletedBuildElement = lastCompletedBuild
? 'lastCompletedBuild/'
: ''
const pluginElement = plugin ? `${plugin}/` : ''
return `${jobUrl}/${lastCompletedBuildElement}${pluginElement}api/json`
}
module.exports = {
queryParamSchema,
buildTreeParamQueryString: tree => ({ tree }),
buildUrl,
buildRedirectUrl,
}

View File

@@ -0,0 +1,65 @@
'use strict'
const { expect } = require('chai')
const { buildRedirectUrl, buildUrl } = require('./jenkins-common')
describe('jenkins-common', function() {
describe('buildUrl', function() {
it('returns the json api url', function() {
const actualResult = buildUrl({
jobUrl: 'https://ci.eclipse.org/jgit/job/jgit',
})
expect(actualResult).to.equal(
'https://ci.eclipse.org/jgit/job/jgit/lastCompletedBuild/api/json'
)
})
it('returns the json api url including a plugin name', function() {
const actualResult = buildUrl({
jobUrl: 'https://ci.eclipse.org/jgit/job/jgit',
plugin: 'cobertura',
})
expect(actualResult).to.equal(
'https://ci.eclipse.org/jgit/job/jgit/lastCompletedBuild/cobertura/api/json'
)
})
it('returns the json api url without the lastCompletedBuild element', function() {
const actualResult = buildUrl({
jobUrl: 'https://ci.eclipse.org/jgit/job/jgit',
lastCompletedBuild: false,
})
expect(actualResult).to.equal(
'https://ci.eclipse.org/jgit/job/jgit/api/json'
)
})
})
describe('buildRedirectUrl', function() {
it('returns the job url', function() {
const actualResult = buildRedirectUrl({
protocol: 'https',
host: 'jenkins.sqlalchemy.org',
job: 'job/alembic_coverage',
})
expect(actualResult).to.equal(
'https://jenkins.sqlalchemy.org/job/alembic_coverage'
)
})
it('returns the job url and adds missing /job prefixes', function() {
const actualResult = buildRedirectUrl({
protocol: 'https',
host: 'jenkins.sqlalchemy.org',
job: 'alembic_coverage',
})
expect(actualResult).to.equal(
'https://jenkins.sqlalchemy.org/job/alembic_coverage'
)
})
})
})

View File

@@ -1,16 +1,35 @@
'use strict'
const { buildRedirectUrl } = require('./jenkins-common')
const { redirector } = require('..')
module.exports = redirector({
const commonProps = {
category: 'coverage',
route: {
base: 'jenkins',
pattern: ':coverageFormat(j|c)/:protocol(http|https)/:host/:job+',
},
transformPath: ({ coverageFormat, protocol, host, job }) =>
`/jenkins/coverage/${
coverageFormat === 'j' ? 'jacoco' : 'cobertura'
}/${protocol}/${host}/${job}`,
dateAdded: new Date('2019-04-20'),
})
transformQueryParams: ({ protocol, host, job }) => ({
jobUrl: buildRedirectUrl({ protocol, host, job }),
}),
}
module.exports = [
redirector({
route: {
base: 'jenkins',
pattern: ':coverageFormat(j|c)/:protocol(http|https)/:host/:job+',
},
transformPath: ({ coverageFormat }) =>
`/jenkins/coverage/${coverageFormat === 'j' ? 'jacoco' : 'cobertura'}`,
dateAdded: new Date('2019-04-20'),
...commonProps,
}),
redirector({
route: {
base: 'jenkins/coverage',
pattern:
':coverageFormat(jacoco|cobertura|api)/:protocol(http|https)/:host/:job+',
},
transformPath: ({ coverageFormat }) =>
`/jenkins/coverage/${coverageFormat}`,
dateAdded: new Date('2019-11-29'),
...commonProps,
}),
]

View File

@@ -8,7 +8,7 @@ const t = (module.exports = new ServiceTester({
pathPrefix: '/jenkins',
}))
t.create('Jacoco')
t.create('old Jacoco prefix + job url in path')
.get(
'/j/https/wso2.org/jenkins/view/All%20Builds/job/sonar/job/sonar-carbon-dashboards.svg',
{
@@ -18,15 +18,64 @@ t.create('Jacoco')
.expectStatus(301)
.expectHeader(
'Location',
'/jenkins/coverage/jacoco/https/wso2.org/jenkins/view/All Builds/job/sonar/job/sonar-carbon-dashboards.svg'
`/jenkins/coverage/jacoco.svg?jobUrl=${encodeURIComponent(
'https://wso2.org/jenkins/view/All Builds/job/sonar/job/sonar-carbon-dashboards'
)}`
)
t.create('Cobertura')
t.create('new Jacoco prefix + job url in path')
.get(
'/coverage/jacoco/https/wso2.org/jenkins/view/All%20Builds/job/sonar/job/sonar-carbon-dashboards.svg',
{
followRedirect: false,
}
)
.expectStatus(301)
.expectHeader(
'Location',
`/jenkins/coverage/jacoco.svg?jobUrl=${encodeURIComponent(
'https://wso2.org/jenkins/view/All Builds/job/sonar/job/sonar-carbon-dashboards'
)}`
)
t.create('old Cobertura prefix + job url in path')
.get('/c/https/jenkins.sqlalchemy.org/job/alembic_coverage.svg', {
followRedirect: false,
})
.expectStatus(301)
.expectHeader(
'Location',
'/jenkins/coverage/cobertura/https/jenkins.sqlalchemy.org/job/alembic_coverage.svg'
`/jenkins/coverage/cobertura.svg?jobUrl=${encodeURIComponent(
'https://jenkins.sqlalchemy.org/job/alembic_coverage'
)}`
)
t.create('new Cobertura prefix + job url in path')
.get(
'/coverage/cobertura/https/jenkins.sqlalchemy.org/job/alembic_coverage.svg',
{
followRedirect: false,
}
)
.expectStatus(301)
.expectHeader(
'Location',
`/jenkins/coverage/cobertura.svg?jobUrl=${encodeURIComponent(
'https://jenkins.sqlalchemy.org/job/alembic_coverage'
)}`
)
t.create('api prefix + job url in path')
.get(
'/coverage/api/https/jenkins.library.illinois.edu/job/OpenSourceProjects/job/Speedwagon/job/master.svg',
{
followRedirect: false,
}
)
.expectStatus(301)
.expectHeader(
'Location',
`/jenkins/coverage/api.svg?jobUrl=${encodeURIComponent(
'https://jenkins.library.illinois.edu/job/OpenSourceProjects/job/Speedwagon/job/master'
)}`
)

View File

@@ -98,8 +98,7 @@ module.exports = class JenkinsCoverage extends JenkinsBase {
static get route() {
return {
base: 'jenkins/coverage',
pattern:
':format(jacoco|cobertura|api)/:protocol(http|https)/:host/:job+',
pattern: ':format(jacoco|cobertura|api)',
queryParamSchema,
}
}
@@ -110,9 +109,9 @@ module.exports = class JenkinsCoverage extends JenkinsBase {
title: 'Jenkins Coverage',
namedParams: {
format: 'cobertura',
protocol: 'https',
host: 'jenkins.sqlalchemy.org',
job: 'job/alembic_coverage',
},
queryParams: {
jobUrl: 'https://jenkins.sqlalchemy.org/job/alembic_coverage',
},
keywords: ['jacoco', 'cobertura', 'llvm-cov', 'istanbul'],
staticPreview: this.render({ coverage: 95 }),
@@ -132,12 +131,12 @@ module.exports = class JenkinsCoverage extends JenkinsBase {
}
}
async handle({ format, protocol, host, job }, { disableStrictSSL }) {
async handle({ format }, { jobUrl, disableStrictSSL }) {
const { schema, transform, treeQueryParam, pluginSpecificPath } = formatMap[
format
]
const json = await this.fetch({
url: buildUrl({ protocol, host, job, plugin: pluginSpecificPath }),
url: buildUrl({ jobUrl, plugin: pluginSpecificPath }),
schema,
qs: buildTreeParamQueryString(treeQueryParam),
disableStrictSSL,

View File

@@ -9,27 +9,37 @@ const t = (module.exports = require('../tester').createServiceTester())
// https://wiki.jenkins.io/pages/viewpage.action?pageId=58001258
t.create('jacoco: job found')
.get('/jacoco/https/wso2.org/jenkins/view/All%20Builds/job/archetypes.json')
.get(
`/jacoco.json?jobUrl=${encodeURIComponent(
'https://wso2.org/jenkins/view/All%20Builds/job/archetypes'
)}`
)
.expectBadge({ label: 'coverage', message: isIntegerPercentage })
t.create('jacoco: job not found')
.get('/jacoco/https/wso2.org/jenkins/job/does-not-exist.json')
.get('/jacoco.json?jobUrl=https://wso2.org/jenkins/job/does-not-exist')
.expectBadge({ label: 'coverage', message: 'job or coverage not found' })
t.create('cobertura: job not found')
.get('/cobertura/https/jenkins.sqlalchemy.org/job/does-not-exist.json')
.get(
'/cobertura.json?jobUrl=https://jenkins.sqlalchemy.org/job/does-not-exist'
)
.expectBadge({ label: 'coverage', message: 'job or coverage not found' })
t.create('cobertura: job found')
.get('/cobertura/https/jenkins.sqlalchemy.org/alembic_coverage.json')
.get(
'/cobertura.json?jobUrl=https://jenkins.sqlalchemy.org/job/alembic_coverage'
)
.expectBadge({ label: 'coverage', message: isIntegerPercentage })
t.create('code coverage API: job not found')
.get('/api/https/jenkins.library.illinois.edu/job/does-not-exist.json')
.get(
'/api.json?jobUrl=https://jenkins.library.illinois.edu/job/does-not-exist'
)
.expectBadge({ label: 'coverage', message: 'job or coverage not found' })
t.create('code coverage API: job found')
.get(
'/api/https/jenkins.library.illinois.edu/job/OpenSourceProjects/job/Speedwagon/job/master.json'
'/api.json?jobUrl=https://jenkins.library.illinois.edu/job/OpenSourceProjects/job/Speedwagon/job/master'
)
.expectBadge({ label: 'coverage', message: isIntegerPercentage })

View File

@@ -1,16 +1,31 @@
'use strict'
const { buildRedirectUrl } = require('./jenkins-common')
const { redirector } = require('..')
const commonProps = {
category: 'build',
transformPath: () => '/jenkins/tests',
transformQueryParams: ({ protocol, host, job }) => ({
jobUrl: buildRedirectUrl({ protocol, host, job }),
}),
}
module.exports = [
redirector({
category: 'build',
route: {
base: 'jenkins/t',
pattern: ':protocol(http|https)/:host/:job+',
},
transformPath: ({ protocol, host, job }) =>
`/jenkins/tests/${protocol}/${host}/${job}`,
dateAdded: new Date('2019-04-20'),
...commonProps,
}),
redirector({
route: {
base: 'jenkins/tests',
pattern: ':protocol(http|https)/:host/:job+',
},
dateAdded: new Date('2019-11-29'),
...commonProps,
}),
]

View File

@@ -8,7 +8,7 @@ const t = (module.exports = new ServiceTester({
pathPrefix: '/jenkins',
}))
t.create('Tests')
t.create('old tests prefix + job url in path')
.get(
'/t/https/jenkins.qa.ubuntu.com/view/Trusty/view/Smoke Testing/job/trusty-touch-flo-smoke-daily.svg',
{
@@ -18,5 +18,22 @@ t.create('Tests')
.expectStatus(301)
.expectHeader(
'Location',
'/jenkins/tests/https/jenkins.qa.ubuntu.com/view/Trusty/view/Smoke Testing/job/trusty-touch-flo-smoke-daily.svg'
`/jenkins/tests.svg?jobUrl=${encodeURIComponent(
'https://jenkins.qa.ubuntu.com/view/Trusty/view/Smoke Testing/job/trusty-touch-flo-smoke-daily'
)}`
)
t.create('new tests prefix + job url in path')
.get(
'/tests/https/jenkins.qa.ubuntu.com/view/Trusty/view/Smoke Testing/job/trusty-touch-flo-smoke-daily.svg',
{
followRedirect: false,
}
)
.expectStatus(301)
.expectHeader(
'Location',
`/jenkins/tests.svg?jobUrl=${encodeURIComponent(
'https://jenkins.qa.ubuntu.com/view/Trusty/view/Smoke Testing/job/trusty-touch-flo-smoke-daily'
)}`
)

View File

@@ -42,8 +42,8 @@ module.exports = class JenkinsTests extends JenkinsBase {
static get route() {
return {
base: 'jenkins/tests',
pattern: ':protocol(http|https)/:host/:job+',
base: 'jenkins',
pattern: 'tests',
queryParamSchema: queryParamSchema.concat(testResultQueryParamSchema),
}
}
@@ -52,16 +52,13 @@ module.exports = class JenkinsTests extends JenkinsBase {
return [
{
title: 'Jenkins tests',
namedParams: {
protocol: 'https',
host: 'jenkins.sqlalchemy.org',
job: 'alembic_coverage',
},
namedParams: {},
queryParams: {
compact_message: null,
passed_label: 'passed',
failed_label: 'failed',
skipped_label: 'skipped',
jobUrl: 'https://jenkins.sqlalchemy.org/job/alembic_coverage',
},
staticPreview: this.render({
passed: 477,
@@ -120,9 +117,10 @@ module.exports = class JenkinsTests extends JenkinsBase {
}
async handle(
{ protocol, host, job },
namedParams,
{
disableStrictSSL,
jobUrl,
compact_message: compactMessage,
passed_label: passedLabel,
failed_label: failedLabel,
@@ -130,7 +128,7 @@ module.exports = class JenkinsTests extends JenkinsBase {
}
) {
const json = await this.fetch({
url: buildUrl({ protocol, host, job }),
url: buildUrl({ jobUrl }),
schema,
qs: buildTreeParamQueryString('actions[failCount,skipCount,totalCount]'),
disableStrictSSL,

View File

@@ -14,43 +14,54 @@ const t = (module.exports = require('../tester').createServiceTester())
// https://wiki.jenkins.io/pages/viewpage.action?pageId=58001258
t.create('Test status')
.get('/https/jenkins.sqlalchemy.org/alembic_coverage.json')
.get('/tests.json?jobUrl=https://jenkins.sqlalchemy.org/job/alembic_coverage')
.expectBadge({ label: 'tests', message: isDefaultTestTotals })
t.create('Test status with compact message')
.get('/https/jenkins.sqlalchemy.org/alembic_coverage.json', {
qs: { compact_message: null },
})
.get(
'/tests.json?jobUrl=https://jenkins.sqlalchemy.org/job/alembic_coverage',
{
qs: { compact_message: null },
}
)
.expectBadge({ label: 'tests', message: isDefaultCompactTestTotals })
t.create('Test status with custom labels')
.get('/https/jenkins.sqlalchemy.org/alembic_coverage.json', {
qs: {
passed_label: 'good',
failed_label: 'bad',
skipped_label: 'n/a',
},
})
.get(
'/tests.json?jobUrl=https://jenkins.sqlalchemy.org/job/alembic_coverage',
{
qs: {
passed_label: 'good',
failed_label: 'bad',
skipped_label: 'n/a',
},
}
)
.expectBadge({ label: 'tests', message: isCustomTestTotals })
t.create('Test status with compact message and custom labels')
.get('/https/jenkins.sqlalchemy.org/alembic_coverage.json', {
qs: {
compact_message: null,
passed_label: '💃',
failed_label: '🤦‍♀️',
skipped_label: '🤷',
},
})
.get(
'/tests.json?jobUrl=https://jenkins.sqlalchemy.org/job/alembic_coverage',
{
qs: {
compact_message: null,
passed_label: '💃',
failed_label: '🤦‍♀️',
skipped_label: '🤷',
},
}
)
.expectBadge({
label: 'tests',
message: isCustomCompactTestTotals,
})
t.create('Test status on job with no tests')
.get('/https/ci.eclipse.org/openj9/job/Build-Doc-Push_to_ghpages.json')
.get(
'/tests.json?jobUrl=https://ci.eclipse.org/openj9/job/Build-Doc-Push_to_ghpages'
)
.expectBadge({ label: 'tests', message: 'no tests found' })
t.create('Test status on non-existent job')
.get('/https/ci.eclipse.org/openj9/job/does-not-exist.json')
.get('/tests.json?jobUrl=https://ci.eclipse.org/openj9/job/does-not-exist')
.expectBadge({ label: 'tests', message: 'instance or job not found' })