Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions src/cli/commands/update/__tests__/update-action.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,30 @@ describe('compareVersions', () => {
it('handles versions with missing parts', () => {
expect(compareVersions('1.0', '1.0.0')).toBe(0);
});

it('returns 1 when latest pre-release is newer', () => {
expect(compareVersions('0.3.0-preview.1.0', '0.3.0-preview.2.0')).toBe(1);
});

it('returns -1 when current pre-release is newer', () => {
expect(compareVersions('0.3.0-preview.2.0', '0.3.0-preview.1.0')).toBe(-1);
});

it('returns 0 for equal pre-release versions', () => {
expect(compareVersions('0.3.0-preview.1.0', '0.3.0-preview.1.0')).toBe(0);
});

it('returns 1 when latest is release and current is pre-release', () => {
expect(compareVersions('1.0.0-preview.1', '1.0.0')).toBe(1);
});

it('returns -1 when current is release and latest is pre-release', () => {
expect(compareVersions('1.0.0', '1.0.0-preview.1')).toBe(-1);
});

it('compares pre-release labels lexicographically', () => {
expect(compareVersions('1.0.0-alpha.1', '1.0.0-beta.1')).toBe(1);
});
});

describe('fetchLatestVersion', () => {
Expand Down
49 changes: 43 additions & 6 deletions src/cli/commands/update/action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,52 @@ export async function fetchLatestVersion(): Promise<string> {
}

export function compareVersions(current: string, latest: string): number {
const currentParts = current.split('.').map(Number);
const latestParts = latest.split('.').map(Number);
const parse = (v: string) => {
const [core = '', ...prereleaseParts] = v.split('-');
const nums = core.split('.').map(Number);
const prerelease = prereleaseParts.join('-');
return { nums, prerelease };
};

const curr = parse(current);
const lat = parse(latest);

// Compare major.minor.patch
for (let i = 0; i < 3; i++) {
const curr = currentParts[i] ?? 0;
const lat = latestParts[i] ?? 0;
if (lat > curr) return 1;
if (lat < curr) return -1;
const c = curr.nums[i] ?? 0;
const l = lat.nums[i] ?? 0;
if (l > c) return 1;
if (l < c) return -1;
}

// Equal core versions — compare pre-release segments
if (!curr.prerelease && !lat.prerelease) return 0;
// A version without pre-release is greater than one with (1.0.0 > 1.0.0-preview)
if (!curr.prerelease) return -1;
if (!lat.prerelease) return 1;

const currSegments = curr.prerelease.split('.');
const latSegments = lat.prerelease.split('.');
const len = Math.max(currSegments.length, latSegments.length);

for (let i = 0; i < len; i++) {
const cs = currSegments[i];
const ls = latSegments[i];
if (cs === undefined) return 1; // fewer segments = earlier
if (ls === undefined) return -1;
const cn = Number(cs);
const ln = Number(ls);
// Both numeric — compare numerically
if (!isNaN(cn) && !isNaN(ln)) {
if (ln > cn) return 1;
if (ln < cn) return -1;
} else {
// Lexicographic comparison for non-numeric segments
if (ls > cs) return 1;
if (ls < cs) return -1;
}
}

return 0;
}

Expand Down
Loading