Change version infering to use full semver range spec (#1248)

* Add support for all semver and forge maven range.

* Fix that old forge was checking version as an object

* Fix new version range checking not being applied to quilt.mod.jsons

* run fix
This commit is contained in:
Brady
2023-08-02 18:32:03 -02:30
committed by GitHub
parent 7c2ea3dcfd
commit a132f0f41d
3 changed files with 108 additions and 57 deletions

View File

@@ -1,6 +1,7 @@
import TOML from '@ltd/j-toml' import TOML from '@ltd/j-toml'
import JSZip from 'jszip' import JSZip from 'jszip'
import yaml from 'js-yaml' import yaml from 'js-yaml'
import { satisfies } from 'semver'
export const inferVersionInfo = async function (rawFile, project, gameVersions) { export const inferVersionInfo = async function (rawFile, project, gameVersions) {
function versionType(number) { function versionType(number) {
@@ -17,50 +18,87 @@ export const inferVersionInfo = async function (rawFile, project, gameVersions)
} }
} }
// TODO: This func does not handle accurate semver parsing. We should eventually function getGameVersionsMatchingSemverRange(range, gameVersions) {
function gameVersionRange(gameVersionString, gameVersions) { if (!range) {
if (!gameVersionString) {
return [] return []
} }
const ranges = Array.isArray(range) ? range : [range]
return gameVersions.filter((version) => {
const semverVersion = version.split('.').length === 2 ? `${version}.0` : version // add patch version if missing (e.g. 1.16 -> 1.16.0)
return ranges.some((v) => satisfies(semverVersion, v))
})
}
// Truncate characters after `-` & `+` function getGameVersionsMatchingMavenRange(range, gameVersions) {
const gameString = gameVersionString.replace(/-|\+.*$/g, '') if (!range) {
return []
}
const ranges = []
let prefix = '' while (range.startsWith('[') || range.startsWith('(')) {
if (gameString.includes('~')) { let index = range.indexOf(')')
// Include minor versions const index2 = range.indexOf(']')
// ~1.2.3 -> 1.2 if (index === -1 || (index2 !== -1 && index2 < index)) {
prefix = gameString.replace('~', '').split('.').slice(0, 2).join('.') index = index2
} else if (gameString.includes('>=')) { }
// Include minor versions if (index === -1) break
// >=1.2.3 -> 1.2 ranges.push(range.substring(0, index + 1))
prefix = gameString.replace('>=', '').split('.').slice(0, 2).join('.') range = range.substring(index + 1).trim()
} else if (gameString.includes('^')) { if (range.startsWith(',')) {
// Include major versions range = range.substring(1).trim()
// ^1.2.3 -> 1 }
prefix = gameString.replace('^', '').split('.')[0]
} else if (gameString.includes('x')) {
// Include versions that match `x.x.x`
// 1.2.x -> 1.2
prefix = gameString.replace(/\.x$/, '')
} else {
// Include exact version
// 1.2.3 -> 1.2.3
prefix = gameString
} }
const simplified = gameVersions if (range) {
.filter((it) => it.version_type === 'release') ranges.push(range)
.map((it) => it.version) }
return simplified.filter((version) => version.startsWith(prefix))
const LESS_THAN_EQUAL = /^\(,(.*)]$/
const LESS_THAN = /^\(,(.*)\)$/
const EQUAL = /^\[(.*)]$/
const GREATER_THAN_EQUAL = /^\[(.*),\)$/
const GREATER_THAN = /^\((.*),\)$/
const BETWEEN = /^\((.*),(.*)\)$/
const BETWEEN_EQUAL = /^\[(.*),(.*)]$/
const BETWEEN_LESS_THAN_EQUAL = /^\((.*),(.*)]$/
const BETWEEN_GREATER_THAN_EQUAL = /^\[(.*),(.*)\)$/
const semverRanges = []
for (const range of ranges) {
let result
if ((result = range.match(LESS_THAN_EQUAL))) {
semverRanges.push(`<=${result[1]}`)
} else if ((result = range.match(LESS_THAN))) {
semverRanges.push(`<${result[1]}`)
} else if ((result = range.match(EQUAL))) {
semverRanges.push(`${result[1]}`)
} else if ((result = range.match(GREATER_THAN_EQUAL))) {
semverRanges.push(`>=${result[1]}`)
} else if ((result = range.match(GREATER_THAN))) {
semverRanges.push(`>${result[1]}`)
} else if ((result = range.match(BETWEEN))) {
semverRanges.push(`>${result[1]} <${result[2]}`)
} else if ((result = range.match(BETWEEN_EQUAL))) {
semverRanges.push(`>=${result[1]} <=${result[2]}`)
} else if ((result = range.match(BETWEEN_LESS_THAN_EQUAL))) {
semverRanges.push(`>${result[1]} <=${result[2]}`)
} else if ((result = range.match(BETWEEN_GREATER_THAN_EQUAL))) {
semverRanges.push(`>=${result[1]} <${result[2]}`)
}
}
return getGameVersionsMatchingSemverRange(semverRanges, gameVersions)
} }
const simplifiedGameVersions = gameVersions
.filter((it) => it.version_type === 'release')
.map((it) => it.version)
const inferFunctions = { const inferFunctions = {
// Forge 1.13+ // Forge 1.13+
'META-INF/mods.toml': async (file, zip) => { 'META-INF/mods.toml': async (file, zip) => {
const metadata = TOML.parse(file, { joiner: '\n' }) const metadata = TOML.parse(file, { joiner: '\n' })
// TODO: Parse minecraft version ranges
if (metadata.mods && metadata.mods.length > 0) { if (metadata.mods && metadata.mods.length > 0) {
let versionNum = metadata.mods[0].version let versionNum = metadata.mods[0].version
@@ -80,11 +118,23 @@ export const inferVersionInfo = async function (rawFile, project, gameVersions)
} }
} }
let gameVersions = []
const mcDependencies = Object.values(metadata.dependencies)
.flat()
.filter((dependency) => dependency.modId === 'minecraft')
if (mcDependencies.length > 0) {
gameVersions = getGameVersionsMatchingMavenRange(
mcDependencies[0].versionRange,
simplifiedGameVersions
)
}
return { return {
name: `${project.title} ${versionNum}`, name: `${project.title} ${versionNum}`,
version_number: versionNum, version_number: versionNum,
version_type: versionType(versionNum), version_type: versionType(versionNum),
loaders: ['forge'], loaders: ['forge'],
game_versions: gameVersions,
} }
} else { } else {
return {} return {}
@@ -99,9 +149,9 @@ export const inferVersionInfo = async function (rawFile, project, gameVersions)
version_number: metadata.version, version_number: metadata.version,
version_type: versionType(metadata.version), version_type: versionType(metadata.version),
loaders: ['forge'], loaders: ['forge'],
game_versions: gameVersions game_versions: simplifiedGameVersions.filter((version) =>
.filter((x) => x.version.startsWith(metadata.mcversion) && x.version_type === 'release') version.startsWith(metadata.mcversion)
.map((x) => x.version), ),
} }
}, },
// Fabric // Fabric
@@ -114,7 +164,7 @@ export const inferVersionInfo = async function (rawFile, project, gameVersions)
loaders: ['fabric'], loaders: ['fabric'],
version_type: versionType(metadata.version), version_type: versionType(metadata.version),
game_versions: metadata.depends game_versions: metadata.depends
? gameVersionRange(metadata.depends.minecraft, gameVersions) ? getGameVersionsMatchingSemverRange(metadata.depends.minecraft, simplifiedGameVersions)
: [], : [],
} }
}, },
@@ -128,11 +178,11 @@ export const inferVersionInfo = async function (rawFile, project, gameVersions)
loaders: ['quilt'], loaders: ['quilt'],
version_type: versionType(metadata.quilt_loader.version), version_type: versionType(metadata.quilt_loader.version),
game_versions: metadata.quilt_loader.depends game_versions: metadata.quilt_loader.depends
? gameVersionRange( ? getGameVersionsMatchingSemverRange(
metadata.quilt_loader.depends.find((x) => x.id === 'minecraft') metadata.quilt_loader.depends.find((x) => x.id === 'minecraft')
? metadata.quilt_loader.depends.find((x) => x.id === 'minecraft').versions ? metadata.quilt_loader.depends.find((x) => x.id === 'minecraft').versions
: [], : [],
gameVersions simplifiedGameVersions
) )
: [], : [],
} }

