From a7d93aefb78eb22f9801aa80a339015bb0a35433 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Thu, 20 Aug 2026 18:28:17 -0700 Subject: [PATCH] fix(repo): make the remote .git probe verdict a discriminated union Promise collapses: the literal is subsumed by string, so callers got no type help distinguishing 'could not look' from a real signature. The three outcomes are now structurally distinct. --- src/main/folder-repo-git-promotion.ts | 25 +++++++++++++++++++------ src/main/ipc/folder-repo-git-upgrade.ts | 10 +++------- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/main/folder-repo-git-promotion.ts b/src/main/folder-repo-git-promotion.ts index 2cf763af7de..5f56a8ca0b2 100644 --- a/src/main/folder-repo-git-promotion.ts +++ b/src/main/folder-repo-git-promotion.ts @@ -103,27 +103,40 @@ function isRemoteMissingPathError(error: unknown): boolean { return /\bENOENT\b|no such file|not found/i.test(message) } +/** + * Remote `.git` probe verdict. A bare `string | 'unverifiable'` union collapses — + * the literal is subsumed by `string`, so callers get no type help distinguishing + * "could not look" from a signature. Keep the three outcomes structurally distinct. + */ +export type RemoteGitMarkerSignature = + | { status: 'present'; signature: string } + | { status: 'absent' } + | { status: 'unverifiable' } + export async function readRemoteGitMarkerSignature( repo: Pick -): Promise { +): Promise { const connectionId = repo.connectionId if (!connectionId) { - return 'unverifiable' + return { status: 'unverifiable' } } const filesystem = getSshFilesystemProvider(connectionId) const git = getSshGitProvider(connectionId) const host = git?.getHostPlatform() if (!filesystem || !git || !host) { - return 'unverifiable' + return { status: 'unverifiable' } } try { const marker = await filesystem.stat(joinRemotePath(host, repo.path, '.git')) - return `${marker.mtimeMs ?? marker.mtime}:${marker.ino ?? 0}:${marker.type}` + return { + status: 'present', + signature: `${marker.mtimeMs ?? marker.mtime}:${marker.ino ?? 0}:${marker.type}` + } } catch (error) { if (isRemoteMissingPathError(error)) { - return null + return { status: 'absent' } } - return 'unverifiable' + return { status: 'unverifiable' } } } diff --git a/src/main/ipc/folder-repo-git-upgrade.ts b/src/main/ipc/folder-repo-git-upgrade.ts index 2f1a01c78eb..afa097a0946 100644 --- a/src/main/ipc/folder-repo-git-upgrade.ts +++ b/src/main/ipc/folder-repo-git-upgrade.ts @@ -152,16 +152,12 @@ async function pollOnce(watch: UpgradeWatch): Promise { const key = candidateKey(repo) liveKeys.add(key) if (isSshCandidate(repo)) { - const signature = await readRemoteGitMarkerSignature(repo) - if ( - signature === 'unverifiable' || - signature === null || - rejectedMarkers.get(key) === signature - ) { + const marker = await readRemoteGitMarkerSignature(repo) + if (marker.status !== 'present' || rejectedMarkers.get(key) === marker.signature) { continue } if ((await upgradeSshFolderRepo(watch, repo)) === 'rejected') { - rejectedMarkers.set(key, signature) + rejectedMarkers.set(key, marker.signature) } continue }