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 [WingetVersion] Badge #10245

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
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
97 changes: 97 additions & 0 deletions services/winget/winget-version.service.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import Joi from 'joi'
import gql from 'graphql-tag'
import { latest, renderVersionBadge } from '../version.js'
import { InvalidParameter, pathParam } from '../index.js'
import { GithubAuthV4Service } from '../github/github-auth-service.js'
import { transformErrors } from '../github/github-helpers.js'

const schema = Joi.object({
data: Joi.object({
repository: Joi.object({
object: Joi.object({
entries: Joi.array().items(
Joi.object({
type: Joi.string().required(),
name: Joi.string().required(),
}),
),
})
.allow(null)
.required(),
}).required(),
}).required(),
}).required()
Copy link
Member

Choose a reason for hiding this comment

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

👍


export default class WingetVersion extends GithubAuthV4Service {
static category = 'version'

static route = {
base: 'winget/v',
pattern: ':name',
}

static openApi = {
'/winget/v/{name}': {
get: {
summary: 'WinGet Package Version',
description: 'WinGet Community Repository',
parameters: [
pathParam({
name: 'name',
example: 'Microsoft.WSL',
}),
],
},
},
}

static defaultBadgeData = {
label: 'winget',
}

async fetch({ name }) {
const nameFirstLower = name[0].toLowerCase()
const nameSlashed = name.replaceAll('.', '/')
const path = `manifests/${nameFirstLower}/${nameSlashed}`
const expression = `HEAD:${path}`
return this._requestGraphql({
query: gql`
query RepoFiles($expression: String!) {
repository(owner: "microsoft", name: "winget-pkgs") {
object(expression: $expression) {
... on Tree {
entries {
type
name
}
}
}
}
}
`,
variables: { expression },
schema,
transformErrors,
})
}

async handle({ name }) {
try {
const json = await this.fetch({ name })
if (json.data.repository.object === null) {
throw new InvalidParameter({
prettyMessage: 'package not found',
})
}
const entries = json.data.repository.object.entries
const directories = entries.filter(file => file.type === 'tree')
const versions = directories.map(file => file.name)
const version = latest(versions)
Copy link
Member

Choose a reason for hiding this comment

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

Is there any way we could find out what the registry considers the "latest" version of a package? Do we have any info on how WinGet sorts versions?

I'm a bit worried that there doesn't really seem to be any constraint on what a version "number" can be. Here's some examples that have one or more odd ones:

Given it seems a version number can be basically any string, it seems like this we are going to get this wrong in some cases.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry for late response. I will investigate the update process in winget database

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I could noy find meaningful source code of winget-pkgs pipeline but I found version comparesion in winget-cli, that are used when one package is provided by multiple multiple sources.

I don't know if this compression is used by winget-pkgs pipeline and may not work well with some if your example packages but I think it's reasonable to use this comparesion.

I will implement this compression in services/winget folder with unit tests.

Version Implementation: https://github.com/microsoft/winget-cli/blob/ae566c7bf21cfcc75be7ec30e4036a30eede8396/src/AppInstallerSharedLib/Versions.cpp

Use case in winget-cli: https://github.com/microsoft/winget-cli/blob/ae566c7bf21cfcc75be7ec30e4036a30eede8396/src/AppInstallerRepositoryCore/PackageVersionSelection.cpp#L82-L100

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have implemented in 6b70732


return renderVersionBadge({ version })
} catch (e) {
anatawa12 marked this conversation as resolved.
Show resolved Hide resolved
console.log(e)
throw e
}
}
}
73 changes: 73 additions & 0 deletions services/winget/winget-version.tester.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { isVPlusDottedVersionNClauses } from '../test-validators.js'
import { createServiceTester } from '../tester.js'

export const t = await createServiceTester()

// basic test
t.create('gets the package version of WSL')
.get('/Microsoft.WSL.json')
.expectBadge({ label: 'winget', message: isVPlusDottedVersionNClauses })

// test more than one dots
t.create('gets the package version of .NET 8')
.get('/Microsoft.DotNet.SDK.8.json')
.expectBadge({ label: 'winget', message: isVPlusDottedVersionNClauses })

// test sort based on dotted version order instead of ASCII
t.create('gets the latest version')
.intercept(nock =>
nock('https://api.github.com/')
.post('/graphql')
.reply(200, {
data: {
repository: {
object: {
entries: [
{
type: 'tree',
name: '0.1001.389.0',
},
{
type: 'tree',
name: '0.1101.416.0',
},
{
type: 'tree',
name: '0.1201.442.0',
},
{
type: 'tree',
name: '0.137.141.0',
},
{
type: 'tree',
name: '0.200.170.0',
},
{
type: 'tree',
name: '0.503.261.0',
},
{
type: 'tree',
name: '0.601.285.0',
},
{
type: 'tree',
name: '0.601.297.0',
},
{
type: 'tree',
name: '0.701.323.0',
},
{
type: 'tree',
name: '0.801.344.0',
},
],
},
},
},
}),
)
.get('/Microsoft.DevHome.json')
.expectBadge({ label: 'winget', message: 'v0.1201.442.0' })
Loading