From 5ad7df46e369e1d248baa412ffe8d0ddcab635a6 Mon Sep 17 00:00:00 2001 From: shahidbeig-a11y Date: Mon, 26 Oct 2026 19:47:32 +0100 Subject: [PATCH] fix(project): name the folder's project id when setup-existing-folder identity mismatches (#23551) When an imported or cloned folder's derived project id does not match the selected project, the error now names the id the folder resolves to (for example github:owner/repo) instead of a generic mismatch message. Host-local ids and identical ids keep the generic message. The setup-existing-folder help notes which ids --project accepts. Fixes #23528 Co-authored-by: Neil --- src/cli/specs/project.ts | 3 ++- .../ipc/repos/project-host-setup-handlers.ts | 2 +- .../runtime-project-host-setup-controller.ts | 2 +- ...project-host-setup-git-remote-link.test.ts | 2 +- .../imported-folder-project-identity.test.ts | 19 +++++++++++++++++-- .../imported-folder-project-identity.ts | 15 ++++++++++++--- 6 files changed, 34 insertions(+), 9 deletions(-) diff --git a/src/cli/specs/project.ts b/src/cli/specs/project.ts index f5e8cf3f492..2de579cc117 100644 --- a/src/cli/specs/project.ts +++ b/src/cli/specs/project.ts @@ -36,7 +36,8 @@ export const PROJECT_COMMAND_SPECS: CommandSpec[] = [ notes: [ 'For remote runtimes, --path must be an absolute path on the remote server.', '--host takes local, ssh:, runtime:, or the bare host name `orca host list` prints for any of them.', - 'SSH targets are set up through the desktop UI because the desktop client owns SSH connections.' + 'SSH targets are set up through the desktop UI because the desktop client owns SSH connections.', + "--project takes a project id or an existing project's name. A remote-backed Git folder's project id usually comes from its remote: github:owner/repo, github:/owner/repo for GitHub Enterprise, or git:/ for other remotes." ], examples: [ 'orca project setup-existing-folder --project github:stablyai/orca --host local --path ~/orca', diff --git a/src/main/ipc/repos/project-host-setup-handlers.ts b/src/main/ipc/repos/project-host-setup-handlers.ts index 37eeb012240..fc0a951e3bf 100644 --- a/src/main/ipc/repos/project-host-setup-handlers.ts +++ b/src/main/ipc/repos/project-host-setup-handlers.ts @@ -64,7 +64,7 @@ function alignRepoWithRequestedProject( const updated = store.updateRepo( repo, resolveImportedFolderProjectLink( - folderRemote, + { ...repo, ...folderRemote }, projectId, project?.providerIdentity ?? requestedProviderIdentity ) diff --git a/src/main/runtime/runtime-project-host-setup-controller.ts b/src/main/runtime/runtime-project-host-setup-controller.ts index 1b428e143e5..d7eb010b0b3 100644 --- a/src/main/runtime/runtime-project-host-setup-controller.ts +++ b/src/main/runtime/runtime-project-host-setup-controller.ts @@ -295,7 +295,7 @@ export class RuntimeProjectHostSetupController { const updated = store.updateRepo( repo, resolveImportedFolderProjectLink( - folderRemote, + { ...repo, ...folderRemote }, args.projectId, existingProject?.providerIdentity ?? args.projectProviderIdentity ) diff --git a/src/main/runtime/runtime-project-host-setup-git-remote-link.test.ts b/src/main/runtime/runtime-project-host-setup-git-remote-link.test.ts index c4e7bfca0ad..3699a5e0493 100644 --- a/src/main/runtime/runtime-project-host-setup-git-remote-link.test.ts +++ b/src/main/runtime/runtime-project-host-setup-git-remote-link.test.ts @@ -88,7 +88,7 @@ describe('RuntimeProjectHostSetupController with a Git-remote project', () => { url: 'https://gitlab.com/group/other.git', destination: '/tmp' }) - ).rejects.toThrow('Imported folder does not match the selected project identity.') + ).rejects.toThrow('Imported folder resolves to project "git:gitlab.com/group/other"') expect(repos).toHaveLength(0) }) }) diff --git a/src/shared/imported-folder-project-identity.test.ts b/src/shared/imported-folder-project-identity.test.ts index 5235653e94b..f6f467b16bd 100644 --- a/src/shared/imported-folder-project-identity.test.ts +++ b/src/shared/imported-folder-project-identity.test.ts @@ -5,6 +5,7 @@ import { getProjectIdentityKey } from './project-host-setup-projection' function folder(remoteUrl: string | null) { return { + id: 'imported-repo', gitRemoteIdentity: remoteUrl ? deriveGitRemoteIdentity(`origin\t${remoteUrl} (fetch)\norigin\t${remoteUrl} (push)\n`) : null @@ -13,7 +14,7 @@ function folder(remoteUrl: string | null) { /** The project a source host derives for a repo with this remote. */ function projectIdFor(remoteUrl: string): string { - return getProjectIdentityKey({ id: 'source-repo', ...folder(remoteUrl) }) + return getProjectIdentityKey(folder(remoteUrl)) } describe('resolveImportedFolderProjectLink', () => { @@ -55,7 +56,21 @@ describe('resolveImportedFolderProjectLink', () => { projectIdFor('https://gitlab.com/group/app.git'), undefined ) - ).toThrow('Imported folder does not match the selected project identity.') + ).toThrow( + 'Imported folder resolves to project "git:gitlab.com/group/other", not the selected project "git:gitlab.com/group/app".' + ) + }) + + it('names the GitHub project a folder resolves to when given a free-form project id', () => { + expect(() => + resolveImportedFolderProjectLink( + folder('https://github.com/owner/repo.git'), + 'my-project', + undefined + ) + ).toThrow( + 'Imported folder resolves to project "github:owner/repo", not the selected project "my-project".' + ) }) it('refuses a folder with no remote for a Git-remote project', () => { diff --git a/src/shared/imported-folder-project-identity.ts b/src/shared/imported-folder-project-identity.ts index 6eb31d73498..4de0955ea3f 100644 --- a/src/shared/imported-folder-project-identity.ts +++ b/src/shared/imported-folder-project-identity.ts @@ -1,7 +1,9 @@ import { GIT_REMOTE_PROJECT_ID_PREFIX, getGitRemoteProviderIdentity, - getProjectIdForProviderIdentity + getProjectIdForProviderIdentity, + getProjectIdentityKey, + isHostLocalProjectId } from './project-host-setup-projection' import type { ProjectProviderIdentity } from './project-types' import type { Repo } from './repo-types' @@ -31,7 +33,7 @@ export function assertImportedFolderRemoteMatchesProject( /** Supply missing project links: GitHub projects stamp upstream; remote-keyed projects record matching remotes not yet probed after cloning. */ export function resolveImportedFolderProjectLink( - folder: Pick, + folder: Pick, projectId: string, providerIdentity: ProjectProviderIdentity | undefined ): Pick | Pick { @@ -53,5 +55,12 @@ export function resolveImportedFolderProjectLink( ) { return { gitRemoteIdentity: remote } } - throw new Error('Imported folder does not match the selected project identity.') + const folderProjectId = getProjectIdentityKey(folder) + // Host-local or identical ids give the caller no better project to choose. + if (isHostLocalProjectId(folderProjectId) || folderProjectId === projectId) { + throw new Error('Imported folder does not match the selected project identity.') + } + throw new Error( + `Imported folder resolves to project "${folderProjectId}", not the selected project "${projectId}".` + ) }