[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'
|
||||
npm:
|
||||
authorizedOrigins: 'NPM_ORIGINS'
|
||||
obs:
|
||||
authorizedOrigins: 'OBS_ORIGINS'
|
||||
sonar:
|
||||
authorizedOrigins: 'SONAR_ORIGINS'
|
||||
teamcity:
|
||||
@@ -87,6 +89,8 @@ private:
|
||||
nexus_user: 'NEXUS_USER'
|
||||
nexus_pass: 'NEXUS_PASS'
|
||||
npm_token: 'NPM_TOKEN'
|
||||
obs_user: 'OBS_USER'
|
||||
obs_pass: 'OBS_PASS'
|
||||
redis_url: 'REDIS_URL'
|
||||
sentry_dsn: 'SENTRY_DSN'
|
||||
shields_secret: 'SHIELDS_SECRET'
|
||||
|
||||
@@ -22,6 +22,8 @@ public:
|
||||
debug:
|
||||
enabled: false
|
||||
intervalSeconds: 200
|
||||
obs:
|
||||
authorizedOrigins: 'https://api.opensuse.org'
|
||||
weblate:
|
||||
authorizedOrigins: 'https://hosted.weblate.org'
|
||||
trace: false
|
||||
|
||||
@@ -6,6 +6,8 @@ private:
|
||||
# preferable for self hosting.
|
||||
gh_token: '...'
|
||||
gitlab_token: '...'
|
||||
obs_user: '...'
|
||||
obs_pass: '...'
|
||||
twitch_client_id: '...'
|
||||
twitch_client_secret: '...'
|
||||
weblate_api_key: '...'
|
||||
|
||||
@@ -134,6 +134,7 @@ const publicConfigSchema = Joi.object({
|
||||
}).default({ authorizedOrigins: [] }),
|
||||
nexus: defaultService,
|
||||
npm: defaultService,
|
||||
obs: defaultService,
|
||||
sonar: defaultService,
|
||||
teamcity: defaultService,
|
||||
weblate: defaultService,
|
||||
@@ -172,6 +173,8 @@ const privateConfigSchema = Joi.object({
|
||||
nexus_user: Joi.string(),
|
||||
nexus_pass: Joi.string(),
|
||||
npm_token: Joi.string(),
|
||||
obs_user: Joi.string(),
|
||||
obs_pass: Joi.string(),
|
||||
redis_url: Joi.string().uri({ scheme: ['redis', 'rediss'] }),
|
||||
sentry_dsn: 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
|
||||
|
||||
## 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)
|
||||
|
||||
- `SL_INSIGHT_USER_UUID` (yml: `private.sl_insight_userUuid`)
|
||||
|
||||
@@ -12,6 +12,7 @@ const greenStatuses = [
|
||||
const orangeStatuses = ['partially succeeded', 'unstable', 'timeout']
|
||||
|
||||
const redStatuses = [
|
||||
'broken',
|
||||
'error',
|
||||
'errored',
|
||||
'failed',
|
||||
|
||||
@@ -53,6 +53,7 @@ test(renderBuildStatusBadge, () => {
|
||||
|
||||
test(renderBuildStatusBadge, () => {
|
||||
forCases([
|
||||
given({ status: 'broken' }),
|
||||
given({ status: 'error' }),
|
||||
given({ status: 'errored' }),
|
||||
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