From dee60112668f7768c8c7ea7c1ffedcd2715e1f03 Mon Sep 17 00:00:00 2001 From: Jinjing <6427696+AmethystLiang@users.noreply.github.com> Date: Sat, 19 Sep 2026 12:15:21 -0700 Subject: [PATCH] Sort dev builds by timestamp instead of semver Dev build base versions can move backwards when a branch is cut before the latest main build. Their embedded timestamp is the authoritative "newest" signal for the picker. For dedicated release repos, compare publishedAt timestamps before falling back to semver comparison. --- src/shared/release-channel.test.ts | 20 ++++++++++++++++++++ src/shared/release-channel.ts | 25 ++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/shared/release-channel.test.ts b/src/shared/release-channel.test.ts index a2d66242d93..fb530d66347 100644 --- a/src/shared/release-channel.test.ts +++ b/src/shared/release-channel.test.ts @@ -324,4 +324,24 @@ describe('release channel', () => { '1.4.160-adhoc.20260728090000' ]) }) + + it('sorts dev builds by their cut time across different base versions', () => { + const build = (version: string): ReleaseBuild => ({ + tag: `v${version}`, + version, + channel: 'adhoc', + name: null, + publishedAt: null, + releaseUrl: `https://github.com/stablyai/orca-adhoc/releases/tag/v${version}`, + installerUrl: null + }) + const sorted = sortReleaseBuildsNewestFirst([ + build('1.4.207-adhoc.20260919025813'), + build('1.4.206-adhoc.20260919173504') + ]) + expect(sorted.map((entry) => entry.version)).toEqual([ + '1.4.206-adhoc.20260919173504', + '1.4.207-adhoc.20260919025813' + ]) + }) }) diff --git a/src/shared/release-channel.ts b/src/shared/release-channel.ts index 056152ffcc9..7a7f3aa96b0 100644 --- a/src/shared/release-channel.ts +++ b/src/shared/release-channel.ts @@ -319,5 +319,28 @@ export type ReleaseBuild = { /** Newest first, so the picker's first row is always the channel's current tip. */ export function sortReleaseBuildsNewestFirst(builds: ReleaseBuild[]): ReleaseBuild[] { - return [...builds].sort((left, right) => compareAppVersions(right.version, left.version)) + return [...builds].sort((left, right) => { + // Dev build base versions can move backwards when a branch was cut before + // the latest main build. Their stamped build time, not semver, is the + // meaningful "newest" signal for the picker. + const leftStamp = parseDevBuildStamp(left.version)?.getTime() ?? null + const rightStamp = parseDevBuildStamp(right.version)?.getTime() ?? null + if (leftStamp !== null && rightStamp !== null && leftStamp !== rightStamp) { + return rightStamp - leftStamp + } + + if (hasDedicatedReleaseRepo(left.channel) && hasDedicatedReleaseRepo(right.channel)) { + const leftPublished = left.publishedAt ? Date.parse(left.publishedAt) : Number.NaN + const rightPublished = right.publishedAt ? Date.parse(right.publishedAt) : Number.NaN + if ( + Number.isFinite(leftPublished) && + Number.isFinite(rightPublished) && + leftPublished !== rightPublished + ) { + return rightPublished - leftPublished + } + } + + return compareAppVersions(right.version, left.version) + }) }