diff --git a/ui/src/app/vite-config.node.test.ts b/ui/src/app/vite-config.node.test.ts index 53bf7ba252ff..2d3f20ecb1cb 100644 --- a/ui/src/app/vite-config.node.test.ts +++ b/ui/src/app/vite-config.node.test.ts @@ -78,26 +78,28 @@ describe("Control UI Vite config", () => { expect(readGitCommitTimestamp).toHaveBeenCalledWith("0123456789abcdef0123456789abcdef01234567"); }); - it("falls back to Git and the current UTC time only when inputs are absent", () => { - expect( - resolveControlUiBuildInfo({ - env: {}, - now: () => new Date("2026-07-10T13:14:15.000Z"), - readGitCommit: () => "a".repeat(40), - readGitCommitTimestamp: () => null, - readGitBranch: () => null, - readGitDirty: () => null, - readPackageVersion: () => null, - }), - ).toEqual({ + it("keeps source-build identity stable when no build timestamp is provided", () => { + const sources = { + env: {}, + readGitCommit: () => "a".repeat(40), + readGitCommitTimestamp: () => null, + readGitBranch: () => null, + readGitDirty: () => null, + readPackageVersion: () => null, + }; + const first = resolveControlUiBuildInfo(sources); + const second = resolveControlUiBuildInfo(sources); + + expect(first).toEqual(second); + expect(first).toEqual({ version: null, commit: "a".repeat(40), commitAt: null, - builtAt: "2026-07-10T13:14:15.000Z", + builtAt: null, branch: null, dirty: null, release: false, - buildId: "aaaaaaaaaaaa-2026-07-10T13-14-15.000Z", + buildId: "aaaaaaaaaaaa", }); }); @@ -182,7 +184,6 @@ describe("Control UI Vite config", () => { expect( resolveControlUiBuildInfo({ env: { GITHUB_SHA: "b".repeat(40) }, - now: () => new Date("2026-07-10T13:14:15.000Z"), readGitCommit, readPackageVersion: () => null, }), @@ -191,7 +192,6 @@ describe("Control UI Vite config", () => { expect( resolveControlUiBuildInfo({ env: { GITHUB_SHA: "b".repeat(40) }, - now: () => new Date("2026-07-10T13:14:15.000Z"), readGitCommit: () => null, readPackageVersion: () => null, }).commit, @@ -210,7 +210,6 @@ describe("Control UI Vite config", () => { expect( resolveControlUiBuildInfo({ env: { GIT_SHA: "A".repeat(40), GITHUB_SHA: "b".repeat(40) }, - now: () => new Date("2026-07-10T13:14:15.000Z"), readGitCommit, readPackageVersion: () => null, }).commit, diff --git a/ui/vite.config.ts b/ui/vite.config.ts index 9d1576b49d53..9a96b872c864 100644 --- a/ui/vite.config.ts +++ b/ui/vite.config.ts @@ -151,7 +151,6 @@ function readGitDirty(): boolean | null { type ControlUiBuildInfoSources = { env?: NodeJS.ProcessEnv; - now?: () => Date; readPackageVersion?: () => string | null; readGitCommit?: () => string | null; readGitCommitTimestamp?: (commit: string) => string | null; @@ -159,19 +158,16 @@ type ControlUiBuildInfoSources = { readGitDirty?: () => boolean | null; }; -function normalizeBuildTimestamp(value: string | undefined, now: () => Date): string | null { +function normalizeBuildTimestamp(value: string | undefined): string | null { const explicit = value?.trim(); - if (explicit) { - const timestamp = normalizeControlUiBuildInfo({ builtAt: explicit }).builtAt; - if (!timestamp) { - throw new Error( - "OPENCLAW_BUILD_TIMESTAMP must be a valid UTC ISO-8601 timestamp ending in Z", - ); - } - return timestamp; + if (!explicit) { + return null; } - const candidate = now(); - return Number.isNaN(candidate.getTime()) ? null : candidate.toISOString(); + const timestamp = normalizeControlUiBuildInfo({ builtAt: explicit }).builtAt; + if (!timestamp) { + throw new Error("OPENCLAW_BUILD_TIMESTAMP must be a valid UTC ISO-8601 timestamp ending in Z"); + } + return timestamp; } export function resolveControlUiBuildInfo( @@ -215,10 +211,7 @@ export function resolveControlUiBuildInfo( commitAt: readCommitTimestamp(commit), }).commitAt : null; - const builtAt = normalizeBuildTimestamp( - env.OPENCLAW_BUILD_TIMESTAMP, - sources.now ?? (() => new Date()), - ); + const builtAt = normalizeBuildTimestamp(env.OPENCLAW_BUILD_TIMESTAMP); // Branch/dirty identity is advisory: the readers return null instead of // throwing, so malformed environment or Git state never blocks a build. // Tags must not be presented as branches in GitHub-built artifacts.