fix(desktop): report the current commit in updater status
This commit is contained in:
committed by
brooklyn!
parent
be84a141f6
commit
7dbbb0f4a6
101
apps/desktop/electron/updater/checkout-current-sha.test.ts
Normal file
101
apps/desktop/electron/updater/checkout-current-sha.test.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
import * as fs from 'node:fs'
|
||||
import * as os from 'node:os'
|
||||
import * as path from 'node:path'
|
||||
|
||||
import { expect, it, vi } from 'vitest'
|
||||
|
||||
import { type CheckoutStrategyDeps, createCheckoutStrategy, readStampedCommit } from './checkout'
|
||||
import type { SourceUpdate } from './checkout-source'
|
||||
|
||||
const STAMPED: string = 'a'.repeat(40)
|
||||
const PROBED: string = 'b'.repeat(40)
|
||||
|
||||
function writeStamp(root: string, commit: string): void {
|
||||
fs.writeFileSync(path.join(root, 'install-stamp.json'), JSON.stringify({ schemaVersion: 2, commit, source: 'git' }))
|
||||
}
|
||||
|
||||
function deps(root: string, status: SourceUpdate | null, isWindows: boolean): CheckoutStrategyDeps {
|
||||
return {
|
||||
readSourceUpdate: vi.fn(async (): Promise<SourceUpdate | null> => status),
|
||||
hermesHome: 'home',
|
||||
isWindows,
|
||||
isMac: process.platform === 'darwin',
|
||||
defaultUpdateBranch: 'main',
|
||||
updateHandoffDwellMs: 0,
|
||||
resolveUpdateRoot: (): string => root,
|
||||
resolveUpdaterBinary: vi.fn((): null => null),
|
||||
remoteGatewayActive: (): boolean => false,
|
||||
emitUpdateProgress: vi.fn(),
|
||||
rememberLog: vi.fn(),
|
||||
startHermes: vi.fn(async (): Promise<void> => {}),
|
||||
stopBackendsForUpdate: vi.fn(async (): Promise<void> => {}),
|
||||
repairMacUpdaterHelper: vi.fn(),
|
||||
preflightStateDb: vi.fn(),
|
||||
runningAppBundle: (): null => null,
|
||||
markQuittingForHandoff: vi.fn(),
|
||||
quit: vi.fn()
|
||||
}
|
||||
}
|
||||
|
||||
it.each([
|
||||
['windows-handoff', true],
|
||||
['posix-handoff', false]
|
||||
])(
|
||||
'%s reports the checkout commit even when the source probe never supplied one',
|
||||
async (_mechanism: string, isWindows: boolean): Promise<void> => {
|
||||
const root: string = fs.mkdtempSync(path.join(os.tmpdir(), 'checkout-sha-'))
|
||||
|
||||
try {
|
||||
// A probe-less checkout (the manual path: no hermes_cli/source_check.py)
|
||||
// previously reported no currentSha at all — the commit showed nowhere.
|
||||
writeStamp(root, STAMPED)
|
||||
const strategy: ReturnType<typeof createCheckoutStrategy> = createCheckoutStrategy(deps(root, null, isWindows))
|
||||
|
||||
expect(await strategy.check()).toMatchObject({
|
||||
supported: true,
|
||||
mechanism: isWindows ? 'windows-handoff' : 'posix-handoff',
|
||||
currentSha: STAMPED
|
||||
})
|
||||
} finally {
|
||||
fs.rmSync(root, { recursive: true, force: true })
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
it('fills currentSha from the install stamp when the probe omitted it, but never overrides the probe', async (): Promise<void> => {
|
||||
const root: string = fs.mkdtempSync(path.join(os.tmpdir(), 'checkout-sha-'))
|
||||
|
||||
try {
|
||||
writeStamp(root, STAMPED)
|
||||
|
||||
// A probe status that carries no currentSha (stale cache shapes, error
|
||||
// statuses) still leaves the badge with a commit to show.
|
||||
const bare: SourceUpdate = { supported: true, error: 'fetch-failed', behind: null }
|
||||
expect(await createCheckoutStrategy(deps(root, bare, false)).check()).toMatchObject({ currentSha: STAMPED })
|
||||
|
||||
// The probe's own answer — including its currentSha — always wins.
|
||||
const probed: SourceUpdate = { supported: true, currentSha: PROBED, behind: 0, updateAvailable: false }
|
||||
expect(await createCheckoutStrategy(deps(root, probed, false)).check()).toMatchObject({ currentSha: PROBED })
|
||||
} finally {
|
||||
fs.rmSync(root, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
it('readStampedCommit returns the stamped commit, or null for absent and malformed stamps', (): void => {
|
||||
const root: string = fs.mkdtempSync(path.join(os.tmpdir(), 'checkout-stamp-'))
|
||||
|
||||
try {
|
||||
expect(readStampedCommit(root)).toBeNull()
|
||||
|
||||
writeStamp(root, STAMPED)
|
||||
expect(readStampedCommit(root)).toBe(STAMPED)
|
||||
|
||||
fs.writeFileSync(path.join(root, 'install-stamp.json'), JSON.stringify({ commit: null, source: 'git' }))
|
||||
expect(readStampedCommit(root)).toBeNull()
|
||||
|
||||
fs.writeFileSync(path.join(root, 'install-stamp.json'), '{not json')
|
||||
expect(readStampedCommit(root)).toBeNull()
|
||||
} finally {
|
||||
fs.rmSync(root, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
@@ -1,6 +1,6 @@
|
||||
// Checkout update policy and handoff execution. The shell supplies process and UI dependencies.
|
||||
|
||||
import { existsSync } from 'node:fs'
|
||||
import { existsSync, readFileSync } from 'node:fs'
|
||||
import * as path from 'node:path'
|
||||
|
||||
import { updateHandoffConflict, writeUpdateMarker } from '../update-marker'
|
||||
@@ -66,6 +66,22 @@ export function buildManualUpdateCommand(currentBranch: string | null | undefine
|
||||
: 'hermes update'
|
||||
}
|
||||
|
||||
/**
|
||||
* The commit this checkout was installed at, from the install stamp the
|
||||
* updater already trusts (written by the CLI's completion tail next to the
|
||||
* checkout it attests). Read-only display data for `currentSha`; never a
|
||||
* git spawn and never an update input. Null when absent or malformed.
|
||||
*/
|
||||
export function readStampedCommit(root: string): string | null {
|
||||
try {
|
||||
const stamp = JSON.parse(readFileSync(path.join(root, 'install-stamp.json'), 'utf8')) as { commit?: unknown }
|
||||
|
||||
return typeof stamp.commit === 'string' && stamp.commit ? stamp.commit : null
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The checkout strategy: windows-handoff on win32, posix-handoff elsewhere.
|
||||
* The bodies are the production update flow; the mechanism stamp rides on
|
||||
@@ -89,6 +105,18 @@ export function createCheckoutStrategy(deps: CheckoutStrategyDeps): UpdaterStrat
|
||||
|
||||
status.mechanism = mechanism
|
||||
|
||||
// Display data only (#122727): the statusbar and command palette read
|
||||
// currentSha, but a probe-less checkout never supplied it. The install
|
||||
// stamp names the checkout's commit without a git spawn; never let it
|
||||
// override the probe and never let it influence the update decision.
|
||||
if (!status.currentSha) {
|
||||
const stamped: string | null = readStampedCommit(root)
|
||||
|
||||
if (stamped) {
|
||||
status.currentSha = stamped
|
||||
}
|
||||
}
|
||||
|
||||
return status
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user