diff --git a/apps/desktop/electron/updater/checkout-legacy.test.ts b/apps/desktop/electron/updater/checkout-legacy.test.ts index 1dec0fbb4b..30d2d672eb 100644 --- a/apps/desktop/electron/updater/checkout-legacy.test.ts +++ b/apps/desktop/electron/updater/checkout-legacy.test.ts @@ -7,7 +7,7 @@ import { expect, it, vi } from 'vitest' import { type CheckoutStrategyDeps, createCheckoutStrategy } from './checkout' import { readSourceUpdate, type SourceUpdate } from './checkout-source' -it('offers manual recovery only for a missing source probe, never for a broken probe', async (): Promise => { +it('moves a checkout without a source probe to main, but surfaces a broken probe', async (): Promise => { const root: string = fs.mkdtempSync(path.join(os.tmpdir(), 'legacy-channel-')) const home: string = path.join(root, 'profile') const modulePath: string = path.join(root, 'hermes_cli', 'source_check.py') @@ -31,7 +31,9 @@ it('offers manual recovery only for a missing source probe, never for a broken p defaultUpdateBranch: 'main', updateHandoffDwellMs: 0, resolveUpdateRoot: (): string => root, - resolveUpdaterBinary: vi.fn((): string => 'frozen-updater'), + // No staged updater and no repo hand-off script: apply ends on the manual + // command card, which exposes the target without spawning anything. + resolveUpdaterBinary: vi.fn((): null => null), remoteGatewayActive: (): boolean => false, emitUpdateProgress: vi.fn(), rememberLog: vi.fn(), @@ -54,14 +56,8 @@ it('offers manual recovery only for a missing source probe, never for a broken p fs.rmSync(modulePath) } - expect(await strategy.check()).toMatchObject({ supported: false, reason: 'source-probe-unavailable' }) - const result: Awaited> = await strategy.apply() - expect(result).toMatchObject({ manual: true, command: 'hermes update --help' }) - expect(result.message).toContain('branch or channel') - expect(result.command).not.toContain('--branch') - expect(deps.stopBackendsForUpdate).not.toHaveBeenCalled() - expect(deps.resolveUpdaterBinary).not.toHaveBeenCalled() - expect(deps.quit).not.toHaveBeenCalled() + expect(await strategy.check()).toMatchObject({ supported: true, updateAvailable: true, branch: 'main' }) + expect(await strategy.apply()).toMatchObject({ ok: true, manual: true, command: 'hermes update' }) } fs.writeFileSync(modulePath, 'def main():\n raise RuntimeError("invalid channel configuration")\n') diff --git a/apps/desktop/electron/updater/checkout-source.ts b/apps/desktop/electron/updater/checkout-source.ts index f571444160..e15c20ad1a 100644 --- a/apps/desktop/electron/updater/checkout-source.ts +++ b/apps/desktop/electron/updater/checkout-source.ts @@ -25,9 +25,6 @@ export interface SourceUpdateProbe { const execute: typeof execFile.__promisify__ = promisify(execFile) -export const SOURCE_PROBE_RECOVERY: string = - 'This checkout predates desktop source-channel checks. Run `hermes update --help` in this installation, then choose the intended branch or channel explicitly before updating.' - export function sourceUpdateEnvironment(updateRoot: string, hermesHome: string): NodeJS.ProcessEnv { const env: NodeJS.ProcessEnv = { ...process.env, diff --git a/apps/desktop/electron/updater/checkout.ts b/apps/desktop/electron/updater/checkout.ts index e85509b6d8..3e553c16bf 100644 --- a/apps/desktop/electron/updater/checkout.ts +++ b/apps/desktop/electron/updater/checkout.ts @@ -18,7 +18,7 @@ import { wrapHandoffForDetachedConsole } from '../updater-process' -import { SOURCE_PROBE_RECOVERY, type SourceUpdate, sourceUpdateEnvironment } from './checkout-source' +import { type SourceUpdate, sourceUpdateEnvironment } from './checkout-source' import type { UpdaterApplyResultWire, UpdaterMechanism, UpdaterStatusWire, UpdaterStrategy } from './index' @@ -77,10 +77,13 @@ export function createCheckoutStrategy(deps: CheckoutStrategyDeps): UpdaterStrat async function check(opts: { force?: boolean } = {}): Promise { const root: string = deps.resolveUpdateRoot() + // A checkout without the source probe predates source channels, so it can + // only be on the git line: move it to main. Its update pulls the probe in. const status: UpdaterStatusWire = (await deps.readSourceUpdate(root, opts)) ?? { - supported: false, - reason: 'source-probe-unavailable', - message: SOURCE_PROBE_RECOVERY, + supported: true, + updateAvailable: true, + behind: null, + branch: deps.defaultUpdateBranch, hermesRoot: root } @@ -101,16 +104,6 @@ export function createCheckoutStrategy(deps: CheckoutStrategyDeps): UpdaterStrat async function applyBody(): Promise { const status: UpdaterStatusWire = await check({ force: true }) - if (status.reason === 'source-probe-unavailable') { - return { - ok: true, - manual: true, - command: 'hermes update --help', - message: status.message, - hermesRoot: status.hermesRoot - } - } - if (!status.supported || status.error) { return { ok: false, error: status.error ?? status.reason, message: status.message } } diff --git a/website/docs/getting-started/updating.md b/website/docs/getting-started/updating.md index 0d4e14d021..f803730d28 100644 --- a/website/docs/getting-started/updating.md +++ b/website/docs/getting-started/updating.md @@ -106,9 +106,9 @@ replace a selected release channel with its default branch. For branch-tracking source installs, the desktop keeps the current named branch unless an explicit desktop branch override exists. A detached checkout uses the -default branch. Older checkouts without source-channel probing show manual -recovery instructions instead of guessing a target: inspect `hermes update --help` -in that installation and choose the intended branch or channel before updating. +default branch. Older checkouts without source-channel probing predate release +channels, so the desktop updates them from `main` over git; that update brings in +the probing. Packaged desktop feed channels derive from their build tag and package owner. Changing a source channel is not an MSIX or Store channel switch. Canary builds