From 3f66759eaff50507c7a37fa8b39112fecce24d88 Mon Sep 17 00:00:00 2001 From: psilospore Date: Tue, 21 Dec 2021 15:33:10 -0500 Subject: [PATCH 01/14] feat: Initial attempt at PVP implementation Co-authored-by: JasonMFry --- lib/versioning/pvp/index.ts | 165 ++++++++++++++++++++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 lib/versioning/pvp/index.ts diff --git a/lib/versioning/pvp/index.ts b/lib/versioning/pvp/index.ts new file mode 100644 index 00000000000000..c95c385f39c42c --- /dev/null +++ b/lib/versioning/pvp/index.ts @@ -0,0 +1,165 @@ +import { VersioningApi } from '../types'; + +export const id = 'pvp'; +export const displayName = 'PVP'; +export const urls = ['https://pvp.haskell.org']; +export const supportsRanges = true; + +// https://pvp.haskell.org/#version-numbers +type PVP = { + A: number; // Major + B: number; // Major + C: number; // Minor + additional: number[]; // First element is patch. Can be empty (no patch version) +}; + +const hasLeadingZero = (str: string): boolean => + str.length > 1 && str.startsWith('0'); + +const anyElementHasLeadingZero = (strings: string[]): boolean => { + const bools = strings.map((str) => hasLeadingZero(str)); + return bools.includes(true); +}; + +const throwInvalidVersionError = (version: string): void => { + throw new Error( + `${version} is not a valid version. See https://pvp.haskell.org.` + ); +}; + +const parse = (version: string): PVP => { + const [a, b, c, ...additional] = version.split('.'); + if (anyElementHasLeadingZero([a, b, c, additional].flat())) { + throwInvalidVersionError(version); + } + const a_ = parseInt(a); + const b_ = parseInt(b); + const c_ = parseInt(c); + if (isNaN(a_) || isNaN(b_) || isNaN(c_)) { + throwInvalidVersionError(version); + } + const additional_: number[] = []; + additional.forEach((ele) => { + const ele_ = parseInt(ele); + if (isNaN(ele_)) { + throwInvalidVersionError(version); + } else { + additional_.push(ele_); + } + }); + return { A: a_, B: b_, C: c_, additional: additional_ }; +}; + +/** + * Represents major version as a float + * A.B + * B is optional so if it is not included it defaults to A.0 + */ +const getMajor = (version: string): number => { + const { A, B } = parse(version); + return parseFloat(`${A}.${B}`); +}; + +const getMinor = (version: string): number => { + const { C } = parse(version); + return C; +}; + +// Patch version is optional +const getPatch = (version: string): number | null => { + const { additional } = parse(version); + return additional[0]; +}; + +const isValid = (version: string): boolean => { + try { + parse(version); + } catch { + return false; + } + return true; +}; + +/** + * PVP does not specify a stable version + * https://pvp.haskell.org/faq/#how-does-the-pvp-relate-to-semantic-versioning-semver + */ +const isStable = (): boolean => true; + +const isVersion = (version: string): string | boolean | null => + !!isValid(version); + +// credit: https://stackoverflow.com/a/22015930/12963115 +const zip = (a: number[], b: number[]): [number?, number?][] => + Array.from(Array(Math.max(b.length, a.length)), (_, i) => [a[i], b[i]]); + +/** + * Compare 2 versions + * Note if the last component (D) is not defined it is considered less than 0 + * e.g. 2.0.1.0 > 2.0.1 + */ +const compare = (version: string, other: string): number => { + const versionA = parse(version); + const versionB = parse(other); + + const versionAL = [ + versionA.A, + versionA.B, + versionA.C, + ...versionA.additional, + ]; + const versionBL = [ + versionB.A, + versionB.B, + versionB.C, + ...versionB.additional, + ]; + const zippedVersions = zip(versionAL, versionBL); + + for (const currentComponents of zippedVersions) { + const [component, otherComponent] = currentComponents; + if (component > otherComponent || otherComponent === undefined) { + return 1; + } else if (component < otherComponent || component === undefined) { + return -1; + } + } + + return 0; +}; + +const equals = (a: string, b: string): boolean => compare(a, b) === 0; +const isGreaterThan = (a: string, b: string): boolean => compare(a, b) === 1; + +/** + * Verifies the version is a single version but since we don't handle non single versions + * so if it fails to parse then it's not a single version. + */ +const isSingleVersion = (version: string): string | boolean | null => { + try { + parse(version); + return true; + } catch { + return false; + } +}; + +export const api: VersioningApi = { + equals, + getMajor, + getMinor, + getPatch, + isCompatible: isVersion, + isGreaterThan, + isSingleVersion, + isStable, + isValid, + isVersion, + sortVersions: compare, + // TODO Range related functions not sure how to implement these if we won't be handling ranges + getNewValue, + isLessThanRange, + matches, + getSatisfyingVersion, + minSatisfyingVersion, +}; From ee79cf7975a8cdda4952e38dc4d7d5676e350ec2 Mon Sep 17 00:00:00 2001 From: psilospore Date: Tue, 21 Dec 2021 16:57:30 -0500 Subject: [PATCH 02/14] Initial sub at test Co-authored-by: JasonMFry --- lib/versioning/pvp/index.spec.ts | 103 +++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 lib/versioning/pvp/index.spec.ts diff --git a/lib/versioning/pvp/index.spec.ts b/lib/versioning/pvp/index.spec.ts new file mode 100644 index 00000000000000..c57e124d8fc94b --- /dev/null +++ b/lib/versioning/pvp/index.spec.ts @@ -0,0 +1,103 @@ +import { api as pvp } from '.'; + +describe('versioning/pvp/index', () => { + test.each` + version | isValid + ${'0.1.0'} | ${true} + ${'1.0.0'} | ${true} + ${'0.0.1'} | ${false} + ${'1.1.0.1.5.123455'} | ${true} + ${'1.2.3'} | ${true} + ${'1.04'} | ${false} + ${'1.2.3-foo'} | ${false} + ${'1.2.3foo'} | ${false} + ${'~1.2.3'} | ${false} + ${'^1.2.3'} | ${false} + ${'>1.2.3'} | ${false} + ${'one.two.three'} | ${false} + ${'renovatebot/renovate'} | ${false} + ${'renovatebot/renovate#main'} | ${false} + ${'https://github.com/renovatebot/renovate.git'} | ${false} + `('isValid("$version") === $isValid', ({ version, isValid }) => { + const res = !!pvp.isValid(version); + expect(res).toBe(isValid); + }); + + test.each` + version | isSingle + ${'1.2.3'} | ${true} + ${'1.2.3-alpha.1'} | ${true} + ${'=1.2.3'} | ${true} + ${'= 1.2.3'} | ${true} + ${'1.x'} | ${false} + `('isSingleVersion("$version") === $isSingle', ({ version, isSingle }) => { + const res = !!pvp.isSingleVersion(version); + expect(res).toBe(isSingle); + }); + + test.each` + currentValue | rangeStrategy | currentVersion | newVersion | expected + ${'=1.0.0'} | ${'bump'} | ${'1.0.0'} | ${'1.1.0'} | ${'=1.1.0'} + ${'^1.0'} | ${'bump'} | ${'1.0.0'} | ${'1.0.7'} | ${'^1.0'} + ${'^1'} | ${'bump'} | ${'1.0.0'} | ${'1.0.7-prerelease.1'} | ${'^1.0.7-prerelease.1'} + ${'~> 1.0.0'} | ${'replace'} | ${'1.0.0'} | ${'1.1.7'} | ${'~> 1.1.0'} + ${'^1.0'} | ${'bump'} | ${'1.0.0'} | ${'1.1.7'} | ${'^1.1'} + ${'~1.0'} | ${'bump'} | ${'1.0.0'} | ${'1.1.7'} | ${'~1.1'} + ${'~1.0'} | ${'bump'} | ${'1.0.0'} | ${'1.0.7-prerelease.1'} | ${'~1.0.7-prerelease.1'} + ${'^1'} | ${'bump'} | ${'1.0.0'} | ${'2.1.7'} | ${'^2'} + ${'~1'} | ${'bump'} | ${'1.0.0'} | ${'1.1.7'} | ${'~1'} + ${'5'} | ${'bump'} | ${'5.0.0'} | ${'5.1.7'} | ${'5'} + ${'5'} | ${'bump'} | ${'5.0.0'} | ${'6.1.7'} | ${'6'} + ${'5.0'} | ${'bump'} | ${'5.0.0'} | ${'5.0.7'} | ${'5.0'} + ${'5.0'} | ${'bump'} | ${'5.0.0'} | ${'5.1.7'} | ${'5.1'} + ${'5.0'} | ${'bump'} | ${'5.0.0'} | ${'6.1.7'} | ${'6.1'} + ${'>=1.0.0'} | ${'bump'} | ${'1.0.0'} | ${'1.1.0'} | ${'>=1.1.0'} + ${'>= 1.0.0'} | ${'bump'} | ${'1.0.0'} | ${'1.1.0'} | ${'>= 1.1.0'} + ${'=1.0.0'} | ${'replace'} | ${'1.0.0'} | ${'1.1.0'} | ${'=1.1.0'} + ${'1.0.*'} | ${'replace'} | ${'1.0.0'} | ${'1.1.0'} | ${'1.1.*'} + ${'1.*'} | ${'replace'} | ${'1.0.0'} | ${'2.1.0'} | ${'2.*'} + ${'~0.6.1'} | ${'replace'} | ${'0.6.8'} | ${'0.7.0-rc.2'} | ${'~0.7.0-rc'} + ${'>= 0.1.21 < 0.2.0'} | ${'bump'} | ${'0.1.21'} | ${'0.1.24'} | ${'>= 0.1.24 < 0.2.0'} + ${'>= 0.1.21 <= 0.2.0'} | ${'bump'} | ${'0.1.21'} | ${'0.1.24'} | ${'>= 0.1.24 <= 0.2.0'} + ${'>= 0.0.1 <= 0.1'} | ${'bump'} | ${'0.0.1'} | ${'0.0.2'} | ${'>= 0.0.2 <= 0.1'} + ${'>= 0.0.1 < 0.1'} | ${'bump'} | ${'0.1.0'} | ${'0.2.1'} | ${'>= 0.2.1 < 0.3'} + ${'>= 0.0.1 < 0.0.4'} | ${'bump'} | ${'0.0.4'} | ${'0.0.5'} | ${'>= 0.0.5 < 0.0.6'} + ${'>= 0.0.1 < 1'} | ${'bump'} | ${'1.0.0'} | ${'1.0.1'} | ${'>= 1.0.1 < 2'} + ${'>= 0.0.1 < 1'} | ${'bump'} | ${'1.0.0'} | ${'1.0.1'} | ${'>= 1.0.1 < 2'} + ${'<=1.2.3'} | ${'widen'} | ${'1.0.0'} | ${'1.2.3'} | ${'<=1.2.3'} + ${'<=1.2.3'} | ${'widen'} | ${'1.0.0'} | ${'1.2.4'} | ${'<=1.2.4'} + ${'>=1.2.3'} | ${'widen'} | ${'1.0.0'} | ${'1.2.3'} | ${'>=1.2.3'} + ${'>=1.2.3'} | ${'widen'} | ${'1.0.0'} | ${'1.2.1'} | ${'>=1.2.3 || 1.2.1'} + ${'^0.0.3'} | ${'replace'} | ${'0.0.3'} | ${'0.0.6'} | ${'^0.0.6'} + ${'^0.0.3'} | ${'replace'} | ${'0.0.3'} | ${'0.5.0'} | ${'^0.5.0'} + ${'^0.0.3'} | ${'replace'} | ${'0.0.3'} | ${'0.5.6'} | ${'^0.5.0'} + ${'^0.0.3'} | ${'replace'} | ${'0.0.3'} | ${'4.0.0'} | ${'^4.0.0'} + ${'^0.0.3'} | ${'replace'} | ${'0.0.3'} | ${'4.0.6'} | ${'^4.0.0'} + ${'^0.0.3'} | ${'replace'} | ${'0.0.3'} | ${'4.5.6'} | ${'^4.0.0'} + ${'^0.2.0'} | ${'replace'} | ${'0.2.0'} | ${'0.5.6'} | ${'^0.5.0'} + ${'^0.2.3'} | ${'replace'} | ${'0.2.3'} | ${'0.5.0'} | ${'^0.5.0'} + ${'^0.2.3'} | ${'replace'} | ${'0.2.3'} | ${'0.5.6'} | ${'^0.5.0'} + ${'^1.2.3'} | ${'replace'} | ${'1.2.3'} | ${'4.0.0'} | ${'^4.0.0'} + ${'^1.2.3'} | ${'replace'} | ${'1.2.3'} | ${'4.5.6'} | ${'^4.0.0'} + ${'^1.0.0'} | ${'replace'} | ${'1.0.0'} | ${'4.5.6'} | ${'^4.0.0'} + ${'^0.2.3'} | ${'replace'} | ${'0.2.3'} | ${'0.2.4'} | ${'^0.2.3'} + ${'^2.3.0'} | ${'replace'} | ${'2.3.0'} | ${'2.4.0'} | ${'^2.3.0'} + ${'^2.3.4'} | ${'replace'} | ${'2.3.4'} | ${'2.4.5'} | ${'^2.3.4'} + ${'^0.0.1'} | ${'replace'} | ${'0.0.1'} | ${'0.0.2'} | ${'^0.0.2'} + ${'^1.0.1'} | ${'replace'} | ${'1.0.1'} | ${'2.0.2'} | ${'^2.0.0'} + ${'^1.2.3'} | ${'replace'} | ${'1.2.3'} | ${'1.2.3'} | ${'^1.2.3'} + ${'^1.2.3'} | ${'replace'} | ${'1.2.3'} | ${'1.2.2'} | ${'^1.2.2'} + ${'^0.9.21'} | ${'replace'} | ${'0.9.21'} | ${'0.9.22'} | ${'^0.9.21'} + `( + 'getNewValue("$currentValue", "$rangeStrategy", "$currentVersion", "$newVersion") === "$expected"', + ({ currentValue, rangeStrategy, currentVersion, newVersion, expected }) => { + const res = pvp.getNewValue({ + currentValue, + rangeStrategy, + currentVersion, + newVersion, + }); + expect(res).toEqual(expected); + } + ); +}); From 50f5f46840871d5bb5af36b75b00a891ae9347a4 Mon Sep 17 00:00:00 2001 From: psilospore Date: Sun, 16 Jan 2022 23:40:18 -0500 Subject: [PATCH 03/14] Refactored to use GenericVersioningApi --- lib/versioning/api.ts | 2 + lib/versioning/pvp/index.spec.ts | 2 +- lib/versioning/pvp/index.ts | 229 ++++++++++++------------------- 3 files changed, 91 insertions(+), 142 deletions(-) diff --git a/lib/versioning/api.ts b/lib/versioning/api.ts index 932441370655fa..b687087207ed06 100644 --- a/lib/versioning/api.ts +++ b/lib/versioning/api.ts @@ -15,6 +15,7 @@ import * as npm from './npm'; import * as nuget from './nuget'; import * as pep440 from './pep440'; import * as poetry from './poetry'; +import * as pvp from './pvp'; import * as regex from './regex'; import * as rez from './rez'; import * as ruby from './ruby'; @@ -44,6 +45,7 @@ api.set('npm', npm.api); api.set('nuget', nuget.api); api.set('pep440', pep440.api); api.set('poetry', poetry.api); +api.set('pvp', pvp.api); api.set('regex', regex.api); api.set('rez', rez.api); api.set('ruby', ruby.api); diff --git a/lib/versioning/pvp/index.spec.ts b/lib/versioning/pvp/index.spec.ts index c57e124d8fc94b..adbe40d293386c 100644 --- a/lib/versioning/pvp/index.spec.ts +++ b/lib/versioning/pvp/index.spec.ts @@ -5,7 +5,7 @@ describe('versioning/pvp/index', () => { version | isValid ${'0.1.0'} | ${true} ${'1.0.0'} | ${true} - ${'0.0.1'} | ${false} + ${'0.0.1'} | ${true} ${'1.1.0.1.5.123455'} | ${true} ${'1.2.3'} | ${true} ${'1.04'} | ${false} diff --git a/lib/versioning/pvp/index.ts b/lib/versioning/pvp/index.ts index c95c385f39c42c..638286837d7e0f 100644 --- a/lib/versioning/pvp/index.ts +++ b/lib/versioning/pvp/index.ts @@ -1,165 +1,112 @@ +import { regEx } from '../../util/regex'; +import { GenericVersion, GenericVersioningApi } from '../loose/generic'; import { VersioningApi } from '../types'; export const id = 'pvp'; export const displayName = 'PVP'; export const urls = ['https://pvp.haskell.org']; -export const supportsRanges = true; - -// https://pvp.haskell.org/#version-numbers -type PVP = { - A: number; // Major - B: number; // Major - C: number; // Minor - additional: number[]; // First element is patch. Can be empty (no patch version) -}; - -const hasLeadingZero = (str: string): boolean => - str.length > 1 && str.startsWith('0'); - -const anyElementHasLeadingZero = (strings: string[]): boolean => { - const bools = strings.map((str) => hasLeadingZero(str)); - return bools.includes(true); -}; - -const throwInvalidVersionError = (version: string): void => { - throw new Error( - `${version} is not a valid version. See https://pvp.haskell.org.` - ); -}; - -const parse = (version: string): PVP => { - const [a, b, c, ...additional] = version.split('.'); - if (anyElementHasLeadingZero([a, b, c, additional].flat())) { - throwInvalidVersionError(version); - } - const a_ = parseInt(a); - const b_ = parseInt(b); - const c_ = parseInt(c); - if (isNaN(a_) || isNaN(b_) || isNaN(c_)) { - throwInvalidVersionError(version); - } - const additional_: number[] = []; - additional.forEach((ele) => { - const ele_ = parseInt(ele); - if (isNaN(ele_)) { - throwInvalidVersionError(version); - } else { - additional_.push(ele_); - } - }); - return { A: a_, B: b_, C: c_, additional: additional_ }; -}; +export const supportsRanges = false; /** - * Represents major version as a float - * A.B - * B is optional so if it is not included it defaults to A.0 + * At least 3 components and no leading 0s */ -const getMajor = (version: string): number => { - const { A, B } = parse(version); - return parseFloat(`${A}.${B}`); -}; +const regex = regEx(/^(([0-9])|([1-9][0-9]*)\.){2,}([0-9])|([1-9][0-9]]*)$/); +class PvpVersioningApi extends GenericVersioningApi { + /** + * https://pvp.haskell.org/#version-numbers + */ + protected _parse(version: string): GenericVersion { + const [a, b, c, ...rest] = version.split('.'); + const a_ = parseInt(a); + const b_ = parseInt(b); + const c_ = parseInt(c); + console.log(c_); + const additional = rest ?? []; + + if (isNaN(a) || isNaN(b_) || isNaN(c_)) { + return null; + } -const getMinor = (version: string): number => { - const { C } = parse(version); - return C; -}; + const additional_: number[] = []; + for (const ele of additional) { + const ele_ = parseInt(ele); + if (isNaN(ele_)) { + //throwInvalidVersionError(version); + return null; // TODO exit loop + } else { + additional_.push(ele_); + } + } -// Patch version is optional -const getPatch = (version: string): number | null => { - const { additional } = parse(version); - return additional[0]; -}; + if (anyElementHasLeadingZero([a, b, c, ...additional].flat())) { + //throwInvalidVersionError(version); + return null; + } -const isValid = (version: string): boolean => { - try { - parse(version); - } catch { - return false; + return { + release: [a_, b_, c_, ...additional_], + }; } - return true; -}; -/** - * PVP does not specify a stable version - * https://pvp.haskell.org/faq/#how-does-the-pvp-relate-to-semantic-versioning-semver - */ -const isStable = (): boolean => true; + // PVP has two major components A.B + // To keep compatability with Renovate's versioning API we will treat it as a float + override getMajor(version: string): number | null { + const { release } = this._parse(version); + return parseFloat(`${release[0]}.${release[1]}`); + } -const isVersion = (version: string): string | boolean | null => - !!isValid(version); + override getMinor(version: string): number | null { + const { release } = this._parse(version); + return release[2]; + } -// credit: https://stackoverflow.com/a/22015930/12963115 -const zip = (a: number[], b: number[]): [number?, number?][] => - Array.from(Array(Math.max(b.length, a.length)), (_, i) => [a[i], b[i]]); + override getPatch(version: string): number | null { + const { release } = this._parse(version); + return release[3]; + } -/** - * Compare 2 versions - * Note if the last component (D) is not defined it is considered less than 0 - * e.g. 2.0.1.0 > 2.0.1 - */ -const compare = (version: string, other: string): number => { - const versionA = parse(version); - const versionB = parse(other); - - const versionAL = [ - versionA.A, - versionA.B, - versionA.C, - ...versionA.additional, - ]; - const versionBL = [ - versionB.A, - versionB.B, - versionB.C, - ...versionB.additional, - ]; - const zippedVersions = zip(versionAL, versionBL); - - for (const currentComponents of zippedVersions) { - const [component, otherComponent] = currentComponents; - if (component > otherComponent || otherComponent === undefined) { + /** + * Compare similar to GenericVersioningApi._compare implementation + * except 2.1.1.0 and 2.1.1 are not equivilant instead 2.1.1.0 > 2.1.1 + */ + override _compare(version: string, other: string): number { + const left = this._parse(version); + const right = this._parse(other); + + // istanbul ignore if + if (!(left && right)) { return 1; - } else if (component < otherComponent || component === undefined) { - return -1; } - } - - return 0; -}; -const equals = (a: string, b: string): boolean => compare(a, b) === 0; -const isGreaterThan = (a: string, b: string): boolean => compare(a, b) === 1; + // support variable length compare + const length = Math.max(left.release.length, right.release.length); + for (let i = 0; i < length; i += 1) { + // 2.1.0 > 2.1 + const part1 = left.release[i] ?? -1; + const part2 = right.release[i] ?? -1; + if (part1 !== part2) { + return part1 - part2; + } + } -/** - * Verifies the version is a single version but since we don't handle non single versions - * so if it fails to parse then it's not a single version. - */ -const isSingleVersion = (version: string): string | boolean | null => { - try { - parse(version); - return true; - } catch { - return false; + return 0; } +} + +const hasLeadingZero = (str: string): boolean => + str.length > 1 && str.startsWith('0'); + +const anyElementHasLeadingZero = (strings: string[]): boolean => { + const bools = strings.map((str) => hasLeadingZero(str)); + return bools.includes(true); }; -export const api: VersioningApi = { - equals, - getMajor, - getMinor, - getPatch, - isCompatible: isVersion, - isGreaterThan, - isSingleVersion, - isStable, - isValid, - isVersion, - sortVersions: compare, - // TODO Range related functions not sure how to implement these if we won't be handling ranges - getNewValue, - isLessThanRange, - matches, - getSatisfyingVersion, - minSatisfyingVersion, +const throwInvalidVersionError = (version: string): void => { + throw new Error( + `${version} is not a valid version. See https://pvp.haskell.org.` + ); }; + +export const api: VersioningApi = new PvpVersioningApi(); + +export default api; From ca70458169f04c3cd06794cc52d8931291ed86bb Mon Sep 17 00:00:00 2001 From: psilospore Date: Wed, 19 Jan 2022 20:23:52 -0500 Subject: [PATCH 04/14] Update pvp to use regex --- lib/versioning/pvp/index.ts | 58 +++++++++---------------------------- 1 file changed, 13 insertions(+), 45 deletions(-) diff --git a/lib/versioning/pvp/index.ts b/lib/versioning/pvp/index.ts index 638286837d7e0f..eb4e654300b016 100644 --- a/lib/versioning/pvp/index.ts +++ b/lib/versioning/pvp/index.ts @@ -9,47 +9,29 @@ export const supportsRanges = false; /** * At least 3 components and no leading 0s + * https://pvp.haskell.org/#version-number */ -const regex = regEx(/^(([0-9])|([1-9][0-9]*)\.){2,}([0-9])|([1-9][0-9]]*)$/); +const versionPattern = regEx( + /^((([0-9])|([1-9][0-9]*))\.){2,}(([0-9])|([1-9][0-9]*))$/ +); + class PvpVersioningApi extends GenericVersioningApi { /** - * https://pvp.haskell.org/#version-numbers + * PVP has two major components A.B + * To keep compatability with Renovate's versioning API we will treat it as a float */ protected _parse(version: string): GenericVersion { - const [a, b, c, ...rest] = version.split('.'); - const a_ = parseInt(a); - const b_ = parseInt(b); - const c_ = parseInt(c); - console.log(c_); - const additional = rest ?? []; - - if (isNaN(a) || isNaN(b_) || isNaN(c_)) { + const matches = versionPattern.exec(version); + if (!matches) { return null; } - const additional_: number[] = []; - for (const ele of additional) { - const ele_ = parseInt(ele); - if (isNaN(ele_)) { - //throwInvalidVersionError(version); - return null; // TODO exit loop - } else { - additional_.push(ele_); - } - } - - if (anyElementHasLeadingZero([a, b, c, ...additional].flat())) { - //throwInvalidVersionError(version); - return null; - } - - return { - release: [a_, b_, c_, ...additional_], - }; + const components = version.split('.'); + const major = parseFloat(`${components[0]}.${components[1]}`); + const rest = components.slice(2).map((i) => parseInt(i)); + return { release: [major, ...rest] }; } - // PVP has two major components A.B - // To keep compatability with Renovate's versioning API we will treat it as a float override getMajor(version: string): number | null { const { release } = this._parse(version); return parseFloat(`${release[0]}.${release[1]}`); @@ -93,20 +75,6 @@ class PvpVersioningApi extends GenericVersioningApi { } } -const hasLeadingZero = (str: string): boolean => - str.length > 1 && str.startsWith('0'); - -const anyElementHasLeadingZero = (strings: string[]): boolean => { - const bools = strings.map((str) => hasLeadingZero(str)); - return bools.includes(true); -}; - -const throwInvalidVersionError = (version: string): void => { - throw new Error( - `${version} is not a valid version. See https://pvp.haskell.org.` - ); -}; - export const api: VersioningApi = new PvpVersioningApi(); export default api; From f5870617f555b26bd30bad5fd1202130e1bcb82d Mon Sep 17 00:00:00 2001 From: psilospore Date: Wed, 19 Jan 2022 20:24:06 -0500 Subject: [PATCH 05/14] Update test case --- lib/versioning/pvp/index.spec.ts | 116 ++++++++++++------------------- 1 file changed, 43 insertions(+), 73 deletions(-) diff --git a/lib/versioning/pvp/index.spec.ts b/lib/versioning/pvp/index.spec.ts index adbe40d293386c..000a9977d37b28 100644 --- a/lib/versioning/pvp/index.spec.ts +++ b/lib/versioning/pvp/index.spec.ts @@ -8,6 +8,7 @@ describe('versioning/pvp/index', () => { ${'0.0.1'} | ${true} ${'1.1.0.1.5.123455'} | ${true} ${'1.2.3'} | ${true} + ${'1.02.3'} | ${false} ${'1.04'} | ${false} ${'1.2.3-foo'} | ${false} ${'1.2.3foo'} | ${false} @@ -24,80 +25,49 @@ describe('versioning/pvp/index', () => { }); test.each` - version | isSingle - ${'1.2.3'} | ${true} - ${'1.2.3-alpha.1'} | ${true} - ${'=1.2.3'} | ${true} - ${'= 1.2.3'} | ${true} - ${'1.x'} | ${false} - `('isSingleVersion("$version") === $isSingle', ({ version, isSingle }) => { - const res = !!pvp.isSingleVersion(version); - expect(res).toBe(isSingle); + input | expected + ${'9.0.3'} | ${true} + ${'1.2019.3.22'} | ${true} + ${'3.0.0-beta'} | ${false} + ${'2.0.2-pre20191018090318'} | ${false} + ${'1.0.0+c30d7625'} | ${false} + ${'2.3.4-beta+1990ef74'} | ${false} + ${'17.04'} | ${false} + ${'3.0.0.beta'} | ${false} + ${'5.1.2-+'} | ${false} + `('isVersion("$input") === $expected', ({ input, expected }) => { + const res = !!pvp.isVersion(input); + expect(res).toBe(expected); }); test.each` - currentValue | rangeStrategy | currentVersion | newVersion | expected - ${'=1.0.0'} | ${'bump'} | ${'1.0.0'} | ${'1.1.0'} | ${'=1.1.0'} - ${'^1.0'} | ${'bump'} | ${'1.0.0'} | ${'1.0.7'} | ${'^1.0'} - ${'^1'} | ${'bump'} | ${'1.0.0'} | ${'1.0.7-prerelease.1'} | ${'^1.0.7-prerelease.1'} - ${'~> 1.0.0'} | ${'replace'} | ${'1.0.0'} | ${'1.1.7'} | ${'~> 1.1.0'} - ${'^1.0'} | ${'bump'} | ${'1.0.0'} | ${'1.1.7'} | ${'^1.1'} - ${'~1.0'} | ${'bump'} | ${'1.0.0'} | ${'1.1.7'} | ${'~1.1'} - ${'~1.0'} | ${'bump'} | ${'1.0.0'} | ${'1.0.7-prerelease.1'} | ${'~1.0.7-prerelease.1'} - ${'^1'} | ${'bump'} | ${'1.0.0'} | ${'2.1.7'} | ${'^2'} - ${'~1'} | ${'bump'} | ${'1.0.0'} | ${'1.1.7'} | ${'~1'} - ${'5'} | ${'bump'} | ${'5.0.0'} | ${'5.1.7'} | ${'5'} - ${'5'} | ${'bump'} | ${'5.0.0'} | ${'6.1.7'} | ${'6'} - ${'5.0'} | ${'bump'} | ${'5.0.0'} | ${'5.0.7'} | ${'5.0'} - ${'5.0'} | ${'bump'} | ${'5.0.0'} | ${'5.1.7'} | ${'5.1'} - ${'5.0'} | ${'bump'} | ${'5.0.0'} | ${'6.1.7'} | ${'6.1'} - ${'>=1.0.0'} | ${'bump'} | ${'1.0.0'} | ${'1.1.0'} | ${'>=1.1.0'} - ${'>= 1.0.0'} | ${'bump'} | ${'1.0.0'} | ${'1.1.0'} | ${'>= 1.1.0'} - ${'=1.0.0'} | ${'replace'} | ${'1.0.0'} | ${'1.1.0'} | ${'=1.1.0'} - ${'1.0.*'} | ${'replace'} | ${'1.0.0'} | ${'1.1.0'} | ${'1.1.*'} - ${'1.*'} | ${'replace'} | ${'1.0.0'} | ${'2.1.0'} | ${'2.*'} - ${'~0.6.1'} | ${'replace'} | ${'0.6.8'} | ${'0.7.0-rc.2'} | ${'~0.7.0-rc'} - ${'>= 0.1.21 < 0.2.0'} | ${'bump'} | ${'0.1.21'} | ${'0.1.24'} | ${'>= 0.1.24 < 0.2.0'} - ${'>= 0.1.21 <= 0.2.0'} | ${'bump'} | ${'0.1.21'} | ${'0.1.24'} | ${'>= 0.1.24 <= 0.2.0'} - ${'>= 0.0.1 <= 0.1'} | ${'bump'} | ${'0.0.1'} | ${'0.0.2'} | ${'>= 0.0.2 <= 0.1'} - ${'>= 0.0.1 < 0.1'} | ${'bump'} | ${'0.1.0'} | ${'0.2.1'} | ${'>= 0.2.1 < 0.3'} - ${'>= 0.0.1 < 0.0.4'} | ${'bump'} | ${'0.0.4'} | ${'0.0.5'} | ${'>= 0.0.5 < 0.0.6'} - ${'>= 0.0.1 < 1'} | ${'bump'} | ${'1.0.0'} | ${'1.0.1'} | ${'>= 1.0.1 < 2'} - ${'>= 0.0.1 < 1'} | ${'bump'} | ${'1.0.0'} | ${'1.0.1'} | ${'>= 1.0.1 < 2'} - ${'<=1.2.3'} | ${'widen'} | ${'1.0.0'} | ${'1.2.3'} | ${'<=1.2.3'} - ${'<=1.2.3'} | ${'widen'} | ${'1.0.0'} | ${'1.2.4'} | ${'<=1.2.4'} - ${'>=1.2.3'} | ${'widen'} | ${'1.0.0'} | ${'1.2.3'} | ${'>=1.2.3'} - ${'>=1.2.3'} | ${'widen'} | ${'1.0.0'} | ${'1.2.1'} | ${'>=1.2.3 || 1.2.1'} - ${'^0.0.3'} | ${'replace'} | ${'0.0.3'} | ${'0.0.6'} | ${'^0.0.6'} - ${'^0.0.3'} | ${'replace'} | ${'0.0.3'} | ${'0.5.0'} | ${'^0.5.0'} - ${'^0.0.3'} | ${'replace'} | ${'0.0.3'} | ${'0.5.6'} | ${'^0.5.0'} - ${'^0.0.3'} | ${'replace'} | ${'0.0.3'} | ${'4.0.0'} | ${'^4.0.0'} - ${'^0.0.3'} | ${'replace'} | ${'0.0.3'} | ${'4.0.6'} | ${'^4.0.0'} - ${'^0.0.3'} | ${'replace'} | ${'0.0.3'} | ${'4.5.6'} | ${'^4.0.0'} - ${'^0.2.0'} | ${'replace'} | ${'0.2.0'} | ${'0.5.6'} | ${'^0.5.0'} - ${'^0.2.3'} | ${'replace'} | ${'0.2.3'} | ${'0.5.0'} | ${'^0.5.0'} - ${'^0.2.3'} | ${'replace'} | ${'0.2.3'} | ${'0.5.6'} | ${'^0.5.0'} - ${'^1.2.3'} | ${'replace'} | ${'1.2.3'} | ${'4.0.0'} | ${'^4.0.0'} - ${'^1.2.3'} | ${'replace'} | ${'1.2.3'} | ${'4.5.6'} | ${'^4.0.0'} - ${'^1.0.0'} | ${'replace'} | ${'1.0.0'} | ${'4.5.6'} | ${'^4.0.0'} - ${'^0.2.3'} | ${'replace'} | ${'0.2.3'} | ${'0.2.4'} | ${'^0.2.3'} - ${'^2.3.0'} | ${'replace'} | ${'2.3.0'} | ${'2.4.0'} | ${'^2.3.0'} - ${'^2.3.4'} | ${'replace'} | ${'2.3.4'} | ${'2.4.5'} | ${'^2.3.4'} - ${'^0.0.1'} | ${'replace'} | ${'0.0.1'} | ${'0.0.2'} | ${'^0.0.2'} - ${'^1.0.1'} | ${'replace'} | ${'1.0.1'} | ${'2.0.2'} | ${'^2.0.0'} - ${'^1.2.3'} | ${'replace'} | ${'1.2.3'} | ${'1.2.3'} | ${'^1.2.3'} - ${'^1.2.3'} | ${'replace'} | ${'1.2.3'} | ${'1.2.2'} | ${'^1.2.2'} - ${'^0.9.21'} | ${'replace'} | ${'0.9.21'} | ${'0.9.22'} | ${'^0.9.21'} - `( - 'getNewValue("$currentValue", "$rangeStrategy", "$currentVersion", "$newVersion") === "$expected"', - ({ currentValue, rangeStrategy, currentVersion, newVersion, expected }) => { - const res = pvp.getNewValue({ - currentValue, - rangeStrategy, - currentVersion, - newVersion, - }); - expect(res).toEqual(expected); - } - ); + input | expected + ${'9.0.3'} | ${true} + ${'1.2019.3.22'} | ${true} + ${'3.0.0-beta'} | ${false} + ${'2.0.2-pre20191018090318'} | ${false} + ${'1.0.0+c30d7625'} | ${false} + ${'2.3.4-beta+1990ef74'} | ${false} + `('isStable("$input") === $expected', ({ input, expected }) => { + expect(pvp.isStable(input)).toBe(expected); + }); + + test.each` + a | b | expected + ${'17.4.0'} | ${'17.4.0'} | ${true} + ${'1.4'} | ${'1.4.0'} | ${false} + ${'1.0.110'} | ${'1.0.110.0'} | ${false} + ${'1.0.0'} | ${'1.0.0'} | ${true} + `('equals($a, $b) === $expected', ({ a, b, expected }) => { + expect(pvp.equals(a, b)).toBe(expected); + }); + + test.each` + a | b | expected + ${'2.4.2'} | ${'2.4.1'} | ${true} + ${'1.9.0'} | ${'2.0.0'} | ${false} + ${'1.9.0'} | ${'1.9.1'} | ${false} + `('isGreaterThan($a, $b) === $expected', ({ a, b, expected }) => { + expect(pvp.isGreaterThan(a, b)).toBe(expected); + }); }); From 8f68b3834b2ef821132ee8a70652ddbc3c2dde7f Mon Sep 17 00:00:00 2001 From: psilospore Date: Thu, 20 Jan 2022 18:18:58 -0500 Subject: [PATCH 06/14] Apply suggestions from code review Co-authored-by: HonkingGoose <34918129+HonkingGoose@users.noreply.github.com> --- lib/versioning/pvp/index.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/versioning/pvp/index.ts b/lib/versioning/pvp/index.ts index eb4e654300b016..50907e67d59c12 100644 --- a/lib/versioning/pvp/index.ts +++ b/lib/versioning/pvp/index.ts @@ -8,7 +8,7 @@ export const urls = ['https://pvp.haskell.org']; export const supportsRanges = false; /** - * At least 3 components and no leading 0s + * At least three components and no leading zeros * https://pvp.haskell.org/#version-number */ const versionPattern = regEx( @@ -18,7 +18,7 @@ const versionPattern = regEx( class PvpVersioningApi extends GenericVersioningApi { /** * PVP has two major components A.B - * To keep compatability with Renovate's versioning API we will treat it as a float + * To keep compatibility with Renovate's versioning API we will treat it as a float */ protected _parse(version: string): GenericVersion { const matches = versionPattern.exec(version); @@ -49,7 +49,7 @@ class PvpVersioningApi extends GenericVersioningApi { /** * Compare similar to GenericVersioningApi._compare implementation - * except 2.1.1.0 and 2.1.1 are not equivilant instead 2.1.1.0 > 2.1.1 + * except 2.1.1.0 and 2.1.1 are not equivalent instead 2.1.1.0 > 2.1.1 */ override _compare(version: string, other: string): number { const left = this._parse(version); From 0d5147e6fe26a8380d3fea4bf1f22c6a587cd969 Mon Sep 17 00:00:00 2001 From: psilospore Date: Tue, 25 Jan 2022 22:20:17 -0500 Subject: [PATCH 07/14] Add documentation for PVP --- lib/versioning/pvp/readme.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 lib/versioning/pvp/readme.md diff --git a/lib/versioning/pvp/readme.md b/lib/versioning/pvp/readme.md new file mode 100644 index 00000000000000..8caffd9ff03f08 --- /dev/null +++ b/lib/versioning/pvp/readme.md @@ -0,0 +1,20 @@ +# PVP + +**What type of versioning is used?** + +> A package version number SHOULD have the form A.B.C, and MAY optionally have any number of additional components, for example 2.1.0.4 (in this case, A=2, B=1, C=0). +> A.B is known as the major version number, and C the minor version number. +> Specification: https://pvp.haskell.org/ + +The one unusual difference between PVP and semver is that there are 2 major versions, and that +there could be additional version numbers past patch. + +**Are ranges supported?** + +Currently this is not supported but you can have ranges. +This implementation just supports updating extra-deps in the stack build tool which does not support ranges. +If this is used for cabal then it would be useful to support ranges. + +**Are commit hashes supported?** + +No. From f16c9460700fae92848c0d4deb50f79386ff3dac Mon Sep 17 00:00:00 2001 From: psilospore Date: Wed, 26 Jan 2022 15:19:40 -0500 Subject: [PATCH 08/14] Apply suggestions from code review Co-authored-by: HonkingGoose <34918129+HonkingGoose@users.noreply.github.com> --- lib/versioning/pvp/readme.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/versioning/pvp/readme.md b/lib/versioning/pvp/readme.md index 8caffd9ff03f08..ec992326b00436 100644 --- a/lib/versioning/pvp/readme.md +++ b/lib/versioning/pvp/readme.md @@ -2,18 +2,19 @@ **What type of versioning is used?** -> A package version number SHOULD have the form A.B.C, and MAY optionally have any number of additional components, for example 2.1.0.4 (in this case, A=2, B=1, C=0). -> A.B is known as the major version number, and C the minor version number. -> Specification: https://pvp.haskell.org/ +Quotes from the [Haskell PVP Specification](https://pvp.haskell.org/): -The one unusual difference between PVP and semver is that there are 2 major versions, and that -there could be additional version numbers past patch. +> A package version number **SHOULD** have the form A.B.C, and **MAY** optionally have any number of additional components, for example 2.1.0.4 (in this case, A=2, B=1, C=0). + +> A.B is known as the _major_ version number, and C the _minor_ version number. + +The one unusual difference between PVP and SemVer is that there are two major versions, and that there could be additional version numbers past _patch_. **Are ranges supported?** Currently this is not supported but you can have ranges. -This implementation just supports updating extra-deps in the stack build tool which does not support ranges. -If this is used for cabal then it would be useful to support ranges. +This implementation just supports updating extra-deps in the Stack build tool which does not support ranges. +If this is used for Cabal then it would be useful to support ranges. **Are commit hashes supported?** From f9cd49a0cf98b176d8e75288d4459ae28a72a5d6 Mon Sep 17 00:00:00 2001 From: psilospore Date: Wed, 26 Jan 2022 15:24:00 -0500 Subject: [PATCH 09/14] Clarify optional additional components --- lib/versioning/pvp/readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/versioning/pvp/readme.md b/lib/versioning/pvp/readme.md index ec992326b00436..bf518ace34dadc 100644 --- a/lib/versioning/pvp/readme.md +++ b/lib/versioning/pvp/readme.md @@ -8,7 +8,7 @@ Quotes from the [Haskell PVP Specification](https://pvp.haskell.org/): > A.B is known as the _major_ version number, and C the _minor_ version number. -The one unusual difference between PVP and SemVer is that there are two major versions, and that there could be additional version numbers past _patch_. +The one unusual difference between PVP and SemVer is that there are two major versions, and that there can be an optional number of additional version numbers past _minor_. For example 1.2.3 (note there's no patch version here), 1.2.3.4 (4 is the patch version), or 1.2.3.4.5.6.7 are all valid version numbers. **Are ranges supported?** From d99e88880661a69a73e2beea68e661f29d32aa66 Mon Sep 17 00:00:00 2001 From: psilospore Date: Thu, 27 Jan 2022 17:48:47 -0500 Subject: [PATCH 10/14] Update lib/versioning/pvp/readme.md Co-authored-by: HonkingGoose <34918129+HonkingGoose@users.noreply.github.com> --- lib/versioning/pvp/readme.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/versioning/pvp/readme.md b/lib/versioning/pvp/readme.md index bf518ace34dadc..32fccc2529dd96 100644 --- a/lib/versioning/pvp/readme.md +++ b/lib/versioning/pvp/readme.md @@ -8,7 +8,8 @@ Quotes from the [Haskell PVP Specification](https://pvp.haskell.org/): > A.B is known as the _major_ version number, and C the _minor_ version number. -The one unusual difference between PVP and SemVer is that there are two major versions, and that there can be an optional number of additional version numbers past _minor_. For example 1.2.3 (note there's no patch version here), 1.2.3.4 (4 is the patch version), or 1.2.3.4.5.6.7 are all valid version numbers. +The one unusual difference between PVP and SemVer is that there are two major versions, and that there can be an optional number of additional version numbers past _minor_. +For example `1.2.3` (note there's no patch version here), `1.2.3.4` (`4` is the patch version), or `1.2.3.4.5.6.7` are all valid version numbers. **Are ranges supported?** From a582f6fb22caa0431dfe19e0223038f6c9c5fbad Mon Sep 17 00:00:00 2001 From: psilospore Date: Fri, 28 Jan 2022 11:26:52 -0500 Subject: [PATCH 11/14] Apply suggestions from code review Co-authored-by: Michael Kriese Co-authored-by: HonkingGoose <34918129+HonkingGoose@users.noreply.github.com> --- lib/versioning/pvp/index.ts | 2 +- lib/versioning/pvp/readme.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/versioning/pvp/index.ts b/lib/versioning/pvp/index.ts index 50907e67d59c12..d4d53b132e2051 100644 --- a/lib/versioning/pvp/index.ts +++ b/lib/versioning/pvp/index.ts @@ -1,5 +1,5 @@ import { regEx } from '../../util/regex'; -import { GenericVersion, GenericVersioningApi } from '../loose/generic'; +import { GenericVersion, GenericVersioningApi } from '../generic'; import { VersioningApi } from '../types'; export const id = 'pvp'; diff --git a/lib/versioning/pvp/readme.md b/lib/versioning/pvp/readme.md index 32fccc2529dd96..0cdf1a738ea70c 100644 --- a/lib/versioning/pvp/readme.md +++ b/lib/versioning/pvp/readme.md @@ -9,7 +9,7 @@ Quotes from the [Haskell PVP Specification](https://pvp.haskell.org/): > A.B is known as the _major_ version number, and C the _minor_ version number. The one unusual difference between PVP and SemVer is that there are two major versions, and that there can be an optional number of additional version numbers past _minor_. -For example `1.2.3` (note there's no patch version here), `1.2.3.4` (`4` is the patch version), or `1.2.3.4.5.6.7` are all valid version numbers. +For example `1.2.3` (note there's no _patch_ version here), `1.2.3.4` (`4` is the _patch_ version), or `1.2.3.4.5.6.7` are all valid version numbers. **Are ranges supported?** From fbd784d67e0806beea11b0103b994cff038b2e42 Mon Sep 17 00:00:00 2001 From: psilospore Date: Wed, 9 Feb 2022 19:17:06 -0500 Subject: [PATCH 12/14] fix: Added additional test coverage, return correct major version --- lib/versioning/pvp/index.spec.ts | 24 ++++++++++++++++++++++++ lib/versioning/pvp/index.ts | 14 +++++++++----- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/lib/versioning/pvp/index.spec.ts b/lib/versioning/pvp/index.spec.ts index 000a9977d37b28..179cbea29d8e95 100644 --- a/lib/versioning/pvp/index.spec.ts +++ b/lib/versioning/pvp/index.spec.ts @@ -70,4 +70,28 @@ describe('versioning/pvp/index', () => { `('isGreaterThan($a, $b) === $expected', ({ a, b, expected }) => { expect(pvp.isGreaterThan(a, b)).toBe(expected); }); + + test.each` + input | expected + ${'1.2.3'} | ${1.2} + ${'1.0.2'} | ${1} + `('getMajor("$input") === $expected', ({ input, expected }) => { + expect(pvp.getMajor(input)).toBe(expected); + }); + + test.each` + input | expected + ${'1.2.3'} | ${3} + ${'1.0.0'} | ${0} + `('getMinor("$input") === $expected', ({ input, expected }) => { + expect(pvp.getMinor(input)).toBe(expected); + }); + + test.each` + input | expected + ${'1.2.3.4'} | ${4} + ${'1.0.2'} | ${undefined} + `('getPatch("$input") === $expected', ({ input, expected }) => { + expect(pvp.getPatch(input)).toBe(expected); + }); }); diff --git a/lib/versioning/pvp/index.ts b/lib/versioning/pvp/index.ts index d4d53b132e2051..ea6e89e483ed7c 100644 --- a/lib/versioning/pvp/index.ts +++ b/lib/versioning/pvp/index.ts @@ -33,18 +33,22 @@ class PvpVersioningApi extends GenericVersioningApi { } override getMajor(version: string): number | null { - const { release } = this._parse(version); - return parseFloat(`${release[0]}.${release[1]}`); + const { + release: [major], + } = this._parse(version); + return major; } override getMinor(version: string): number | null { - const { release } = this._parse(version); - return release[2]; + const { + release: [, minor], + } = this._parse(version); + return minor; } override getPatch(version: string): number | null { const { release } = this._parse(version); - return release[3]; + return release[2]; } /** From f4ec4f39b79d35aa481b86c3184c2b7b040db103 Mon Sep 17 00:00:00 2001 From: psilospore Date: Wed, 16 Feb 2022 21:06:03 -0500 Subject: [PATCH 13/14] fix: lint markdown and use import type --- lib/versioning/pvp/index.ts | 2 +- lib/versioning/pvp/readme.md | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/versioning/pvp/index.ts b/lib/versioning/pvp/index.ts index ea6e89e483ed7c..7d68edeb16acfd 100644 --- a/lib/versioning/pvp/index.ts +++ b/lib/versioning/pvp/index.ts @@ -1,6 +1,6 @@ import { regEx } from '../../util/regex'; import { GenericVersion, GenericVersioningApi } from '../generic'; -import { VersioningApi } from '../types'; +import type { VersioningApi } from '../types'; export const id = 'pvp'; export const displayName = 'PVP'; diff --git a/lib/versioning/pvp/readme.md b/lib/versioning/pvp/readme.md index 0cdf1a738ea70c..23cb503e6b88a8 100644 --- a/lib/versioning/pvp/readme.md +++ b/lib/versioning/pvp/readme.md @@ -5,7 +5,6 @@ Quotes from the [Haskell PVP Specification](https://pvp.haskell.org/): > A package version number **SHOULD** have the form A.B.C, and **MAY** optionally have any number of additional components, for example 2.1.0.4 (in this case, A=2, B=1, C=0). - > A.B is known as the _major_ version number, and C the _minor_ version number. The one unusual difference between PVP and SemVer is that there are two major versions, and that there can be an optional number of additional version numbers past _minor_. From db522522a40b8e90ab80e708dda17883fdc7f09e Mon Sep 17 00:00:00 2001 From: psilospore Date: Mon, 21 Feb 2022 13:01:48 -0500 Subject: [PATCH 14/14] Apply suggestions from code review Co-authored-by: Michael Kriese Co-authored-by: HonkingGoose <34918129+HonkingGoose@users.noreply.github.com> --- lib/versioning/pvp/index.spec.ts | 2 +- lib/versioning/pvp/index.ts | 4 ++-- lib/versioning/pvp/readme.md | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/versioning/pvp/index.spec.ts b/lib/versioning/pvp/index.spec.ts index 179cbea29d8e95..5e16e95d6d7946 100644 --- a/lib/versioning/pvp/index.spec.ts +++ b/lib/versioning/pvp/index.spec.ts @@ -20,7 +20,7 @@ describe('versioning/pvp/index', () => { ${'renovatebot/renovate#main'} | ${false} ${'https://github.com/renovatebot/renovate.git'} | ${false} `('isValid("$version") === $isValid', ({ version, isValid }) => { - const res = !!pvp.isValid(version); + const res = pvp.isValid(version); expect(res).toBe(isValid); }); diff --git a/lib/versioning/pvp/index.ts b/lib/versioning/pvp/index.ts index 7d68edeb16acfd..cea1723a66c340 100644 --- a/lib/versioning/pvp/index.ts +++ b/lib/versioning/pvp/index.ts @@ -20,7 +20,7 @@ class PvpVersioningApi extends GenericVersioningApi { * PVP has two major components A.B * To keep compatibility with Renovate's versioning API we will treat it as a float */ - protected _parse(version: string): GenericVersion { + protected _parse(version: string): GenericVersion | null { const matches = versionPattern.exec(version); if (!matches) { return null; @@ -28,7 +28,7 @@ class PvpVersioningApi extends GenericVersioningApi { const components = version.split('.'); const major = parseFloat(`${components[0]}.${components[1]}`); - const rest = components.slice(2).map((i) => parseInt(i)); + const rest = components.slice(2).map((i) => parseInt(i, 10)); return { release: [major, ...rest] }; } diff --git a/lib/versioning/pvp/readme.md b/lib/versioning/pvp/readme.md index 23cb503e6b88a8..42e4bd278d13de 100644 --- a/lib/versioning/pvp/readme.md +++ b/lib/versioning/pvp/readme.md @@ -1,6 +1,6 @@ # PVP -**What type of versioning is used?** +## What type of versioning is used? Quotes from the [Haskell PVP Specification](https://pvp.haskell.org/): @@ -10,12 +10,12 @@ Quotes from the [Haskell PVP Specification](https://pvp.haskell.org/): The one unusual difference between PVP and SemVer is that there are two major versions, and that there can be an optional number of additional version numbers past _minor_. For example `1.2.3` (note there's no _patch_ version here), `1.2.3.4` (`4` is the _patch_ version), or `1.2.3.4.5.6.7` are all valid version numbers. -**Are ranges supported?** +## Are ranges supported? Currently this is not supported but you can have ranges. This implementation just supports updating extra-deps in the Stack build tool which does not support ranges. If this is used for Cabal then it would be useful to support ranges. -**Are commit hashes supported?** +## Are commit hashes supported? No.