From a914f6863a9551ffe67ded69dc88b308fa873229 Mon Sep 17 00:00:00 2001 From: Will Brennan Date: Mon, 16 Sep 2024 15:10:03 +1000 Subject: [PATCH 1/9] feat(versioning): add devbox versioning module --- lib/modules/versioning/api.ts | 2 + lib/modules/versioning/devbox/index.spec.ts | 38 ++++++++++++++++ lib/modules/versioning/devbox/index.ts | 48 +++++++++++++++++++++ lib/modules/versioning/devbox/readme.md | 2 + 4 files changed, 90 insertions(+) create mode 100644 lib/modules/versioning/devbox/index.spec.ts create mode 100644 lib/modules/versioning/devbox/index.ts create mode 100644 lib/modules/versioning/devbox/readme.md diff --git a/lib/modules/versioning/api.ts b/lib/modules/versioning/api.ts index 764529bf40c69f..0f7c338d8a7ca7 100644 --- a/lib/modules/versioning/api.ts +++ b/lib/modules/versioning/api.ts @@ -6,6 +6,7 @@ import * as composer from './composer'; import * as conan from './conan'; import * as deb from './deb'; import * as debian from './debian'; +import * as devbox from './devbox'; import * as docker from './docker'; import * as git from './git'; import * as glasskube from './glasskube'; @@ -51,6 +52,7 @@ api.set(composer.id, composer.api); api.set(conan.id, conan.api); api.set(deb.id, deb.api); api.set(debian.id, debian.api); +api.set(devbox.id, devbox.api); api.set(docker.id, docker.api); api.set(git.id, git.api); api.set(glasskube.id, glasskube.api); diff --git a/lib/modules/versioning/devbox/index.spec.ts b/lib/modules/versioning/devbox/index.spec.ts new file mode 100644 index 00000000000000..3dea549c065d01 --- /dev/null +++ b/lib/modules/versioning/devbox/index.spec.ts @@ -0,0 +1,38 @@ +import devbox from '.'; + +describe('modules/versioning/devbox/index', () => { + it.each` + version | expected + ${'1'} | ${true} + ${'01'} | ${false} + ${'1.01'} | ${false} + ${'1.1'} | ${true} + ${'1.3.0'} | ${true} + ${'2.1.20'} | ${true} + `('isVersion("$version") === $expected', ({ version, expected }) => { + expect(!!devbox.isVersion(version)).toBe(expected); + }); + + it.each` + version | isValid + ${'v1.4'} | ${false} + ${'V0.5'} | ${false} + ${'3.5.0'} | ${true} + ${'4.2.21.Final'} | ${false} + ${'1234'} | ${true} + ${'foo'} | ${false} + ${'latest'} | ${false} + ${''} | ${false} + ${'3.5.0-beta.3'} | ${false} + ${'*'} | ${false} + ${'x'} | ${false} + ${'X'} | ${false} + ${'~1.2.3'} | ${false} + ${'>1.2.3'} | ${false} + ${'^1.2.3'} | ${false} + ${'1.2.3-foo'} | ${false} + ${'1.2.3foo'} | ${false} + `('isValid("$version") === $isValid', ({ version, isValid }) => { + expect(!!devbox.isValid(version)).toBe(isValid); + }); +}); diff --git a/lib/modules/versioning/devbox/index.ts b/lib/modules/versioning/devbox/index.ts new file mode 100644 index 00000000000000..e7be4e04936982 --- /dev/null +++ b/lib/modules/versioning/devbox/index.ts @@ -0,0 +1,48 @@ +import { regEx } from '../../../util/regex'; +import { GenericVersion, GenericVersioningApi } from '../generic'; +import type { VersioningApi } from '../types'; + +export const id = 'devbox'; +export const displayName = 'devbox'; + +export const supportsRanges = false; + +const versionPattern = regEx(/^((\d|[1-9]\d*)(\.(\d|[1-9]\d*)){0,2})$/); + +class DevboxVersioningApi extends GenericVersioningApi { + protected _parse(version: string): GenericVersion | null { + const matches = versionPattern.exec(version); + if (!matches) { + return null; + } + const release = matches[0].split('.').map(Number); + return { release }; + } + protected override _compare(version: string, other: string): number { + const parsed1 = this._parse(version); + const parsed2 = this._parse(other); + if (!(parsed1 && parsed2)) { + return 0; + } + 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; + } + } + return 0; + } +} + +export const api: VersioningApi = new DevboxVersioningApi(); + +export default api; diff --git a/lib/modules/versioning/devbox/readme.md b/lib/modules/versioning/devbox/readme.md new file mode 100644 index 00000000000000..6bd67019e96047 --- /dev/null +++ b/lib/modules/versioning/devbox/readme.md @@ -0,0 +1,2 @@ +Devbox's Nixhub uses fairly strict versioning, no ranges are permitted. +The semver values must not include "\*" or "x". "1.2.3" "1.2" and "1" are the only valid formats. From f55a7ae88dc08ce85cd243b80f26f5f4a118e906 Mon Sep 17 00:00:00 2001 From: Will Brennan Date: Tue, 17 Sep 2024 09:16:52 +1000 Subject: [PATCH 2/9] Add additional tests --- lib/modules/versioning/devbox/index.spec.ts | 6 ++++++ lib/modules/versioning/devbox/index.ts | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/modules/versioning/devbox/index.spec.ts b/lib/modules/versioning/devbox/index.spec.ts index 3dea549c065d01..cb517b0d14ae87 100644 --- a/lib/modules/versioning/devbox/index.spec.ts +++ b/lib/modules/versioning/devbox/index.spec.ts @@ -15,6 +15,12 @@ describe('modules/versioning/devbox/index', () => { it.each` version | isValid + ${'1'} | ${true} + ${'01'} | ${false} + ${'1.01'} | ${false} + ${'1.1'} | ${true} + ${'1.3.0'} | ${true} + ${'2.1.20'} | ${true} ${'v1.4'} | ${false} ${'V0.5'} | ${false} ${'3.5.0'} | ${true} diff --git a/lib/modules/versioning/devbox/index.ts b/lib/modules/versioning/devbox/index.ts index e7be4e04936982..1b9a0e2a940936 100644 --- a/lib/modules/versioning/devbox/index.ts +++ b/lib/modules/versioning/devbox/index.ts @@ -1,5 +1,6 @@ import { regEx } from '../../../util/regex'; -import { GenericVersion, GenericVersioningApi } from '../generic'; +import type { GenericVersion } from '../generic'; +import { GenericVersioningApi } from '../generic'; import type { VersioningApi } from '../types'; export const id = 'devbox'; From 7db0e3a3d9df7523efad7268d53c4c257bd8a175 Mon Sep 17 00:00:00 2001 From: Will Brennan Date: Thu, 19 Sep 2024 13:42:31 +1000 Subject: [PATCH 3/9] use versions as constraints like npm --- lib/modules/versioning/devbox/index.spec.ts | 50 +++++++++++++++++---- lib/modules/versioning/devbox/index.ts | 28 ++++++++---- 2 files changed, 62 insertions(+), 16 deletions(-) diff --git a/lib/modules/versioning/devbox/index.spec.ts b/lib/modules/versioning/devbox/index.spec.ts index cb517b0d14ae87..ac35318029e572 100644 --- a/lib/modules/versioning/devbox/index.spec.ts +++ b/lib/modules/versioning/devbox/index.spec.ts @@ -2,13 +2,30 @@ import devbox from '.'; describe('modules/versioning/devbox/index', () => { it.each` - version | expected - ${'1'} | ${true} - ${'01'} | ${false} - ${'1.01'} | ${false} - ${'1.1'} | ${true} - ${'1.3.0'} | ${true} - ${'2.1.20'} | ${true} + version | expected + ${'1'} | ${true} + ${'01'} | ${false} + ${'1.01'} | ${false} + ${'1.1'} | ${true} + ${'1.3.0'} | ${true} + ${'2.1.20'} | ${true} + ${'v1.4'} | ${false} + ${'V0.5'} | ${false} + ${'3.5.0'} | ${true} + ${'4.2.21.Final'} | ${false} + ${'1234'} | ${true} + ${'foo'} | ${false} + ${'latest'} | ${false} + ${''} | ${false} + ${'3.5.0-beta.3'} | ${false} + ${'*'} | ${false} + ${'x'} | ${false} + ${'X'} | ${false} + ${'~1.2.3'} | ${false} + ${'>1.2.3'} | ${false} + ${'^1.2.3'} | ${false} + ${'1.2.3-foo'} | ${false} + ${'1.2.3foo'} | ${false} `('isVersion("$version") === $expected', ({ version, expected }) => { expect(!!devbox.isVersion(version)).toBe(expected); }); @@ -27,7 +44,7 @@ describe('modules/versioning/devbox/index', () => { ${'4.2.21.Final'} | ${false} ${'1234'} | ${true} ${'foo'} | ${false} - ${'latest'} | ${false} + ${'latest'} | ${true} ${''} | ${false} ${'3.5.0-beta.3'} | ${false} ${'*'} | ${false} @@ -41,4 +58,21 @@ describe('modules/versioning/devbox/index', () => { `('isValid("$version") === $isValid', ({ version, isValid }) => { expect(!!devbox.isValid(version)).toBe(isValid); }); + + it.each` + version | range | expected + ${'1'} | ${'1'} | ${true} + ${'1'} | ${'0'} | ${false} + ${'1.2.3'} | ${'1'} | ${true} + ${'1.2'} | ${'1'} | ${true} + ${'1.0.0'} | ${'1'} | ${true} + ${'1.2.0'} | ${'1.2'} | ${true} + ${'1.2.3'} | ${'1.2'} | ${true} + ${'0'} | ${'latest'} | ${true} + `( + 'matches("$version", "$range") === $expected', + ({ version, range, expected }) => { + expect(devbox.matches(version, range)).toBe(expected); + }, + ); }); diff --git a/lib/modules/versioning/devbox/index.ts b/lib/modules/versioning/devbox/index.ts index 1b9a0e2a940936..f797172ec38ebc 100644 --- a/lib/modules/versioning/devbox/index.ts +++ b/lib/modules/versioning/devbox/index.ts @@ -19,24 +19,36 @@ class DevboxVersioningApi extends GenericVersioningApi { const release = matches[0].split('.').map(Number); return { release }; } + + override isValid(version: string): boolean { + if (version === 'latest') { + return true; + } + return this._parse(version) !== null; + } + + override isVersion(version: string): boolean { + if (version === 'latest') { + return false; + } + return this.isValid(version); + } + protected override _compare(version: string, other: string): number { const parsed1 = this._parse(version); const parsed2 = this._parse(other); if (!(parsed1 && parsed2)) { return 0; } + // support variable length compare const length = Math.max(parsed1.release.length, parsed2.release.length); for (let i = 0; i < length; i += 1) { + // 2.1 and 2.1.0 are equivalent 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) { + // if part1 or part2 is undefined, we should treat them as equal + // e.g. 1.0.0 === 1.0 + if (part1 !== undefined && part2 !== undefined && part1 !== part2) { return part1 - part2; } } From 864d1f014a0c92d1f0830e06db39638b6f42d67b Mon Sep 17 00:00:00 2001 From: Will Brennan Date: Mon, 30 Sep 2024 11:22:45 +1000 Subject: [PATCH 4/9] Update so values like 1 and 1.2 are constraints --- lib/modules/versioning/devbox/index.spec.ts | 6 +++--- lib/modules/versioning/devbox/index.ts | 8 +++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/modules/versioning/devbox/index.spec.ts b/lib/modules/versioning/devbox/index.spec.ts index ac35318029e572..fd06efc302886a 100644 --- a/lib/modules/versioning/devbox/index.spec.ts +++ b/lib/modules/versioning/devbox/index.spec.ts @@ -3,17 +3,17 @@ import devbox from '.'; describe('modules/versioning/devbox/index', () => { it.each` version | expected - ${'1'} | ${true} + ${'1'} | ${false} ${'01'} | ${false} ${'1.01'} | ${false} - ${'1.1'} | ${true} + ${'1.1'} | ${false} ${'1.3.0'} | ${true} ${'2.1.20'} | ${true} ${'v1.4'} | ${false} ${'V0.5'} | ${false} ${'3.5.0'} | ${true} ${'4.2.21.Final'} | ${false} - ${'1234'} | ${true} + ${'1234'} | ${false} ${'foo'} | ${false} ${'latest'} | ${false} ${''} | ${false} diff --git a/lib/modules/versioning/devbox/index.ts b/lib/modules/versioning/devbox/index.ts index f797172ec38ebc..d2e14f65ef1078 100644 --- a/lib/modules/versioning/devbox/index.ts +++ b/lib/modules/versioning/devbox/index.ts @@ -8,11 +8,12 @@ export const displayName = 'devbox'; export const supportsRanges = false; -const versionPattern = regEx(/^((\d|[1-9]\d*)(\.(\d|[1-9]\d*)){0,2})$/); +const validPattern = regEx(/^((\d|[1-9]\d*)(\.(\d|[1-9]\d*)){0,2})$/); +const versionPattern = regEx(/^((\d|[1-9]\d*)(\.(\d|[1-9]\d*)){2})$/); class DevboxVersioningApi extends GenericVersioningApi { protected _parse(version: string): GenericVersion | null { - const matches = versionPattern.exec(version); + const matches = validPattern.exec(version); if (!matches) { return null; } @@ -31,7 +32,8 @@ class DevboxVersioningApi extends GenericVersioningApi { if (version === 'latest') { return false; } - return this.isValid(version); + const matches = versionPattern.exec(version); + return !!matches; } protected override _compare(version: string, other: string): number { From 0bb4f2128d24ffbb3417966b7feb026921ff1388 Mon Sep 17 00:00:00 2001 From: Will Brennan Date: Wed, 2 Oct 2024 08:48:59 +1000 Subject: [PATCH 5/9] Add check in matches for valid versions --- lib/modules/versioning/devbox/index.spec.ts | 6 +++--- lib/modules/versioning/devbox/index.ts | 4 ++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/modules/versioning/devbox/index.spec.ts b/lib/modules/versioning/devbox/index.spec.ts index fd06efc302886a..7068d7bd49913f 100644 --- a/lib/modules/versioning/devbox/index.spec.ts +++ b/lib/modules/versioning/devbox/index.spec.ts @@ -61,14 +61,14 @@ describe('modules/versioning/devbox/index', () => { it.each` version | range | expected - ${'1'} | ${'1'} | ${true} + ${'1'} | ${'1'} | ${false} ${'1'} | ${'0'} | ${false} ${'1.2.3'} | ${'1'} | ${true} - ${'1.2'} | ${'1'} | ${true} + ${'1.2'} | ${'1'} | ${false} ${'1.0.0'} | ${'1'} | ${true} ${'1.2.0'} | ${'1.2'} | ${true} ${'1.2.3'} | ${'1.2'} | ${true} - ${'0'} | ${'latest'} | ${true} + ${'0'} | ${'latest'} | ${false} `( 'matches("$version", "$range") === $expected', ({ version, range, expected }) => { diff --git a/lib/modules/versioning/devbox/index.ts b/lib/modules/versioning/devbox/index.ts index d2e14f65ef1078..75d301548ab630 100644 --- a/lib/modules/versioning/devbox/index.ts +++ b/lib/modules/versioning/devbox/index.ts @@ -36,6 +36,10 @@ class DevboxVersioningApi extends GenericVersioningApi { return !!matches; } + override matches(version: string, range: string): boolean { + return this.isVersion(version) && this.equals(version, range); + } + protected override _compare(version: string, other: string): number { const parsed1 = this._parse(version); const parsed2 = this._parse(other); From d21a240dbf18c0c60778540709edc98d291b4fc2 Mon Sep 17 00:00:00 2001 From: Will Brennan Date: Fri, 4 Oct 2024 14:36:41 +1000 Subject: [PATCH 6/9] Treat latest as *, always return equal --- lib/modules/versioning/devbox/index.spec.ts | 1 + lib/modules/versioning/devbox/index.ts | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/modules/versioning/devbox/index.spec.ts b/lib/modules/versioning/devbox/index.spec.ts index 7068d7bd49913f..a9ba6542f900d7 100644 --- a/lib/modules/versioning/devbox/index.spec.ts +++ b/lib/modules/versioning/devbox/index.spec.ts @@ -69,6 +69,7 @@ describe('modules/versioning/devbox/index', () => { ${'1.2.0'} | ${'1.2'} | ${true} ${'1.2.3'} | ${'1.2'} | ${true} ${'0'} | ${'latest'} | ${false} + ${'1.2.3'} | ${'latest'} | ${true} `( 'matches("$version", "$range") === $expected', ({ version, range, expected }) => { diff --git a/lib/modules/versioning/devbox/index.ts b/lib/modules/versioning/devbox/index.ts index 75d301548ab630..b15ad720584ace 100644 --- a/lib/modules/versioning/devbox/index.ts +++ b/lib/modules/versioning/devbox/index.ts @@ -43,9 +43,13 @@ class DevboxVersioningApi extends GenericVersioningApi { protected override _compare(version: string, other: string): number { const parsed1 = this._parse(version); const parsed2 = this._parse(other); - if (!(parsed1 && parsed2)) { + // Treat "latest" as * and always return equal + if (other === 'latest' && parsed1) { return 0; } + if (!(parsed1 && parsed2)) { + return 1; + } // support variable length compare const length = Math.max(parsed1.release.length, parsed2.release.length); for (let i = 0; i < length; i += 1) { From f3d4a16c22674a3a31d79e6089ee373a49bd7730 Mon Sep 17 00:00:00 2001 From: Will Brennan Date: Fri, 4 Oct 2024 15:17:06 +1000 Subject: [PATCH 7/9] Update readme --- lib/modules/versioning/devbox/readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/modules/versioning/devbox/readme.md b/lib/modules/versioning/devbox/readme.md index 6bd67019e96047..e7eff534d5cfef 100644 --- a/lib/modules/versioning/devbox/readme.md +++ b/lib/modules/versioning/devbox/readme.md @@ -1,2 +1,2 @@ -Devbox's Nixhub uses fairly strict versioning, no ranges are permitted. +Devbox's Nixhub uses fairly strict versioning, characters such as ~, ^ and >= are not allowed. The semver values must not include "\*" or "x". "1.2.3" "1.2" and "1" are the only valid formats. From 289539f45f23fa76e57dbd935345bb865710b9cd Mon Sep 17 00:00:00 2001 From: Will Brennan Date: Mon, 14 Oct 2024 11:45:35 +1100 Subject: [PATCH 8/9] Add required urls export --- lib/modules/versioning/devbox/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/modules/versioning/devbox/index.ts b/lib/modules/versioning/devbox/index.ts index b15ad720584ace..d3e88780c539c8 100644 --- a/lib/modules/versioning/devbox/index.ts +++ b/lib/modules/versioning/devbox/index.ts @@ -5,7 +5,7 @@ import type { VersioningApi } from '../types'; export const id = 'devbox'; export const displayName = 'devbox'; - +export const urls = []; export const supportsRanges = false; const validPattern = regEx(/^((\d|[1-9]\d*)(\.(\d|[1-9]\d*)){0,2})$/); From a4b7b95f7fc8644fd90b3a1f0ecea825d57017d1 Mon Sep 17 00:00:00 2001 From: Will Brennan Date: Tue, 17 Dec 2024 09:58:06 +1100 Subject: [PATCH 9/9] Fix code coverage issue --- lib/modules/versioning/devbox/index.spec.ts | 44 ++++++++++++++++----- lib/modules/versioning/devbox/index.ts | 3 ++ 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/lib/modules/versioning/devbox/index.spec.ts b/lib/modules/versioning/devbox/index.spec.ts index a9ba6542f900d7..542a28eeed6a4d 100644 --- a/lib/modules/versioning/devbox/index.spec.ts +++ b/lib/modules/versioning/devbox/index.spec.ts @@ -60,20 +60,44 @@ describe('modules/versioning/devbox/index', () => { }); it.each` - version | range | expected - ${'1'} | ${'1'} | ${false} - ${'1'} | ${'0'} | ${false} - ${'1.2.3'} | ${'1'} | ${true} - ${'1.2'} | ${'1'} | ${false} - ${'1.0.0'} | ${'1'} | ${true} - ${'1.2.0'} | ${'1.2'} | ${true} - ${'1.2.3'} | ${'1.2'} | ${true} - ${'0'} | ${'latest'} | ${false} - ${'1.2.3'} | ${'latest'} | ${true} + version | range | expected + ${'1'} | ${'1'} | ${false} + ${'1'} | ${'0'} | ${false} + ${'1.2.3'} | ${'1'} | ${true} + ${'1.2'} | ${'1'} | ${false} + ${'1.0.0'} | ${'1'} | ${true} + ${'1.2.0'} | ${'1.2'} | ${true} + ${'1.2.3'} | ${'1.2'} | ${true} + ${'0'} | ${'latest'} | ${false} + ${'1.2.3'} | ${'latest'} | ${true} + ${'1.2.3.5'} | ${'1.2.3.5'} | ${false} + ${'1.2'} | ${'1.2.3'} | ${false} `( 'matches("$version", "$range") === $expected', ({ version, range, expected }) => { expect(devbox.matches(version, range)).toBe(expected); }, ); + + it.each` + version | range | expected + ${'1'} | ${'1'} | ${true} + ${'1'} | ${'0'} | ${false} + ${'1.2.3'} | ${'1'} | ${true} + ${'1.2'} | ${'1'} | ${true} + ${'1.0.0'} | ${'1'} | ${true} + ${'1.2.0'} | ${'1.2'} | ${true} + ${'1.2.3'} | ${'1.2'} | ${true} + ${'0'} | ${'latest'} | ${true} + ${'1.2.3'} | ${'latest'} | ${true} + ${'1.2.3.5'} | ${'1.2.3.5'} | ${false} + ${'latest'} | ${'latest'} | ${false} + ${'latest'} | ${'1.2.3'} | ${false} + ${'1.2'} | ${'1.2.3'} | ${true} + `( + 'equals("$version", "$range") === $expected', + ({ version, range, expected }) => { + expect(devbox.equals(version, range)).toBe(expected); + }, + ); }); diff --git a/lib/modules/versioning/devbox/index.ts b/lib/modules/versioning/devbox/index.ts index d3e88780c539c8..153f01f4f4de30 100644 --- a/lib/modules/versioning/devbox/index.ts +++ b/lib/modules/versioning/devbox/index.ts @@ -43,10 +43,13 @@ class DevboxVersioningApi extends GenericVersioningApi { protected override _compare(version: string, other: string): number { const parsed1 = this._parse(version); const parsed2 = this._parse(other); + // Treat "latest" as * and always return equal if (other === 'latest' && parsed1) { return 0; } + + // If either version is invalid, return unequal if (!(parsed1 && parsed2)) { return 1; }