add [WingetVersion] Badge (#10245)
* feat: add winget version badge * chore: accept dotted path instead of slashed * test: add test for winget-version * fix: remove debug code * chore: use winget-specific version compare algorithm * fix: support latest and unknown * fix(winget/version): trailing '.0' handling is incorrect * fix(winget/version): latest returns last newest version instead of the first newest version * fix(winget/version): confusing subpackage and version name * fix(winget/version): example for latest is incorrect * add a couple of extra test cases for latest() --------- Co-authored-by: chris48s <git@chris-shaw.dev>
This commit is contained in:
172
services/winget/version.js
Normal file
172
services/winget/version.js
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
/**
|
||||||
|
* Comparing versions with winget's version comparator.
|
||||||
|
*
|
||||||
|
* See https://github.com/microsoft/winget-cli/blob/ae566c7bf21cfcc75be7ec30e4036a30eede8396/src/AppInstallerSharedLib/Versions.cpp for original implementation.
|
||||||
|
*
|
||||||
|
* @module
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compares two strings representing version numbers lexicographically and returns an integer value.
|
||||||
|
*
|
||||||
|
* @param {string} v1 - The first version to compare
|
||||||
|
* @param {string} v2 - The second version to compare
|
||||||
|
* @returns {number} -1 if v1 is smaller than v2, 1 if v1 is larger than v2, 0 if v1 and v2 are equal
|
||||||
|
* @example
|
||||||
|
* compareVersion('1.2.3', '1.2.4') // returns -1 because numeric part of first version is smaller than the numeric part of second version.
|
||||||
|
*/
|
||||||
|
function compareVersion(v1, v2) {
|
||||||
|
// https://github.com/microsoft/winget-cli/blob/ae566c7bf21cfcc75be7ec30e4036a30eede8396/src/AppInstallerSharedLib/Versions.cpp#L109-L173
|
||||||
|
// This implementation does not parse s_Approximate_Greater_Than
|
||||||
|
// and s_Approximate_Less_Than since they won't appear in directory name (package version parsed by shields.io)
|
||||||
|
const v1Trimmed = trimPrefix(v1)
|
||||||
|
const v2Trimmed = trimPrefix(v2)
|
||||||
|
|
||||||
|
const v1Latest = v1Trimmed.trim().toLowerCase() === 'latest'
|
||||||
|
const v2Latest = v2Trimmed.trim().toLowerCase() === 'latest'
|
||||||
|
|
||||||
|
if (v1Latest && v2Latest) {
|
||||||
|
return 0
|
||||||
|
} else if (v1Latest) {
|
||||||
|
return 1
|
||||||
|
} else if (v2Latest) {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
const v1Unknown = v1Trimmed.trim().toLowerCase() === 'unknown'
|
||||||
|
const v2Unknown = v2Trimmed.trim().toLowerCase() === 'unknown'
|
||||||
|
|
||||||
|
if (v1Unknown && v2Unknown) {
|
||||||
|
return 0
|
||||||
|
} else if (v1Unknown) {
|
||||||
|
return -1
|
||||||
|
} else if (v2Unknown) {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
const parts1 = v1Trimmed.split('.')
|
||||||
|
const parts2 = v2Trimmed.split('.')
|
||||||
|
|
||||||
|
trimLastZeros(parts1)
|
||||||
|
trimLastZeros(parts2)
|
||||||
|
|
||||||
|
for (let i = 0; i < Math.min(parts1.length, parts2.length); i++) {
|
||||||
|
const part1 = parts1[i]
|
||||||
|
const part2 = parts2[i]
|
||||||
|
|
||||||
|
const compare = compareVersionPart(part1, part2)
|
||||||
|
if (compare !== 0) {
|
||||||
|
return compare
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parts1.length === parts2.length) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parts1.length > parts2.length) {
|
||||||
|
return 1
|
||||||
|
} else if (parts1.length < parts2.length) {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all leading non-digit characters from a version number string
|
||||||
|
* if there is a digit before the split character, or no split characters exist.
|
||||||
|
*
|
||||||
|
* @param {string} version The version number string to trim
|
||||||
|
* @returns {string} The version number string with all leading non-digit characters removed
|
||||||
|
*/
|
||||||
|
function trimPrefix(version) {
|
||||||
|
// https://github.com/microsoft/winget-cli/blob/ae566c7bf21cfcc75be7ec30e4036a30eede8396/src/AppInstallerSharedLib/Versions.cpp#L66
|
||||||
|
// If there is a digit before the split character, or no split characters exist, trim off all leading non-digit characters
|
||||||
|
|
||||||
|
const digitPos = version.match(/(\d.*)/)
|
||||||
|
const splitPos = version.match(/\./)
|
||||||
|
if (digitPos && (splitPos == null || digitPos.index < splitPos.index)) {
|
||||||
|
// there is digit before the split character so strip off all leading non-digit characters
|
||||||
|
return version.slice(digitPos.index)
|
||||||
|
}
|
||||||
|
return version
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all trailing zeros from a version number part array.
|
||||||
|
*
|
||||||
|
* @param {string[]} parts - parts
|
||||||
|
*/
|
||||||
|
function trimLastZeros(parts) {
|
||||||
|
while (parts.length > 1 && parts[parts.length - 1].trim() === '0') {
|
||||||
|
parts.pop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compares two strings representing version number parts lexicographically and returns an integer value.
|
||||||
|
*
|
||||||
|
* @param {string} part1 - The first version part to compare
|
||||||
|
* @param {string} part2 - The second version part to compare
|
||||||
|
* @returns {number} -1 if part1 is smaller than part2, 1 if part1 is larger than part2, 0 if part1 and part2 are equal
|
||||||
|
* @example
|
||||||
|
* compareVersionPart('3', '4') // returns -1 because numeric part of first part is smaller than the numeric part of second part.
|
||||||
|
*/
|
||||||
|
function compareVersionPart(part1, part2) {
|
||||||
|
// https://github.com/microsoft/winget-cli/blob/ae566c7bf21cfcc75be7ec30e4036a30eede8396/src/AppInstallerSharedLib/Versions.cpp#L324-L352
|
||||||
|
const [, numericString1, other1] = part1.trim().match(/^(\d*)(.*)$/)
|
||||||
|
const [, numericString2, other2] = part2.trim().match(/^(\d*)(.*)$/)
|
||||||
|
const numeric1 = parseInt(numericString1 || '0', 10)
|
||||||
|
const numeric2 = parseInt(numericString2 || '0', 10)
|
||||||
|
|
||||||
|
if (numeric1 < numeric2) {
|
||||||
|
return -1
|
||||||
|
} else if (numeric1 > numeric2) {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
// numeric1 === numeric2
|
||||||
|
|
||||||
|
const otherFolded1 = (other1 ?? '').toLowerCase()
|
||||||
|
const otherFolded2 = (other2 ?? '').toLowerCase()
|
||||||
|
|
||||||
|
if (otherFolded1.length !== 0 && otherFolded2.length === 0) {
|
||||||
|
return -1
|
||||||
|
} else if (otherFolded1.length === 0 && otherFolded2.length !== 0) {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if (otherFolded1 < otherFolded2) {
|
||||||
|
return -1
|
||||||
|
} else if (otherFolded1 > otherFolded2) {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the largest version number lexicographically from an array of strings representing version numbers and returns it as a string.
|
||||||
|
*
|
||||||
|
* @param {string[]} versions - The array of version numbers to compare
|
||||||
|
* @returns {string|undefined} The largest version number as a string, or undefined if the array is empty
|
||||||
|
* @example
|
||||||
|
* latest(['1.2.3', '1.2.4', '1.3', '2.0']) // returns '2.0' because it is the largest version number.
|
||||||
|
* latest(['1.2.3', '1.2.4', '1.3-alpha', '2.0-beta']) // returns '2.0-beta'. there is no special handling for pre-release versions.
|
||||||
|
*/
|
||||||
|
function latest(versions) {
|
||||||
|
const len = versions.length
|
||||||
|
if (len === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let version = versions[0]
|
||||||
|
for (let i = 1; i < len; i++) {
|
||||||
|
if (compareVersion(version, versions[i]) <= 0) {
|
||||||
|
version = versions[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return version
|
||||||
|
}
|
||||||
|
|
||||||
|
export { latest, compareVersion }
|
||||||
57
services/winget/version.spec.js
Normal file
57
services/winget/version.spec.js
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import { test, given } from 'sazerac'
|
||||||
|
import { compareVersion, latest } from './version.js'
|
||||||
|
|
||||||
|
describe('Winget Version helpers', function () {
|
||||||
|
test(compareVersion, () => {
|
||||||
|
// basic compare
|
||||||
|
// https://github.com/microsoft/winget-cli/blob/43425fe97d237e03026fca4530dbc422ab445595/src/AppInstallerCLITests/Versions.cpp#L147
|
||||||
|
given('1', '2').expect(-1)
|
||||||
|
given('1.0.0', '2.0.0').expect(-1)
|
||||||
|
given('0.0.1', '0.0.2').expect(-1)
|
||||||
|
given('0.0.1-alpha', '0.0.2-alpha').expect(-1)
|
||||||
|
given('0.0.1-beta', '0.0.2-alpha').expect(-1)
|
||||||
|
given('0.0.1-beta', '0.0.2-alpha').expect(-1)
|
||||||
|
given('13.9.8', '14.1').expect(-1)
|
||||||
|
|
||||||
|
given('1.0', '1.0.0').expect(0)
|
||||||
|
|
||||||
|
// Ensure whitespace doesn't affect equality
|
||||||
|
given('1.0', '1.0 ').expect(0)
|
||||||
|
given('1.0', '1. 0').expect(0)
|
||||||
|
|
||||||
|
// Ensure versions with preambles are sorted correctly
|
||||||
|
given('1.0', 'Version 1.0').expect(0)
|
||||||
|
given('foo1', 'bar1').expect(0)
|
||||||
|
given('v0.0.1', '0.0.2').expect(-1)
|
||||||
|
given('v0.0.1', 'v0.0.2').expect(-1)
|
||||||
|
given('1.a2', '1.b1').expect(-1)
|
||||||
|
given('alpha', 'beta').expect(-1)
|
||||||
|
|
||||||
|
// latest
|
||||||
|
// https://github.com/microsoft/winget-cli/blob/43425fe97d237e03026fca4530dbc422ab445595/src/AppInstallerCLITests/Versions.cpp#L217
|
||||||
|
given('1.0', 'latest').expect(-1)
|
||||||
|
given('100', 'latest').expect(-1)
|
||||||
|
given('943849587389754876.1', 'latest').expect(-1)
|
||||||
|
given('latest', 'LATEST').expect(0)
|
||||||
|
|
||||||
|
// unknown
|
||||||
|
// https://github.com/microsoft/winget-cli/blob/43425fe97d237e03026fca4530dbc422ab445595/src/AppInstallerCLITests/Versions.cpp#L231
|
||||||
|
given('unknown', '1.0').expect(-1)
|
||||||
|
given('unknown', '1.fork').expect(-1)
|
||||||
|
given('unknown', 'UNKNOWN').expect(0)
|
||||||
|
|
||||||
|
// porting failure tests
|
||||||
|
// https://github.com/badges/shields/pull/10245#discussion_r1817931237
|
||||||
|
// trailing .0 and .0-beta
|
||||||
|
given('1.6.0', '1.6.0-beta.98').expect(-1)
|
||||||
|
})
|
||||||
|
|
||||||
|
test(latest, () => {
|
||||||
|
given(['1.2.3', '1.2.4', '2.0', '1.3.9.1']).expect('2.0')
|
||||||
|
given(['1.2.3', '1.2.4', '2.0-beta', '1.3-alpha']).expect('2.0-beta')
|
||||||
|
|
||||||
|
// compareVersion('3.1.1.0', '3.1.1') == 0, so It's free to choose any of them.
|
||||||
|
// I don't know why but it looks winget registry uses last newest version.
|
||||||
|
given(['3.1.1.0', '3.1.1']).expect('3.1.1')
|
||||||
|
})
|
||||||
|
})
|
||||||
120
services/winget/winget-version.service.js
Normal file
120
services/winget/winget-version.service.js
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
import Joi from 'joi'
|
||||||
|
import gql from 'graphql-tag'
|
||||||
|
import { 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'
|
||||||
|
import { latest } from './version.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(),
|
||||||
|
object: Joi.object({
|
||||||
|
entries: Joi.array().items(
|
||||||
|
Joi.object({
|
||||||
|
type: Joi.string().required(),
|
||||||
|
name: Joi.string().required(),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
}).required(),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
})
|
||||||
|
.allow(null)
|
||||||
|
.required(),
|
||||||
|
}).required(),
|
||||||
|
}).required(),
|
||||||
|
}).required()
|
||||||
|
|
||||||
|
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
|
||||||
|
object {
|
||||||
|
... on Tree {
|
||||||
|
entries {
|
||||||
|
type
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
variables: { expression },
|
||||||
|
schema,
|
||||||
|
transformErrors,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async handle({ name }) {
|
||||||
|
const json = await this.fetch({ name })
|
||||||
|
if (json.data.repository.object?.entries == null) {
|
||||||
|
throw new InvalidParameter({
|
||||||
|
prettyMessage: 'package not found',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const entries = json.data.repository.object.entries
|
||||||
|
const directories = entries.filter(entry => entry.type === 'tree')
|
||||||
|
const versionDirs = directories.filter(dir =>
|
||||||
|
dir.object.entries.some(
|
||||||
|
file => file.type === 'blob' && file.name === `${name}.yaml`,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
const versions = versionDirs.map(dir => dir.name)
|
||||||
|
const version = latest(versions)
|
||||||
|
|
||||||
|
if (version == null) {
|
||||||
|
throw new InvalidParameter({
|
||||||
|
prettyMessage: 'no versions found',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return renderVersionBadge({ version })
|
||||||
|
}
|
||||||
|
}
|
||||||
343
services/winget/winget-version.tester.js
Normal file
343
services/winget/winget-version.tester.js
Normal file
@@ -0,0 +1,343 @@
|
|||||||
|
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',
|
||||||
|
object: {
|
||||||
|
entries: [
|
||||||
|
{
|
||||||
|
type: 'blob',
|
||||||
|
name: 'Microsoft.DevHome.installer.yaml',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'blob',
|
||||||
|
name: 'Microsoft.DevHome.locale.en-US.yaml',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'blob',
|
||||||
|
name: 'Microsoft.DevHome.yaml',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'tree',
|
||||||
|
name: '0.1101.416.0',
|
||||||
|
object: {
|
||||||
|
entries: [
|
||||||
|
{
|
||||||
|
type: 'blob',
|
||||||
|
name: 'Microsoft.DevHome.installer.yaml',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'blob',
|
||||||
|
name: 'Microsoft.DevHome.locale.en-US.yaml',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'blob',
|
||||||
|
name: 'Microsoft.DevHome.yaml',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'tree',
|
||||||
|
name: '0.1201.442.0',
|
||||||
|
object: {
|
||||||
|
entries: [
|
||||||
|
{
|
||||||
|
type: 'blob',
|
||||||
|
name: 'Microsoft.DevHome.installer.yaml',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'blob',
|
||||||
|
name: 'Microsoft.DevHome.locale.en-US.yaml',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'blob',
|
||||||
|
name: 'Microsoft.DevHome.yaml',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'tree',
|
||||||
|
name: '0.137.141.0',
|
||||||
|
object: {
|
||||||
|
entries: [
|
||||||
|
{
|
||||||
|
type: 'blob',
|
||||||
|
name: 'Microsoft.DevHome.installer.yaml',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'blob',
|
||||||
|
name: 'Microsoft.DevHome.locale.en-US.yaml',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'blob',
|
||||||
|
name: 'Microsoft.DevHome.yaml',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'tree',
|
||||||
|
name: '0.200.170.0',
|
||||||
|
object: {
|
||||||
|
entries: [
|
||||||
|
{
|
||||||
|
type: 'blob',
|
||||||
|
name: 'Microsoft.DevHome.installer.yaml',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'blob',
|
||||||
|
name: 'Microsoft.DevHome.locale.en-US.yaml',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'blob',
|
||||||
|
name: 'Microsoft.DevHome.yaml',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'tree',
|
||||||
|
name: '0.503.261.0',
|
||||||
|
object: {
|
||||||
|
entries: [
|
||||||
|
{
|
||||||
|
type: 'blob',
|
||||||
|
name: 'Microsoft.DevHome.installer.yaml',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'blob',
|
||||||
|
name: 'Microsoft.DevHome.locale.en-US.yaml',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'blob',
|
||||||
|
name: 'Microsoft.DevHome.yaml',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'tree',
|
||||||
|
name: '0.601.285.0',
|
||||||
|
object: {
|
||||||
|
entries: [
|
||||||
|
{
|
||||||
|
type: 'blob',
|
||||||
|
name: 'Microsoft.DevHome.installer.yaml',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'blob',
|
||||||
|
name: 'Microsoft.DevHome.locale.en-US.yaml',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'blob',
|
||||||
|
name: 'Microsoft.DevHome.yaml',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'tree',
|
||||||
|
name: '0.601.297.0',
|
||||||
|
object: {
|
||||||
|
entries: [
|
||||||
|
{
|
||||||
|
type: 'blob',
|
||||||
|
name: 'Microsoft.DevHome.installer.yaml',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'blob',
|
||||||
|
name: 'Microsoft.DevHome.locale.en-US.yaml',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'blob',
|
||||||
|
name: 'Microsoft.DevHome.yaml',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'tree',
|
||||||
|
name: '0.701.323.0',
|
||||||
|
object: {
|
||||||
|
entries: [
|
||||||
|
{
|
||||||
|
type: 'blob',
|
||||||
|
name: 'Microsoft.DevHome.installer.yaml',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'blob',
|
||||||
|
name: 'Microsoft.DevHome.locale.en-US.yaml',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'blob',
|
||||||
|
name: 'Microsoft.DevHome.yaml',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'tree',
|
||||||
|
name: '0.801.344.0',
|
||||||
|
object: {
|
||||||
|
entries: [
|
||||||
|
{
|
||||||
|
type: 'blob',
|
||||||
|
name: 'Microsoft.DevHome.installer.yaml',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'blob',
|
||||||
|
name: 'Microsoft.DevHome.locale.en-US.yaml',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'blob',
|
||||||
|
name: 'Microsoft.DevHome.yaml',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.get('/Microsoft.DevHome.json')
|
||||||
|
.expectBadge({ label: 'winget', message: 'v0.1201.442.0' })
|
||||||
|
|
||||||
|
// Both 'Some.Package' and 'Some.Package.Sub' are present in the response.
|
||||||
|
// We should ignore 'Some.Package.Sub' in response to 'Some.Package' request.
|
||||||
|
// In this test case, Canonical.Ubuntu.2404 is present, but it should not be treated as Canonical.Ubuntu version 2404.
|
||||||
|
t.create('do not pick sub-package as version')
|
||||||
|
.intercept(nock =>
|
||||||
|
nock('https://api.github.com/')
|
||||||
|
.post('/graphql')
|
||||||
|
.reply(200, {
|
||||||
|
data: {
|
||||||
|
repository: {
|
||||||
|
object: {
|
||||||
|
entries: [
|
||||||
|
{
|
||||||
|
type: 'blob',
|
||||||
|
name: '.validation',
|
||||||
|
object: {},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'tree',
|
||||||
|
name: '1804',
|
||||||
|
object: {
|
||||||
|
entries: [
|
||||||
|
{
|
||||||
|
type: 'tree',
|
||||||
|
name: '1804.6.4.0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'tree',
|
||||||
|
name: '2004',
|
||||||
|
object: {
|
||||||
|
entries: [
|
||||||
|
{
|
||||||
|
type: 'tree',
|
||||||
|
name: '2004.6.16.0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'tree',
|
||||||
|
name: '2204.1.8.0',
|
||||||
|
object: {
|
||||||
|
entries: [
|
||||||
|
{
|
||||||
|
type: 'blob',
|
||||||
|
name: 'Canonical.Ubuntu.installer.yaml',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'blob',
|
||||||
|
name: 'Canonical.Ubuntu.locale.en-US.yaml',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'blob',
|
||||||
|
name: 'Canonical.Ubuntu.locale.zh-CN.yaml',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'blob',
|
||||||
|
name: 'Canonical.Ubuntu.yaml',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'tree',
|
||||||
|
name: '2204',
|
||||||
|
object: {
|
||||||
|
entries: [
|
||||||
|
{
|
||||||
|
type: 'blob',
|
||||||
|
name: '.validation',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'tree',
|
||||||
|
name: '2204.0.10.0',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'tree',
|
||||||
|
name: '2204.2.47.0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'tree',
|
||||||
|
name: '2404',
|
||||||
|
object: {
|
||||||
|
entries: [
|
||||||
|
{
|
||||||
|
type: 'blob',
|
||||||
|
name: '.validation',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'tree',
|
||||||
|
name: '2404.0.5.0',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.get('/Canonical.Ubuntu.json')
|
||||||
|
.expectBadge({ label: 'winget', message: 'v2204.1.8.0' })
|
||||||
Reference in New Issue
Block a user