Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[OBS] add Open Build Service service-badge #6993

Merged
merged 9 commits into from
Sep 25, 2021
4 changes: 4 additions & 0 deletions config/custom-environment-variables.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ public:
authorizedOrigins: 'NEXUS_ORIGINS'
npm:
authorizedOrigins: 'NPM_ORIGINS'
obs:
authorizedOrigins: 'OBS_ORIGINS'
sonar:
authorizedOrigins: 'SONAR_ORIGINS'
teamcity:
Expand Down Expand Up @@ -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'
Expand Down
2 changes: 2 additions & 0 deletions config/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ public:
debug:
enabled: false
intervalSeconds: 200
obs:
authorizedOrigins: 'https://api.opensuse.org'
weblate:
authorizedOrigins: 'https://hosted.weblate.org'
trace: false
Expand Down
2 changes: 2 additions & 0 deletions config/local.template.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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: '...'
Expand Down
3 changes: 3 additions & 0 deletions core/server/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ const publicConfigSchema = Joi.object({
}).default({ authorizedOrigins: [] }),
nexus: defaultService,
npm: defaultService,
obs: defaultService,
sonar: defaultService,
teamcity: defaultService,
weblate: defaultService,
Expand Down Expand Up @@ -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(),
Expand Down
15 changes: 15 additions & 0 deletions doc/server-secrets.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`)
Expand Down
1 change: 1 addition & 0 deletions services/build-status.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const greenStatuses = [
const orangeStatuses = ['partially succeeded', 'unstable', 'timeout']

const redStatuses = [
'broken',
'error',
'errored',
'failed',
Expand Down
1 change: 1 addition & 0 deletions services/build-status.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ test(renderBuildStatusBadge, () => {

test(renderBuildStatusBadge, () => {
forCases([
given({ status: 'broken' }),
given({ status: 'error' }),
given({ status: 'errored' }),
given({ status: 'failed' }),
Expand Down
34 changes: 34 additions & 0 deletions services/obs/obs-build-status.js
Original file line number Diff line number Diff line change
@@ -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 changes: 81 additions & 0 deletions services/obs/obs.service.js
Original file line number Diff line number Diff line change
@@ -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'],
},
sp1ritCS marked this conversation as resolved.
Show resolved Hide resolved
]

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 changes: 25 additions & 0 deletions services/obs/obs.tester.js
Original file line number Diff line number Diff line change
@@ -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',
})