From e0481f69b18ab06338211f8e9e12f8a571344e66 Mon Sep 17 00:00:00 2001 From: Chaim Lev-Ari Date: Sun, 26 Feb 2023 15:25:24 +0200 Subject: [PATCH] chore(deps): replace semver compare with local solutions [EE-5018] (#8491) --- .../common/UpdateScheduleDetailsFieldset.tsx | 2 +- .../update-schedules/common/utils.test.ts | 78 +++++++++++++++++++ .../update-schedules/common/utils.ts | 16 +++- .../queries/useSupportedAgentVersions.ts | 3 +- package.json | 2 - yarn.lock | 5 -- 6 files changed, 96 insertions(+), 10 deletions(-) create mode 100644 app/react/portainer/environments/update-schedules/common/utils.test.ts diff --git a/app/react/portainer/environments/update-schedules/common/UpdateScheduleDetailsFieldset.tsx b/app/react/portainer/environments/update-schedules/common/UpdateScheduleDetailsFieldset.tsx index 629107b2f..45d7dc465 100644 --- a/app/react/portainer/environments/update-schedules/common/UpdateScheduleDetailsFieldset.tsx +++ b/app/react/portainer/environments/update-schedules/common/UpdateScheduleDetailsFieldset.tsx @@ -1,4 +1,3 @@ -import semverCompare from 'semver-compare'; import _ from 'lodash'; import { Environment } from '@/react/portainer/environments/types'; @@ -7,6 +6,7 @@ import { TextTip } from '@@/Tip/TextTip'; import { VersionSelect } from './VersionSelect'; import { ScheduledTimeField } from './ScheduledTimeField'; +import { semverCompare } from './utils'; interface Props { environments: Environment[]; diff --git a/app/react/portainer/environments/update-schedules/common/utils.test.ts b/app/react/portainer/environments/update-schedules/common/utils.test.ts new file mode 100644 index 000000000..5f78b1826 --- /dev/null +++ b/app/react/portainer/environments/update-schedules/common/utils.test.ts @@ -0,0 +1,78 @@ +import { semverCompare } from './utils'; + +describe('semverCompare', () => { + test('sort array', () => { + const versions = [ + '1.2.3', + '4.11.6', + '4.2.0', + '1.5.19', + '1.5.5', + '4.1.3', + '2.3.1', + '10.5.5', + '11.3.0', + ]; + expect(versions.sort(semverCompare)).toStrictEqual([ + '1.2.3', + '1.5.5', + '1.5.19', + '2.3.1', + '4.1.3', + '4.2.0', + '4.11.6', + '10.5.5', + '11.3.0', + ]); + }); + + test('compare versions', () => { + // 1.0.0 < 2.0.0 < 2.1.0 < 2.1.1 + expect(semverCompare('1.0.0', '2.0.0')).toBe(-1); + expect(semverCompare('2.0.0', '2.1.0')).toBe(-1); + expect(semverCompare('2.1.0', '2.1.1')).toBe(-1); + + // 1.0.0-alpha < 1.0.0 + expect(semverCompare('1.0.0-alpha', '1.0.0')).toBe(-1); + + // 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0 + expect(semverCompare('1.0.0-alpha', '1.0.0-alpha.1')).toBe(-1); + expect(semverCompare('1.0.0-alpha.1', '1.0.0-alpha.beta')).toBe(-1); + expect(semverCompare('1.0.0-alpha.beta', '1.0.0-beta')).toBe(-1); + expect(semverCompare('1.0.0-beta', '1.0.0-beta.2')).toBe(-1); + expect(semverCompare('1.0.0-beta.2', '1.0.0-beta.11')).toBe(-1); + expect(semverCompare('1.0.0-beta.11', '1.0.0-rc.1')).toBe(-1); + expect(semverCompare('1.0.0-rc.1', '1.0.0')).toBe(-1); + + // Build metadata MUST be ignored when determining version precedence. + // expect(semverCompare("1.0.0", "=", "1.0.0+asdf") // ❌ exp: =, got: <).toBe(-1) + // expect(semverCompare("1.0.0+qwer", "=", "1.0.0+asdf") // ❌ exp: =, got: >).toBe(-1) + + // Workaround via `v.replace(/\+.*/, "")` + // expect(semverCompare("1.0.0", "=", "1.0.0+asdf".replace(/\+.*/, ""))).toBe(-1) + // expect(semverCompare("1.0.0+qwer".replace(/\+.*/, ""), "=", "1.0.0+asdf".replace(/\+.*/, ""))).toBe(-1) + + expect(semverCompare('0.0.0', '0.0.0-foo')).toBe(1); + expect(semverCompare('0.0.1', '0.0.0')).toBe(1); + expect(semverCompare('1.0.0', '0.9.9')).toBe(1); + expect(semverCompare('0.10.0', '0.9.0')).toBe(1); + expect(semverCompare('0.99.0', '0.10.0')).toBe(1); + expect(semverCompare('2.0.0', '1.2.3')).toBe(1); + expect(semverCompare('1.2.3', '1.2.3-asdf')).toBe(1); + expect(semverCompare('1.2.3', '1.2.3-4')).toBe(1); + expect(semverCompare('1.2.3', '1.2.3-4-foo')).toBe(1); + // expect(semverCompare("1.2.3-5-foo", ">", "1.2.3-5").toBe(1) // ❌ exp: >, got: <) + expect(semverCompare('1.2.3-5', '1.2.3-4')).toBe(1); + expect(semverCompare('1.2.3-5-foo', '1.2.3-5-Foo')).toBe(1); + expect(semverCompare('3.0.0', '2.7.2+asdf')).toBe(1); + expect(semverCompare('1.2.3-a.10', '1.2.3-a.5')).toBe(1); + expect(semverCompare('1.2.3-a.b', '1.2.3-a.5')).toBe(1); + expect(semverCompare('1.2.3-a.b', '1.2.3-a')).toBe(1); + expect(semverCompare('1.2.3-a.b.c.10.d.5', '1.2.3-a.b.c.5.d.100')).toBe(1); + // expect(semverCompare("1.2.3-r2", "1.2.3-r100").toBe(1) // ❌ exp: >, got: <) + expect(semverCompare('1.2.3-r100', '1.2.3-R2')).toBe(1); + + expect(semverCompare('1.0.0', '1.0.0')).toBe(0); + expect(semverCompare('1.2.3', '1.2.3')).toBe(0); + }); +}); diff --git a/app/react/portainer/environments/update-schedules/common/utils.ts b/app/react/portainer/environments/update-schedules/common/utils.ts index 954587210..32696ce1b 100644 --- a/app/react/portainer/environments/update-schedules/common/utils.ts +++ b/app/react/portainer/environments/update-schedules/common/utils.ts @@ -1,4 +1,18 @@ -import semverCompare from 'semver-compare'; +export function semverCompare(a: string, b: string) { + if (a.startsWith(`${b}-`)) { + return -1; + } + + if (b.startsWith(`${a}-`)) { + return 1; + } + + return a.localeCompare(b, undefined, { + numeric: true, + sensitivity: 'case', + caseFirst: 'upper', + }); +} export function compareVersion( currentVersion: string, diff --git a/app/react/portainer/environments/update-schedules/queries/useSupportedAgentVersions.ts b/app/react/portainer/environments/update-schedules/queries/useSupportedAgentVersions.ts index c8c9f3bb2..38cd616c3 100644 --- a/app/react/portainer/environments/update-schedules/queries/useSupportedAgentVersions.ts +++ b/app/react/portainer/environments/update-schedules/queries/useSupportedAgentVersions.ts @@ -1,9 +1,10 @@ import { useQuery } from 'react-query'; -import semverCompare from 'semver-compare'; import axios, { parseAxiosError } from '@/portainer/services/axios'; import { withError } from '@/react-tools/react-query'; +import { semverCompare } from '../common/utils'; + import { queryKeys } from './query-keys'; import { buildUrl } from './urls'; diff --git a/package.json b/package.json index 4a8801fdf..f2f330c32 100644 --- a/package.json +++ b/package.json @@ -143,7 +143,6 @@ "react-select": "^5.2.1", "react-table": "^7.7.0", "sanitize-html": "^2.8.1", - "semver-compare": "^1.0.0", "spinkit": "^2.0.1", "splitargs": "github:deviantony/splitargs#semver:~0.2.0", "strip-ansi": "^6.0.0", @@ -185,7 +184,6 @@ "@types/react-is": "^17.0.3", "@types/react-table": "^7.7.6", "@types/sanitize-html": "^2.8.0", - "@types/semver-compare": "^1.0.1", "@types/toastr": "^2.1.39", "@types/uuid": "^3.3.2", "@typescript-eslint/eslint-plugin": "^5.7.0", diff --git a/yarn.lock b/yarn.lock index 4f1686a51..9802e54b4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5021,11 +5021,6 @@ resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew== -"@types/semver-compare@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@types/semver-compare/-/semver-compare-1.0.1.tgz#17d1dc62c516c133ab01efb7803a537ee6eaf3d5" - integrity sha512-wx2LQVvKlEkhXp/HoKIZ/aSL+TvfJdKco8i0xJS3aR877mg4qBHzNT6+B5a61vewZHo79EdZavskGnRXEC2H6A== - "@types/serve-index@^1.9.1": version "1.9.1" resolved "https://registry.yarnpkg.com/@types/serve-index/-/serve-index-1.9.1.tgz#1b5e85370a192c01ec6cec4735cf2917337a6278"