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 <neil@stably.ai>
This commit is contained in:
shahidbeig-a11y
2026-10-26 11:47:32 -07:00
committed by GitHub
co-authored by Neil
parent e33400845c
commit 5ad7df46e3
6 changed files with 34 additions and 9 deletions
+2 -1
View File
@@ -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:<target-id>, runtime:<environment-id>, 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:<host>/owner/repo for GitHub Enterprise, or git:<host>/<path> for other remotes."
],
examples: [
'orca project setup-existing-folder --project github:stablyai/orca --host local --path ~/orca',
@@ -64,7 +64,7 @@ function alignRepoWithRequestedProject(
const updated = store.updateRepo(
repo,
resolveImportedFolderProjectLink(
folderRemote,
{ ...repo, ...folderRemote },
projectId,
project?.providerIdentity ?? requestedProviderIdentity
)
@@ -295,7 +295,7 @@ export class RuntimeProjectHostSetupController {
const updated = store.updateRepo(
repo,
resolveImportedFolderProjectLink(
folderRemote,
{ ...repo, ...folderRemote },
args.projectId,
existingProject?.providerIdentity ?? args.projectProviderIdentity
)
@@ -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)
})
})
@@ -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', () => {
+12 -3
View File
@@ -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<Repo, 'gitRemoteIdentity'>,
folder: Pick<Repo, 'id' | 'upstream' | 'repoIcon' | 'gitRemoteIdentity'>,
projectId: string,
providerIdentity: ProjectProviderIdentity | undefined
): Pick<Repo, 'upstream'> | Pick<Repo, 'gitRemoteIdentity'> {
@@ -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}".`
)
}