[OBS] add Open Build Service service-badge (#6993)
* service: add obs service * service: obs: replaced replaceAll with replace and global regex * service: obs: added space between class members * service: obs: support for multiple instances * service: obs: removed user prefix from auth vars obs_userName is now called obs_user and obs_userPass is called obs_pass Co-authored-by: Caleb Cartwright <calebcartwright@users.noreply.github.com> * service: obs: removed constructor hack in favour of serviceKey * service: obs: apply suggestions from @calebcartwright * service: obs: remove unneccesary http status mappings Co-authored-by: Caleb Cartwright <calebcartwright@users.noreply.github.com> Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
This commit is contained in:
@@ -50,6 +50,8 @@ public:
|
|||||||
authorizedOrigins: 'NEXUS_ORIGINS'
|
authorizedOrigins: 'NEXUS_ORIGINS'
|
||||||
npm:
|
npm:
|
||||||
authorizedOrigins: 'NPM_ORIGINS'
|
authorizedOrigins: 'NPM_ORIGINS'
|
||||||
|
obs:
|
||||||
|
authorizedOrigins: 'OBS_ORIGINS'
|
||||||
sonar:
|
sonar:
|
||||||
authorizedOrigins: 'SONAR_ORIGINS'
|
authorizedOrigins: 'SONAR_ORIGINS'
|
||||||
teamcity:
|
teamcity:
|
||||||
@@ -87,6 +89,8 @@ private:
|
|||||||
nexus_user: 'NEXUS_USER'
|
nexus_user: 'NEXUS_USER'
|
||||||
nexus_pass: 'NEXUS_PASS'
|
nexus_pass: 'NEXUS_PASS'
|
||||||
npm_token: 'NPM_TOKEN'
|
npm_token: 'NPM_TOKEN'
|
||||||
|
obs_user: 'OBS_USER'
|
||||||
|
obs_pass: 'OBS_PASS'
|
||||||
redis_url: 'REDIS_URL'
|
redis_url: 'REDIS_URL'
|
||||||
sentry_dsn: 'SENTRY_DSN'
|
sentry_dsn: 'SENTRY_DSN'
|
||||||
shields_secret: 'SHIELDS_SECRET'
|
shields_secret: 'SHIELDS_SECRET'
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ public:
|
|||||||
debug:
|
debug:
|
||||||
enabled: false
|
enabled: false
|
||||||
intervalSeconds: 200
|
intervalSeconds: 200
|
||||||
|
obs:
|
||||||
|
authorizedOrigins: 'https://api.opensuse.org'
|
||||||
weblate:
|
weblate:
|
||||||
authorizedOrigins: 'https://hosted.weblate.org'
|
authorizedOrigins: 'https://hosted.weblate.org'
|
||||||
trace: false
|
trace: false
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ private:
|
|||||||
# preferable for self hosting.
|
# preferable for self hosting.
|
||||||
gh_token: '...'
|
gh_token: '...'
|
||||||
gitlab_token: '...'
|
gitlab_token: '...'
|
||||||
|
obs_user: '...'
|
||||||
|
obs_pass: '...'
|
||||||
twitch_client_id: '...'
|
twitch_client_id: '...'
|
||||||
twitch_client_secret: '...'
|
twitch_client_secret: '...'
|
||||||
weblate_api_key: '...'
|
weblate_api_key: '...'
|
||||||
|
|||||||
@@ -134,6 +134,7 @@ const publicConfigSchema = Joi.object({
|
|||||||
}).default({ authorizedOrigins: [] }),
|
}).default({ authorizedOrigins: [] }),
|
||||||
nexus: defaultService,
|
nexus: defaultService,
|
||||||
npm: defaultService,
|
npm: defaultService,
|
||||||
|
obs: defaultService,
|
||||||
sonar: defaultService,
|
sonar: defaultService,
|
||||||
teamcity: defaultService,
|
teamcity: defaultService,
|
||||||
weblate: defaultService,
|
weblate: defaultService,
|
||||||
@@ -172,6 +173,8 @@ const privateConfigSchema = Joi.object({
|
|||||||
nexus_user: Joi.string(),
|
nexus_user: Joi.string(),
|
||||||
nexus_pass: Joi.string(),
|
nexus_pass: Joi.string(),
|
||||||
npm_token: Joi.string(),
|
npm_token: Joi.string(),
|
||||||
|
obs_user: Joi.string(),
|
||||||
|
obs_pass: Joi.string(),
|
||||||
redis_url: Joi.string().uri({ scheme: ['redis', 'rediss'] }),
|
redis_url: Joi.string().uri({ scheme: ['redis', 'rediss'] }),
|
||||||
sentry_dsn: Joi.string(),
|
sentry_dsn: Joi.string(),
|
||||||
shields_secret: Joi.string(),
|
shields_secret: Joi.string(),
|
||||||
|
|||||||
@@ -193,6 +193,21 @@ installation access to private npm packages
|
|||||||
|
|
||||||
[npm token]: https://docs.npmjs.com/getting-started/working_with_tokens
|
[npm token]: https://docs.npmjs.com/getting-started/working_with_tokens
|
||||||
|
|
||||||
|
## Open Build Service
|
||||||
|
|
||||||
|
- `OBS_USER` (yml: `private.obs_user`)
|
||||||
|
- `OBS_PASS` (yml: `private.obs_user`)
|
||||||
|
|
||||||
|
Only authenticated users are allowed to access the Open Build Service API.
|
||||||
|
Authentication is done by sending a Basic HTTP Authorisation header. A user
|
||||||
|
account for the [reference instance](https://build.opensuse.org) is a SUSE
|
||||||
|
IdP account, which can be created [here](https://idp-portal.suse.com/univention/self-service/#page=createaccount).
|
||||||
|
|
||||||
|
While OBS supports [API tokens](https://openbuildservice.org/help/manuals/obs-user-guide/cha.obs.authorization.token.html#id-1.5.10.16.4),
|
||||||
|
they can only be scoped to execute specific actions on a POST request. This
|
||||||
|
means however, that an actual account is required to read the build status
|
||||||
|
of a package.
|
||||||
|
|
||||||
### SymfonyInsight (formerly Sensiolabs)
|
### SymfonyInsight (formerly Sensiolabs)
|
||||||
|
|
||||||
- `SL_INSIGHT_USER_UUID` (yml: `private.sl_insight_userUuid`)
|
- `SL_INSIGHT_USER_UUID` (yml: `private.sl_insight_userUuid`)
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ const greenStatuses = [
|
|||||||
const orangeStatuses = ['partially succeeded', 'unstable', 'timeout']
|
const orangeStatuses = ['partially succeeded', 'unstable', 'timeout']
|
||||||
|
|
||||||
const redStatuses = [
|
const redStatuses = [
|
||||||
|
'broken',
|
||||||
'error',
|
'error',
|
||||||
'errored',
|
'errored',
|
||||||
'failed',
|
'failed',
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ test(renderBuildStatusBadge, () => {
|
|||||||
|
|
||||||
test(renderBuildStatusBadge, () => {
|
test(renderBuildStatusBadge, () => {
|
||||||
forCases([
|
forCases([
|
||||||
|
given({ status: 'broken' }),
|
||||||
given({ status: 'error' }),
|
given({ status: 'error' }),
|
||||||
given({ status: 'errored' }),
|
given({ status: 'errored' }),
|
||||||
given({ status: 'failed' }),
|
given({ status: 'failed' }),
|
||||||
|
|||||||
34
services/obs/obs-build-status.js
Normal file
34
services/obs/obs-build-status.js
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import Joi from 'joi'
|
||||||
|
import {
|
||||||
|
isBuildStatus as gIsBuildStatus,
|
||||||
|
renderBuildStatusBadge as gRenderBuildStatusBadge,
|
||||||
|
} from '../build-status.js'
|
||||||
|
|
||||||
|
const localStatuses = {
|
||||||
|
blocked: 'inactive',
|
||||||
|
disabled: 'inactive',
|
||||||
|
finished: 'orange',
|
||||||
|
'scheduled-warning': 'orange',
|
||||||
|
signing: 'orange',
|
||||||
|
unknown: 'inactive',
|
||||||
|
unresolvable: 'red',
|
||||||
|
}
|
||||||
|
|
||||||
|
const isBuildStatus = Joi.alternatives().try(
|
||||||
|
gIsBuildStatus,
|
||||||
|
Joi.equal(...Object.keys(localStatuses))
|
||||||
|
)
|
||||||
|
|
||||||
|
function renderBuildStatusBadge({ repository, status }) {
|
||||||
|
const color = localStatuses[status]
|
||||||
|
if (color) {
|
||||||
|
return {
|
||||||
|
message: status.toLowerCase(),
|
||||||
|
color,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return gRenderBuildStatusBadge({ status: status.toLowerCase() })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { isBuildStatus, renderBuildStatusBadge }
|
||||||
81
services/obs/obs.service.js
Normal file
81
services/obs/obs.service.js
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
import Joi from 'joi'
|
||||||
|
import { BaseXmlService } from '../index.js'
|
||||||
|
import { optionalUrl } from '../validators.js'
|
||||||
|
import { isBuildStatus, renderBuildStatusBadge } from './obs-build-status.js'
|
||||||
|
|
||||||
|
const schema = Joi.object({
|
||||||
|
status: Joi.object({
|
||||||
|
'@_code': isBuildStatus,
|
||||||
|
}).required(),
|
||||||
|
}).required()
|
||||||
|
|
||||||
|
export default class ObsService extends BaseXmlService {
|
||||||
|
static category = 'build'
|
||||||
|
static route = {
|
||||||
|
base: 'obs',
|
||||||
|
pattern: ':project/:packageName/:repository/:arch',
|
||||||
|
queryParamSchema: Joi.object({
|
||||||
|
instance: optionalUrl,
|
||||||
|
}).required(),
|
||||||
|
}
|
||||||
|
|
||||||
|
static auth = {
|
||||||
|
userKey: 'obs_user',
|
||||||
|
passKey: 'obs_pass',
|
||||||
|
serviceKey: 'obs',
|
||||||
|
isRequired: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
static examples = [
|
||||||
|
{
|
||||||
|
title: 'OBS package build status',
|
||||||
|
namedParams: {
|
||||||
|
project: 'openSUSE:Tools',
|
||||||
|
packageName: 'osc',
|
||||||
|
repository: 'Debian_11',
|
||||||
|
arch: 'x86_64',
|
||||||
|
},
|
||||||
|
queryParams: { instance: 'https://api.opensuse.org' },
|
||||||
|
staticPreview: this.render({
|
||||||
|
repository: 'Debian_11',
|
||||||
|
status: 'succeeded',
|
||||||
|
}),
|
||||||
|
keywords: ['open build service'],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
static defaultBadgeData = { label: 'build' }
|
||||||
|
|
||||||
|
static render({ repository, status }) {
|
||||||
|
return renderBuildStatusBadge({ repository, status })
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetch({ instance, project, packageName, repository, arch }) {
|
||||||
|
return this._requestXml(
|
||||||
|
this.authHelper.withBasicAuth({
|
||||||
|
schema,
|
||||||
|
url: `${instance}/build/${project}/${repository}/${arch}/${packageName}/_status`,
|
||||||
|
parserOptions: {
|
||||||
|
ignoreAttributes: false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async handle(
|
||||||
|
{ project, packageName, repository, arch },
|
||||||
|
{ instance = 'https://api.opensuse.org' }
|
||||||
|
) {
|
||||||
|
const resp = await this.fetch({
|
||||||
|
instance,
|
||||||
|
project,
|
||||||
|
packageName,
|
||||||
|
repository,
|
||||||
|
arch,
|
||||||
|
})
|
||||||
|
return this.constructor.render({
|
||||||
|
repository,
|
||||||
|
status: resp.status['@_code'],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
25
services/obs/obs.tester.js
Normal file
25
services/obs/obs.tester.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { ServiceTester } from '../tester.js'
|
||||||
|
import { noToken } from '../test-helpers.js'
|
||||||
|
import ObsService from './obs.service.js'
|
||||||
|
import { isBuildStatus } from './obs-build-status.js'
|
||||||
|
|
||||||
|
export const t = new ServiceTester({
|
||||||
|
id: 'obs',
|
||||||
|
title: 'openSUSE Open Build Service',
|
||||||
|
})
|
||||||
|
|
||||||
|
t.create('status (valid)')
|
||||||
|
.skipWhen(noToken(ObsService))
|
||||||
|
.get('/openSUSE:Factory/aaa_base/standard/x86_64.json?label=standard')
|
||||||
|
.expectBadge({
|
||||||
|
label: 'standard',
|
||||||
|
message: isBuildStatus,
|
||||||
|
})
|
||||||
|
|
||||||
|
t.create('status (invalid)')
|
||||||
|
.skipWhen(noToken(ObsService))
|
||||||
|
.get('/home:sp1rit/this_package_will_never_exist/repo/arch.json')
|
||||||
|
.expectBadge({
|
||||||
|
label: 'build',
|
||||||
|
message: 'not found',
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user