Skip to content

Commit

Permalink
[OBS] add Open Build Service service-badge (#6993)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
3 people authored Sep 25, 2021
1 parent e9153ab commit 8a9efb2
Show file tree
Hide file tree
Showing 10 changed files with 168 additions and 0 deletions.
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'],
},
]

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',
})

0 comments on commit 8a9efb2

Please sign in to comment.