fix(updates): compare prerelease versions correctly (#1220)

Co-authored-by: Isaac Sanchez <isanchez-hawkins@arize.com>
This commit is contained in:
Isaac Sanchez-Hawkins
2026-05-12 10:57:47 +03:00
committed by GitHub
co-authored by Isaac Sanchez
parent eb3f0e7f72
commit 0cfa37932e
2 changed files with 42 additions and 6 deletions
+23 -4
View File
@@ -655,16 +655,35 @@ export async function getLatestVersion() {
/**
* Compare semver-like version strings.
*/
function parseVersionForComparison(value) {
const normalized = String(value || '').replace(/^v/, '').split('+')[0];
const prereleaseIndex = normalized.indexOf('-');
const core = prereleaseIndex >= 0 ? normalized.slice(0, prereleaseIndex) : normalized;
const parts = core.split('.').map((part) => {
const parsed = Number.parseInt(part || '0', 10);
return Number.isFinite(parsed) ? parsed : 0;
});
return {
parts,
prerelease: prereleaseIndex >= 0,
};
}
function compareVersions(left, right) {
const a = String(left || '').replace(/^v/, '').split('.').map((part) => Number.parseInt(part || '0', 10));
const b = String(right || '').replace(/^v/, '').split('.').map((part) => Number.parseInt(part || '0', 10));
const length = Math.max(a.length, b.length);
const a = parseVersionForComparison(left);
const b = parseVersionForComparison(right);
const length = Math.max(a.parts.length, b.parts.length);
for (let index = 0; index < length; index += 1) {
const diff = (a[index] || 0) - (b[index] || 0);
const diff = (a.parts[index] || 0) - (b.parts[index] || 0);
if (diff !== 0) return diff;
}
if (a.prerelease !== b.prerelease) {
return a.prerelease ? -1 : 1;
}
return 0;
}
@@ -34,14 +34,16 @@ function createFetchMock() {
describe('checkForUpdates', () => {
let fetchMock;
let originalFetch;
beforeEach(() => {
fetchMock = createFetchMock();
vi.stubGlobal('fetch', fetchMock);
originalFetch = globalThis.fetch;
globalThis.fetch = fetchMock;
});
afterEach(() => {
vi.unstubAllGlobals();
globalThis.fetch = originalFetch;
});
// --- Scenario: API says update available, npm confirms ---
@@ -98,6 +100,21 @@ describe('checkForUpdates', () => {
expect(result.available).toBe(false);
});
it('returns available=false when npm only has a prerelease of the current version', async () => {
fetchMock
.when('api.openchamber.dev', Promise.reject(new Error('Network error')))
.when('registry.npmjs.org', {
ok: true,
json: async () => ({
'dist-tags': { latest: '1.10.0-beta.1' },
}),
});
const result = await checkForUpdates({ currentVersion: '1.10.0' });
expect(result.available).toBe(false);
});
it('does not cross-check desktop update claims against npm', async () => {
fetchMock
.when('api.openchamber.dev', {