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

Add [ROS] version service #8169

Merged
merged 4 commits into from
Jul 12, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions services/ros/ros-version-helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import yaml from 'js-yaml'
import { NotFound, InvalidResponse } from '../index.js'

export function parseReleaseVersionFromDistro(configYaml, packageName) {
let packageInfo
try {
const config = yaml.load(configYaml)
packageInfo = config.repositories[packageName]
} catch (err) {
throw new InvalidResponse({
prettyMessage: 'invalid distribution.yml',
underlyingError: err,
})
}

if (!packageInfo) {
throw new NotFound({ prettyMessage: `package not found: ${packageName}` })
}

const version = packageInfo.release?.version
if (typeof version !== 'string') {
throw new NotFound({
chris48s marked this conversation as resolved.
Show resolved Hide resolved
prettyMessage: `unable to determine version for ${packageName}`,
})
}

// Strip off "release inc" suffix
return version.replace(/-\d+$/, '')
}
44 changes: 44 additions & 0 deletions services/ros/ros-version-helpers.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { expect } from 'chai'
import { parseReleaseVersionFromDistro } from './ros-version-helpers.js'

describe('parseReleaseVersionFromDistro', function () {
it('returns correct version', function () {
expect(
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')
})
})
111 changes: 111 additions & 0 deletions services/ros/ros-version.service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import gql from 'graphql-tag'
import Joi from 'joi'
import { renderVersionBadge } from '../version.js'
import { GithubAuthV4Service } from '../github/github-auth-service.js'
import { NotFound } from '../index.js'
import { parseReleaseVersionFromDistro } from './ros-version-helpers.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()

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: 'humble',
},
},
]

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))
chris48s marked this conversation as resolved.
Show resolved Hide resolved

const ref = tags[0] ? `refs/tags/${distro}/${tags[0]}` : 'refs/heads/master'
const prettyRef = tags[0] ? `${distro}/${tags[0]}` : 'master'
chris48s marked this conversation as resolved.
Show resolved Hide resolved

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 = parseReleaseVersionFromDistro(
contentJson.data.repository.object.text,
packageName
)

return { ...renderVersionBadge({ version }), label: distro }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For version badges, the label is usually the name of the package registry, as opposed to the package.

I think we do want to include ros in the label. With the conda badges, we include the channel in the label separated by a pipe. How about we do similar here - ros|distro e.g:

Copy link
Contributor Author

@jtbandes jtbandes Jul 11, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, makes sense. What do you think about including spaces around the pipe? Like this: I think it makes it more readable.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah agreed 👍 Change it on this PR and I will do another PR to update the conda one.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done!

}
}
28 changes: 28 additions & 0 deletions services/ros/ros-version.tester.js
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: 'humble', message: isSemver })

t.create('gets the package version of vision_msgs in EOL distro')
.get('/lunar/vision_msgs.json')
.expectBadge({ label: 'lunar', message: isSemver })

t.create('returns not found for invalid package')
.get('/humble/this package does not exist - ros test.json')
.expectBadge({
label: 'version',
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: 'version',
color: 'red',
message: 'distribution.yaml not found: xxxxxx@master',
})