diff --git a/lib/versioning/aws-machine-image/index.spec.ts b/lib/versioning/aws-machine-image/index.spec.ts index e4aff0a818fdc7b655a0a8e8b877c9b8a4d9f90d..69af0932d73d161e5ef0dc6620a1ca3d0cebc6ce 100644 --- a/lib/versioning/aws-machine-image/index.spec.ts +++ b/lib/versioning/aws-machine-image/index.spec.ts @@ -44,7 +44,8 @@ describe('versioning/aws-machine-image/index', () => { }); describe('isGreaterThan(version1, version2)', () => { it('should return false', () => { - expect(aws.isGreaterThan('', '')).toBeTruthy(); + expect(aws.isGreaterThan('ami-00', 'ami-99')).toBeFalse(); + expect(aws.isGreaterThan('ami-99', 'ami-00')).toBeFalse(); }); }); }); diff --git a/lib/versioning/aws-machine-image/index.ts b/lib/versioning/aws-machine-image/index.ts index 0f7b6fed1b8ec2a3b847fbc5ca14f27468d12b86..736fa54dc1938ccf4d18c8c6a7ddaf4e6adbbd0b 100644 --- a/lib/versioning/aws-machine-image/index.ts +++ b/lib/versioning/aws-machine-image/index.ts @@ -1,4 +1,5 @@ -import * as generic from '../loose/generic'; +import { regEx } from '../../util/regex'; +import { GenericVersion, GenericVersioningApi } from '../loose/generic'; import type { VersioningApi } from '../types'; export const id = 'aws-machine-image'; @@ -8,37 +9,25 @@ export const urls = []; export const supportsRanges = false; -function parse(version: string): any { - return { release: [1, 0, 0] }; +const awsMachineImageRegex = regEx('^ami-(?<suffix>[a-z0-9]{17})$'); + +class AwsMachineImageVersioningApi extends GenericVersioningApi { + protected _parse(version: string): GenericVersion | null { + if (version) { + const matchGroups = awsMachineImageRegex.exec(version)?.groups; + if (matchGroups) { + const { suffix } = matchGroups; + return { release: [1, 0, 0], suffix }; + } + } + return null; + } + + protected override _compare(_version: string, _other: string): number { + return 0; + } } -function compare(version1: string, version2: string): number { - return 1; -} - -function isValid(input: string): string | boolean | null { - return typeof input === 'string' && /^ami-[a-z0-9]{17}$/.test(input); -} - -function isVersion(input: string): string | boolean | null { - return isValid(input); -} - -function isCompatible( - version: string, - _range?: string -): string | boolean | null { - return isValid(version); -} - -export const api: VersioningApi = { - ...generic.create({ - parse, - compare, - }), - isValid, - isVersion, - isCompatible, -}; +export const api: VersioningApi = new AwsMachineImageVersioningApi(); export default api; diff --git a/lib/versioning/docker/index.spec.ts b/lib/versioning/docker/index.spec.ts index d9284a79c954842823e3633ee698c0ff21567a10..f49a9cb9aedb791af4a6424506f38bdaea0f3dd5 100644 --- a/lib/versioning/docker/index.spec.ts +++ b/lib/versioning/docker/index.spec.ts @@ -131,7 +131,7 @@ describe('versioning/docker/index', () => { '3.8.0', ]; - expect(versions.sort(docker.sortVersions)).toEqual([ + expect(versions.sort((x, y) => docker.sortVersions(x, y))).toEqual([ '3.7.0b1', '3.7.0b5', '3.7.0', diff --git a/lib/versioning/docker/index.ts b/lib/versioning/docker/index.ts index 1ec88bb36ed330f993b69f373795d997160e1f12..909ede9a8ec083a00f4feed481f128f70f75506b 100644 --- a/lib/versioning/docker/index.ts +++ b/lib/versioning/docker/index.ts @@ -1,5 +1,5 @@ import { regEx } from '../../util/regex'; -import * as generic from '../loose/generic'; +import { GenericVersion, GenericVersioningApi } from '../loose/generic'; import type { VersioningApi } from '../types'; export const id = 'docker'; @@ -13,81 +13,79 @@ const versionPattern = regEx(/^(?<version>\d+(?:\.\d+)*)(?<prerelease>.*)$/); const commitHashPattern = regEx(/^[a-f0-9]{7,40}$/); const numericPattern = regEx(/^[0-9]+$/); -function parse(version: string): generic.GenericVersion { - if (commitHashPattern.test(version) && !numericPattern.test(version)) { - return null; - } - const versionPieces = version.replace(regEx(/^v/), '').split('-'); - const prefix = versionPieces.shift(); - const suffix = versionPieces.join('-'); - const m = versionPattern.exec(prefix); - if (!m?.groups) { - return null; - } - - const { version: ver, prerelease } = m.groups; - const release = ver.split('.').map(Number); - return { release, suffix, prerelease }; -} - -function valueToVersion(value: string): string { - // Remove any suffix after '-', e.g. '-alpine' - return value ? value.split('-')[0] : value; -} - -function compare(version1: string, version2: string): number { - const parsed1 = parse(version1); - const parsed2 = parse(version2); - // istanbul ignore if - if (!(parsed1 && parsed2)) { - return 1; - } - const length = Math.max(parsed1.release.length, parsed2.release.length); - for (let i = 0; i < length; i += 1) { - const part1 = parsed1.release[i]; - const part2 = parsed2.release[i]; - // shorter is bigger 2.1 > 2.1.1 - if (part1 === undefined) { - return 1; +class DockerVersioningApi extends GenericVersioningApi { + protected _parse(version: string): GenericVersion | null { + if (!version) { + return null; } - if (part2 === undefined) { - return -1; + if (commitHashPattern.test(version) && !numericPattern.test(version)) { + return null; } - if (part1 !== part2) { - return part1 - part2; + const versionPieces = version.replace(regEx(/^v/), '').split('-'); + const prefix = versionPieces.shift(); + const suffix = versionPieces.join('-'); + const m = versionPattern.exec(prefix); + if (!m?.groups) { + return null; } + + const { version: ver, prerelease } = m.groups; + const release = ver.split('.').map(Number); + return { release, suffix, prerelease }; } - if (parsed1.prerelease !== parsed2.prerelease) { - // unstable is lower - if (!parsed1.prerelease && parsed2.prerelease) { + + protected override _compare(version: string, other: string): number { + const parsed1 = this._parse(version); + const parsed2 = this._parse(other); + // istanbul ignore if + if (!(parsed1 && parsed2)) { return 1; } - if (parsed1.prerelease && !parsed2.prerelease) { - return -1; + const length = Math.max(parsed1.release.length, parsed2.release.length); + for (let i = 0; i < length; i += 1) { + const part1 = parsed1.release[i]; + const part2 = parsed2.release[i]; + // shorter is bigger 2.1 > 2.1.1 + if (part1 === undefined) { + return 1; + } + if (part2 === undefined) { + return -1; + } + if (part1 !== part2) { + return part1 - part2; + } + } + if (parsed1.prerelease !== parsed2.prerelease) { + // unstable is lower + if (!parsed1.prerelease && parsed2.prerelease) { + return 1; + } + if (parsed1.prerelease && !parsed2.prerelease) { + return -1; + } + // alphabetic order + return parsed1.prerelease.localeCompare(parsed2.prerelease); } - // alphabetic order - return parsed1.prerelease.localeCompare(parsed2.prerelease); + // equals + return parsed2.suffix.localeCompare(parsed1.suffix); + } + + override isCompatible(version: string, range: string): boolean { + const parsed1 = this._parse(version); + const parsed2 = this._parse(range); + return ( + parsed1.suffix === parsed2.suffix && + parsed1.release.length === parsed2.release.length + ); } - // equals - return parsed2.suffix.localeCompare(parsed1.suffix); -} -function isCompatible(version: string, range: string): boolean { - const parsed1 = parse(version); - const parsed2 = parse(range); - return ( - parsed1.suffix === parsed2.suffix && - parsed1.release.length === parsed2.release.length - ); + valueToVersion(value: string): string { + // Remove any suffix after '-', e.g. '-alpine' + return value ? value.split('-')[0] : value; + } } -export const api: VersioningApi = { - ...generic.create({ - parse, - compare, - }), - isCompatible, - valueToVersion, -}; +export const api: VersioningApi = new DockerVersioningApi(); export default api; diff --git a/lib/versioning/git/index.spec.ts b/lib/versioning/git/index.spec.ts index 53b5b8b35c9e4a5b13e37e850db61a92bcdba988..5230eb7932fa9421e36492bbe8417c98607e395d 100644 --- a/lib/versioning/git/index.spec.ts +++ b/lib/versioning/git/index.spec.ts @@ -1,24 +1,40 @@ import git from '.'; describe('versioning/git/index', () => { - describe('isValid(version)', () => { - it('should return true', () => { - expect(git.isValid('a1')).toBeTruthy(); - }); + test.each` + input | expected + ${''} | ${false} + ${'2'} | ${false} + ${'29'} | ${false} + ${'29c'} | ${false} + ${'29c7'} | ${false} + ${'29c79'} | ${false} + ${'29c792'} | ${false} + ${'29c7921'} | ${true} + ${'29c792109259545157f4bc3f8d43f47ffcf34e20'} | ${true} + ${'foobar'} | ${false} + `('isValid("$input") === $expected', ({ input, expected }) => { + expect(git.isValid(input)).toBe(expected); }); - describe('isCompatible(version)', () => { - it('should return true', () => { - expect(git.isCompatible('')).toBeTruthy(); - }); - }); - describe('isGreaterThan(version1, version2)', () => { - it('should return false', () => { - expect(git.isGreaterThan('', '')).toBeFalsy(); - }); - }); - describe('valueToVersion(version)', () => { - it('should return same as input', () => { - expect(git.valueToVersion('')).toBe(''); - }); + + test.each` + version | range | expected + ${''} | ${''} | ${false} + ${'1234567890aBcDeF'} | ${''} | ${true} + `( + 'isCompatible("$version") === $expected', + ({ version, range, expected }) => { + const res = git.isCompatible(version, range); + expect(!!res).toBe(expected); + } + ); + + test.each` + a | b | expected + ${''} | ${''} | ${false} + ${'abc'} | ${'bca'} | ${false} + ${'123'} | ${'321'} | ${false} + `('isGreaterThan("$a", "$b") === $expected', ({ a, b, expected }) => { + expect(git.isGreaterThan(a, b)).toBe(expected); }); }); diff --git a/lib/versioning/git/index.ts b/lib/versioning/git/index.ts index 3cf9cfcc187cb7fdd5ba1a6e8ab8bd0bcb636867..3c2e50991732d6eb634d17a0145ac5c797227493 100644 --- a/lib/versioning/git/index.ts +++ b/lib/versioning/git/index.ts @@ -1,4 +1,5 @@ -import * as generic from '../loose/generic'; +import { regEx } from '../../util/regex'; +import { GenericVersion, GenericVersioningApi } from '../loose/generic'; import type { VersioningApi } from '../types'; export const id = 'git'; @@ -6,21 +7,21 @@ export const displayName = 'git'; export const urls = ['https://git-scm.com/']; export const supportsRanges = false; -const parse = (version: string): any => ({ release: [parseInt(version, 10)] }); +const regex = regEx('^[0-9a-f]{7,40}$', 'i'); -const isCompatible = (version: string, range: string): boolean => true; +class GitVersioningApi extends GenericVersioningApi { + protected _parse(version: string): GenericVersion | null { + if (version?.match(regex)) { + return { release: [1, 0, 0], suffix: version }; + } + return null; + } -const compare = (version1: string, version2: string): number => -1; + protected override _compare(_version: string, _other: string): number { + return -1; + } +} -const valueToVersion = (value: string): string => value; - -export const api: VersioningApi = { - ...generic.create({ - parse, - compare, - }), - isCompatible, - valueToVersion, -}; +export const api: VersioningApi = new GitVersioningApi(); export default api; diff --git a/lib/versioning/loose/generic.ts b/lib/versioning/loose/generic.ts index a57acac54ced7bcff823bbc5e512823a8f2b8fd4..cf8d49bbed7fd7246d2630760a224296f9bb2a4c 100644 --- a/lib/versioning/loose/generic.ts +++ b/lib/versioning/loose/generic.ts @@ -15,119 +15,6 @@ export interface VersionComparator { (version: string, other: string): number; } -// since this file was meant for no range support, a range = version -// parse should return null if version not valid -// parse should return an object with property release, an array of version sections major.minor.patch -export const parser = (parse: VersionParser): Partial<VersioningApi> => { - function isValid(version: string): string { - if (!version) { - return null; - } - const parsed = parse(version); - return parsed ? version : null; - } - function getSection(version: string, index: number): number { - const parsed = parse(version); - return parsed && parsed.release.length > index - ? parsed.release[index] - : null; - } - function getMajor(version: string): number { - return getSection(version, 0); - } - function getMinor(version: string): number { - return getSection(version, 1); - } - function getPatch(version: string): number { - return getSection(version, 2); - } - - function isStable(version: string): boolean { - const parsed = parse(version); - return parsed && !parsed.prerelease; - } - - return { - // validation - isCompatible: isValid, - isSingleVersion: isValid, - isStable, - isValid, - isVersion: isValid, - // digestion of version - getMajor, - getMinor, - getPatch, - }; -}; - -// this is the main reason this file was created -// most operations below could be derived from a compare function -export const comparer = ( - compare: VersionComparator -): Partial<VersioningApi> => { - function equals(version: string, other: string): boolean { - return compare(version, other) === 0; - } - - function isGreaterThan(version: string, other: string): boolean { - return compare(version, other) > 0; - } - function isLessThanRange(version: string, range: string): boolean { - return compare(version, range) < 0; - } - - // we don't not have ranges, so versions has to be equal - function getSatisfyingVersion(versions: string[], range: string): string { - return versions.find((v) => equals(v, range)) || null; - } - function minSatisfyingVersion(versions: string[], range: string): string { - return versions.find((v) => equals(v, range)) || null; - } - function getNewValue(newValueConfig: NewValueConfig): string { - const { newVersion } = newValueConfig || {}; - return newVersion; - } - function sortVersions(version: string, other: string): number { - return compare(version, other); - } - - return { - equals, - isGreaterThan, - isLessThanRange, - matches: equals, - getSatisfyingVersion, - minSatisfyingVersion, - getNewValue, - sortVersions, - }; -}; - -/** - * helper functions to ease create other versioning schemas with little code - * especially if those schemas do not support ranges - * @deprecated Use `GenericVersioningApi` instead - * @param param0 object with parse and optional compare function - * @returns - */ -export const create = ({ - parse, - compare, -}: { - parse: VersionParser; - compare: VersionComparator; -}): VersioningApi => { - let schema: VersioningApi = {} as any; - if (parse) { - schema = { ...schema, ...parser(parse) }; - } - if (compare) { - schema = { ...schema, ...comparer(compare) }; - } - return schema; -}; - export abstract class GenericVersioningApi< T extends GenericVersion = GenericVersion > implements VersioningApi diff --git a/lib/versioning/loose/index.ts b/lib/versioning/loose/index.ts index 32cba9301f3fb086570fdd755e3075d893b2004f..39d27d631b77f7253de73e4cecb87c26fa249bc2 100644 --- a/lib/versioning/loose/index.ts +++ b/lib/versioning/loose/index.ts @@ -1,6 +1,6 @@ import { regEx } from '../../util/regex'; import type { VersioningApi } from '../types'; -import * as generic from './generic'; +import { GenericVersion, GenericVersioningApi } from './generic'; export const id = 'loose'; export const displayName = 'Loose'; @@ -11,51 +11,50 @@ const versionPattern = regEx(/^v?(\d+(?:\.\d+)*)(.*)$/); const commitHashPattern = regEx(/^[a-f0-9]{7,40}$/); const numericPattern = regEx(/^[0-9]+$/); -function parse(version: string): any { - if (commitHashPattern.test(version) && !numericPattern.test(version)) { - return null; - } - const matches = versionPattern.exec(version); - if (!matches) { - return null; - } - const [, prefix, suffix] = matches; - const release = prefix.split('.').map(Number); - if (release.length > 6) { - return null; +class LooseVersioningApi extends GenericVersioningApi { + protected _parse(version: string): GenericVersion | null { + if (commitHashPattern.test(version) && !numericPattern.test(version)) { + return null; + } + const matches = versionPattern.exec(version); + if (!matches) { + return null; + } + const [, prefix, suffix] = matches; + const release = prefix.split('.').map(Number); + if (release.length > 6) { + return null; + } + return { release, suffix: suffix || '' }; } - return { release, suffix: suffix || '' }; -} -function compare(version1: string, version2: string): number { - const parsed1 = parse(version1); - const parsed2 = parse(version2); - // istanbul ignore if - if (!(parsed1 && parsed2)) { - return 1; - } - const length = Math.max(parsed1.release.length, parsed2.release.length); - for (let i = 0; i < length; i += 1) { - const part1 = parsed1.release[i]; - const part2 = parsed2.release[i]; - // shorter is smaller 2.1 < 2.1.0 - if (part1 === undefined) { - return -1; - } - if (part2 === undefined) { + protected override _compare(version: string, other: string): number { + const parsed1 = this._parse(version); + const parsed2 = this._parse(other); + // istanbul ignore if + if (!(parsed1 && parsed2)) { return 1; } - if (part1 !== part2) { - return part1 - part2; + const length = Math.max(parsed1.release.length, parsed2.release.length); + for (let i = 0; i < length; i += 1) { + const part1 = parsed1.release[i]; + const part2 = parsed2.release[i]; + // shorter is smaller 2.1 < 2.1.0 + if (part1 === undefined) { + return -1; + } + if (part2 === undefined) { + return 1; + } + if (part1 !== part2) { + return part1 - part2; + } } + // equals + return parsed1.suffix.localeCompare(parsed2.suffix); } - // equals - return parsed1.suffix.localeCompare(parsed2.suffix); } -export const api: VersioningApi = generic.create({ - parse, - compare, -}); +export const api: VersioningApi = new LooseVersioningApi(); export default api; diff --git a/lib/workers/repository/process/lookup/__snapshots__/index.spec.ts.snap b/lib/workers/repository/process/lookup/__snapshots__/index.spec.ts.snap index dd710b7fc1f2737a6f676d5d201951e1b55212ca..47c4490be5da2cfac8ba7055f0ceb701ca2aa6e8 100644 --- a/lib/workers/repository/process/lookup/__snapshots__/index.spec.ts.snap +++ b/lib/workers/repository/process/lookup/__snapshots__/index.spec.ts.snap @@ -175,12 +175,10 @@ Object { exports[`workers/repository/process/lookup/index .lookupUpdates() handles git submodule update 1`] = ` Object { - "currentVersion": undefined, "updates": Array [ Object { "newDigest": "4b825dc642cb6eb9a060e54bf8d69288fbee4904", "newValue": undefined, - "newVersion": undefined, "updateType": "digest", }, ],