forked from badges/shields
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add [ROS] version service (badges#8169)
* Add [ROS] version service * review feedback * add spaces
- Loading branch information
1 parent
3bc0201
commit 665aa75
Showing
3 changed files
with
226 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
import gql from 'graphql-tag' | ||
import Joi from 'joi' | ||
import yaml from 'js-yaml' | ||
import { renderVersionBadge } from '../version.js' | ||
import { GithubAuthV4Service } from '../github/github-auth-service.js' | ||
import { NotFound, InvalidResponse } from '../index.js' | ||
|
||
const tagsSchema = Joi.object({ | ||
data: Joi.object({ | ||
repository: Joi.object({ | ||
refs: Joi.object({ | ||
edges: Joi.array() | ||
.items({ | ||
node: Joi.object({ | ||
name: Joi.string().required(), | ||
}).required(), | ||
}) | ||
.required(), | ||
}).required(), | ||
}).required(), | ||
}).required(), | ||
}).required() | ||
|
||
const contentSchema = Joi.object({ | ||
data: Joi.object({ | ||
repository: Joi.object({ | ||
object: Joi.object({ | ||
text: Joi.string().required(), | ||
}).allow(null), | ||
}).required(), | ||
}).required(), | ||
}).required() | ||
|
||
const distroSchema = Joi.object({ | ||
repositories: Joi.object().required(), | ||
}) | ||
const packageSchema = Joi.object({ | ||
release: Joi.object({ | ||
version: Joi.string().required(), | ||
}).required(), | ||
}) | ||
|
||
export default class RosVersion extends GithubAuthV4Service { | ||
static category = 'version' | ||
|
||
static route = { base: 'ros/v', pattern: ':distro/:packageName' } | ||
|
||
static examples = [ | ||
{ | ||
title: 'ROS Package Index', | ||
namedParams: { distro: 'humble', packageName: 'vision_msgs' }, | ||
staticPreview: { | ||
...renderVersionBadge({ version: '4.0.0' }), | ||
label: 'ros | humble', | ||
}, | ||
}, | ||
] | ||
|
||
static defaultBadgeData = { label: 'ros' } | ||
|
||
async handle({ distro, packageName }) { | ||
const tagsJson = await this._requestGraphql({ | ||
query: gql` | ||
query ($refPrefix: String!) { | ||
repository(owner: "ros", name: "rosdistro") { | ||
refs( | ||
refPrefix: $refPrefix | ||
first: 30 | ||
orderBy: { field: TAG_COMMIT_DATE, direction: DESC } | ||
) { | ||
edges { | ||
node { | ||
name | ||
} | ||
} | ||
} | ||
} | ||
} | ||
`, | ||
variables: { refPrefix: `refs/tags/${distro}/` }, | ||
schema: tagsSchema, | ||
}) | ||
|
||
// Filter for tags that look like dates: humble/2022-06-10 | ||
const tags = tagsJson.data.repository.refs.edges | ||
.map(edge => edge.node.name) | ||
.filter(tag => /^\d+-\d+-\d+$/.test(tag)) | ||
.sort() | ||
.reverse() | ||
|
||
const ref = tags[0] ? `refs/tags/${distro}/${tags[0]}` : 'refs/heads/master' | ||
const prettyRef = tags[0] ? `${distro}/${tags[0]}` : 'master' | ||
|
||
const contentJson = await this._requestGraphql({ | ||
query: gql` | ||
query ($expression: String!) { | ||
repository(owner: "ros", name: "rosdistro") { | ||
object(expression: $expression) { | ||
... on Blob { | ||
text | ||
} | ||
} | ||
} | ||
} | ||
`, | ||
variables: { | ||
expression: `${ref}:${distro}/distribution.yaml`, | ||
}, | ||
schema: contentSchema, | ||
}) | ||
|
||
if (!contentJson.data.repository.object) { | ||
throw new NotFound({ | ||
prettyMessage: `distribution.yaml not found: ${distro}@${prettyRef}`, | ||
}) | ||
} | ||
const version = this.constructor._parseReleaseVersionFromDistro( | ||
contentJson.data.repository.object.text, | ||
packageName | ||
) | ||
|
||
return { ...renderVersionBadge({ version }), label: `ros | ${distro}` } | ||
} | ||
|
||
static _parseReleaseVersionFromDistro(distroYaml, packageName) { | ||
let distro | ||
try { | ||
distro = yaml.load(distroYaml) | ||
} catch (err) { | ||
throw new InvalidResponse({ | ||
prettyMessage: 'unparseable distribution.yml', | ||
underlyingError: err, | ||
}) | ||
} | ||
|
||
const validatedDistro = this._validate(distro, distroSchema, { | ||
prettyErrorMessage: 'invalid distribution.yml', | ||
}) | ||
if (!validatedDistro.repositories[packageName]) { | ||
throw new NotFound({ prettyMessage: `package not found: ${packageName}` }) | ||
} | ||
|
||
const packageInfo = this._validate( | ||
validatedDistro.repositories[packageName], | ||
packageSchema, | ||
{ | ||
prettyErrorMessage: `invalid section for ${packageName} in distribution.yml`, | ||
} | ||
) | ||
|
||
// Strip off "release inc" suffix | ||
return packageInfo.release.version.replace(/-\d+$/, '') | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import { expect } from 'chai' | ||
import RosVersion from './ros-version.service.js' | ||
|
||
describe('parseReleaseVersionFromDistro', function () { | ||
it('returns correct version', function () { | ||
expect( | ||
RosVersion._parseReleaseVersionFromDistro( | ||
` | ||
%YAML 1.1 | ||
# ROS distribution file | ||
# see REP 143: http://ros.org/reps/rep-0143.html | ||
--- | ||
release_platforms: | ||
debian: | ||
- bullseye | ||
rhel: | ||
- '8' | ||
ubuntu: | ||
- jammy | ||
repositories: | ||
vision_msgs: | ||
doc: | ||
type: git | ||
url: https://github.com/ros-perception/vision_msgs.git | ||
version: ros2 | ||
release: | ||
tags: | ||
release: release/humble/{package}/{version} | ||
url: https://github.com/ros2-gbp/vision_msgs-release.git | ||
version: 4.0.0-2 | ||
source: | ||
test_pull_requests: true | ||
type: git | ||
url: https://github.com/ros-perception/vision_msgs.git | ||
version: ros2 | ||
status: developed | ||
type: distribution | ||
version: 2 | ||
`, | ||
'vision_msgs' | ||
) | ||
).to.equal('4.0.0') | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { isSemver } from '../test-validators.js' | ||
import { createServiceTester } from '../tester.js' | ||
|
||
export const t = await createServiceTester() | ||
|
||
t.create('gets the package version of vision_msgs in active distro') | ||
.get('/humble/vision_msgs.json') | ||
.expectBadge({ label: 'ros | humble', message: isSemver }) | ||
|
||
t.create('gets the package version of vision_msgs in EOL distro') | ||
.get('/lunar/vision_msgs.json') | ||
.expectBadge({ label: 'ros | lunar', message: isSemver }) | ||
|
||
t.create('returns not found for invalid package') | ||
.get('/humble/this package does not exist - ros test.json') | ||
.expectBadge({ | ||
label: 'ros', | ||
color: 'red', | ||
message: 'package not found: this package does not exist - ros test', | ||
}) | ||
|
||
t.create('returns error for invalid distro') | ||
.get('/xxxxxx/vision_msgs.json') | ||
.expectBadge({ | ||
label: 'ros', | ||
color: 'red', | ||
message: 'distribution.yaml not found: xxxxxx@master', | ||
}) |