View File

@@ -46,6 +46,7 @@
"markdown-it": "^13.0.1", "markdown-it": "^13.0.1",
"omorphia": "^0.4.31", "omorphia": "^0.4.31",
"qrcode.vue": "^3.4.0", "qrcode.vue": "^3.4.0",
"semver": "^7.5.4",
"vue-multiselect": "^3.0.0-alpha.2", "vue-multiselect": "^3.0.0-alpha.2",
"xss": "^1.0.14" "xss": "^1.0.14"
}, },

40
pnpm-lock.yaml generated
View File

@@ -37,6 +37,9 @@ dependencies:
qrcode.vue: qrcode.vue:
specifier: ^3.4.0 specifier: ^3.4.0
version: 3.4.0(vue@3.3.4) version: 3.4.0(vue@3.3.4)
semver:
specifier: ^7.5.4
version: 7.5.4
vue-multiselect: vue-multiselect:
specifier: ^3.0.0-alpha.2 specifier: ^3.0.0-alpha.2
version: 3.0.0-alpha.2 version: 3.0.0-alpha.2
@@ -1102,7 +1105,7 @@ packages:
nopt: 5.0.0 nopt: 5.0.0
npmlog: 5.0.1 npmlog: 5.0.1
rimraf: 3.0.2 rimraf: 3.0.2
semver: 7.5.3 semver: 7.5.4
tar: 6.1.15 tar: 6.1.15
transitivePeerDependencies: transitivePeerDependencies:
- encoding - encoding
@@ -1158,7 +1161,7 @@ packages:
pathe: 1.1.1 pathe: 1.1.1
pkg-types: 1.0.3 pkg-types: 1.0.3
scule: 1.0.0 scule: 1.0.0
semver: 7.5.3 semver: 7.5.4
unctx: 2.3.1 unctx: 2.3.1
unimport: 3.0.12(rollup@3.26.0) unimport: 3.0.12(rollup@3.26.0)
untyped: 1.3.2 untyped: 1.3.2
@@ -1184,7 +1187,7 @@ packages:
pathe: 1.1.1 pathe: 1.1.1
pkg-types: 1.0.3 pkg-types: 1.0.3
scule: 1.0.0 scule: 1.0.0
semver: 7.5.3 semver: 7.5.4
unctx: 2.3.1 unctx: 2.3.1
unimport: 3.0.12(rollup@3.26.0) unimport: 3.0.12(rollup@3.26.0)
untyped: 1.3.2 untyped: 1.3.2
@@ -1619,7 +1622,7 @@ packages:
grapheme-splitter: 1.0.4 grapheme-splitter: 1.0.4
ignore: 5.2.4 ignore: 5.2.4
natural-compare-lite: 1.4.0 natural-compare-lite: 1.4.0
semver: 7.5.3 semver: 7.5.4
tsutils: 3.21.0(typescript@5.0.4) tsutils: 3.21.0(typescript@5.0.4)
typescript: 5.0.4 typescript: 5.0.4
transitivePeerDependencies: transitivePeerDependencies:
@@ -1693,7 +1696,7 @@ packages:
debug: 4.3.4 debug: 4.3.4
globby: 11.1.0 globby: 11.1.0
is-glob: 4.0.3 is-glob: 4.0.3
semver: 7.5.3 semver: 7.5.4
tsutils: 3.21.0(typescript@5.0.4) tsutils: 3.21.0(typescript@5.0.4)
typescript: 5.0.4 typescript: 5.0.4
transitivePeerDependencies: transitivePeerDependencies:
@@ -1714,7 +1717,7 @@ packages:
'@typescript-eslint/typescript-estree': 5.59.8(typescript@5.0.4) '@typescript-eslint/typescript-estree': 5.59.8(typescript@5.0.4)
eslint: 8.41.0 eslint: 8.41.0
eslint-scope: 5.1.1 eslint-scope: 5.1.1
semver: 7.5.3 semver: 7.5.4
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
- typescript - typescript
@@ -2401,7 +2404,7 @@ packages:
/builtins@5.0.1: /builtins@5.0.1:
resolution: {integrity: sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==} resolution: {integrity: sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==}
dependencies: dependencies:
semver: 7.5.3 semver: 7.5.4
dev: true dev: true
/bundle-name@3.0.0: /bundle-name@3.0.0:
@@ -3401,7 +3404,7 @@ packages:
is-core-module: 2.12.1 is-core-module: 2.12.1
minimatch: 3.1.2 minimatch: 3.1.2
resolve: 1.22.2 resolve: 1.22.2
semver: 7.5.3 semver: 7.5.4
dev: true dev: true
/eslint-plugin-node@11.1.0(eslint@8.41.0): /eslint-plugin-node@11.1.0(eslint@8.41.0):
@@ -3464,7 +3467,7 @@ packages:
read-pkg-up: 7.0.1 read-pkg-up: 7.0.1
regexp-tree: 0.1.27 regexp-tree: 0.1.27
safe-regex: 2.1.1 safe-regex: 2.1.1
semver: 7.5.3 semver: 7.5.4
strip-indent: 3.0.0 strip-indent: 3.0.0
dev: true dev: true
@@ -3479,7 +3482,7 @@ packages:
natural-compare: 1.4.0 natural-compare: 1.4.0
nth-check: 2.1.1 nth-check: 2.1.1
postcss-selector-parser: 6.0.13 postcss-selector-parser: 6.0.13
semver: 7.5.3 semver: 7.5.4
vue-eslint-parser: 9.3.1(eslint@8.41.0) vue-eslint-parser: 9.3.1(eslint@8.41.0)
xml-name-validator: 4.0.0 xml-name-validator: 4.0.0
transitivePeerDependencies: transitivePeerDependencies:
@@ -4847,7 +4850,6 @@ packages:
engines: {node: '>=10'} engines: {node: '>=10'}
dependencies: dependencies:
yallist: 4.0.0 yallist: 4.0.0
dev: true
/magic-string-ast@0.1.2: /magic-string-ast@0.1.2:
resolution: {integrity: sha512-P53AZrzq7hclCU6HWj88xNZHmP15DKjMmK/vBytO1qnpYP3ul4IEZlyCE0aU3JRnmgWmZPmoTKj4Bls7v0pMyA==} resolution: {integrity: sha512-P53AZrzq7hclCU6HWj88xNZHmP15DKjMmK/vBytO1qnpYP3ul4IEZlyCE0aU3JRnmgWmZPmoTKj4Bls7v0pMyA==}
@@ -5132,7 +5134,7 @@ packages:
rollup: 3.26.0 rollup: 3.26.0
rollup-plugin-visualizer: 5.9.2(rollup@3.26.0) rollup-plugin-visualizer: 5.9.2(rollup@3.26.0)
scule: 1.0.0 scule: 1.0.0
semver: 7.5.3 semver: 7.5.4
serve-placeholder: 2.0.1 serve-placeholder: 2.0.1
serve-static: 1.15.0 serve-static: 1.15.0
source-map-support: 0.5.21 source-map-support: 0.5.21
@@ -6323,13 +6325,12 @@ packages:
hasBin: true hasBin: true
dev: true dev: true
/semver@7.5.3: /semver@7.5.4:
resolution: {integrity: sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==} resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==}
engines: {node: '>=10'} engines: {node: '>=10'}
hasBin: true hasBin: true
dependencies: dependencies:
lru-cache: 6.0.0 lru-cache: 6.0.0
dev: true
/send@0.18.0: /send@0.18.0:
resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==}
@@ -7099,7 +7100,7 @@ packages:
lodash.debounce: 4.0.8 lodash.debounce: 4.0.8
lodash.pick: 4.4.0 lodash.pick: 4.4.0
npm-run-path: 4.0.1 npm-run-path: 4.0.1
semver: 7.5.3 semver: 7.5.4
strip-ansi: 6.0.1 strip-ansi: 6.0.1
tiny-invariant: 1.3.1 tiny-invariant: 1.3.1
typescript: 5.0.4 typescript: 5.0.4
@@ -7175,7 +7176,7 @@ packages:
engines: {vscode: ^1.52.0} engines: {vscode: ^1.52.0}
dependencies: dependencies:
minimatch: 3.1.2 minimatch: 3.1.2
semver: 7.5.3 semver: 7.5.4
vscode-languageserver-protocol: 3.16.0 vscode-languageserver-protocol: 3.16.0
dev: true dev: true
@@ -7228,7 +7229,7 @@ packages:
espree: 9.6.0 espree: 9.6.0
esquery: 1.5.0 esquery: 1.5.0
lodash: 4.17.21 lodash: 4.17.21
semver: 7.5.3 semver: 7.5.4
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
dev: true dev: true
@@ -7277,7 +7278,7 @@ packages:
dependencies: dependencies:
'@volar/vue-language-core': 1.6.5 '@volar/vue-language-core': 1.6.5
'@volar/vue-typescript': 1.6.5(typescript@5.0.4) '@volar/vue-typescript': 1.6.5(typescript@5.0.4)
semver: 7.5.3 semver: 7.5.4
typescript: 5.0.4 typescript: 5.0.4
dev: true dev: true
@@ -7419,7 +7420,6 @@ packages:
/yallist@4.0.0: /yallist@4.0.0:
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
dev: true
/yaml@2.3.1: /yaml@2.3.1:
resolution: {integrity: sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==} resolution: {integrity: sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==}