fix: distinguish remote project paths by SSH connection (#5279)

Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
Jinwoo Hong 2026-06-12 13:40:51 -07:00 committed by GitHub
parent 011ea0db34
commit ebeb12fedc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 74 additions and 6 deletions

View File

@ -89,7 +89,7 @@ vi.mock('./filesystem-auth', () => ({
vi.mock('../providers/ssh-git-dispatch', () => ({
getSshGitProvider: vi.fn().mockImplementation((id: string) => {
if (id === 'conn-1') {
if (id === 'conn-1' || id === 'conn-2') {
return mockGitProvider
}
return undefined
@ -98,7 +98,7 @@ vi.mock('../providers/ssh-git-dispatch', () => ({
vi.mock('../providers/ssh-filesystem-dispatch', () => ({
getSshFilesystemProvider: vi.fn().mockImplementation((id: string) => {
if (id === 'conn-1') {
if (id === 'conn-1' || id === 'conn-2') {
return mockFilesystemProvider
}
return undefined
@ -107,7 +107,7 @@ vi.mock('../providers/ssh-filesystem-dispatch', () => ({
vi.mock('./ssh', () => ({
getActiveMultiplexer: vi.fn().mockImplementation((id: string) => {
if (id === 'conn-1') {
if (id === 'conn-1' || id === 'conn-2') {
return mockMultiplexer
}
return undefined
@ -886,6 +886,59 @@ describe('repos:addRemote', () => {
expect(mockStore.addRepo).not.toHaveBeenCalled()
})
it('allows the same resolved remote path on a different SSH connection', async () => {
const existing = {
id: 'machine-1-project',
path: '/home/user/project',
connectionId: 'conn-1',
displayName: 'project',
badgeColor: '#fff',
addedAt: 1000,
kind: 'git'
}
mockStore.getRepos.mockReturnValue([existing])
const result = await handlers.get('repos:addRemote')!(null, {
connectionId: 'conn-2',
remotePath: '/home/user/project'
})
expect(mockStore.addRepo).toHaveBeenCalledWith(
expect.objectContaining({
path: '/home/user/project',
connectionId: 'conn-2'
})
)
expect(result).toHaveProperty('repo.connectionId', 'conn-2')
expect(result).toHaveProperty('repo.id')
expect(result).not.toEqual({ repo: existing })
})
it('dedupes remote projects after git root resolution on the same SSH connection', async () => {
const existing = {
id: 'existing-id',
path: '/home/user/project',
connectionId: 'conn-1',
displayName: 'project',
badgeColor: '#fff',
addedAt: 1000,
kind: 'git'
}
mockStore.getRepos.mockReturnValue([existing])
mockGitProvider.isGitRepoAsync.mockResolvedValueOnce({
isRepo: true,
rootPath: '/home/user/project'
})
const result = await handlers.get('repos:addRemote')!(null, {
connectionId: 'conn-1',
remotePath: '/home/user/project/src'
})
expect(result).toEqual({ repo: existing })
expect(mockStore.addRepo).not.toHaveBeenCalled()
})
it('throws when SSH connection is not found', async () => {
const result = await handlers.get('repos:addRemote')!(null, {
connectionId: 'unknown-conn',

View File

@ -937,6 +937,16 @@ export function registerRepoHandlers(mainWindow: BrowserWindow, store: Store): v
return { error: `SSH connection "${args.connectionId}" not found or not connected` }
}
const findExistingRemoteRepo = (path: string): Repo | undefined =>
store
.getRepos()
.find(
(repo) =>
repo.connectionId === args.connectionId &&
normalizeRuntimePathForComparison(repo.path) ===
normalizeRuntimePathForComparison(path)
)
let repoKind: 'git' | 'folder' = args.kind ?? 'git'
let resolvedPath = args.remotePath
@ -959,9 +969,7 @@ export function registerRepoHandlers(mainWindow: BrowserWindow, store: Store): v
// Why: check for duplicates after tilde resolution so that adding `~/`
// when `/home/ubuntu` is already stored correctly detects the duplicate.
const existing = store
.getRepos()
.find((r) => r.connectionId === args.connectionId && r.path === resolvedPath)
const existing = findExistingRemoteRepo(resolvedPath)
if (existing) {
// Why: duplicate hit is suppressed by `emitRepoAdded` anyway, and for
// remote adds git-ness isn't resolved until the isGitRepoAsync check
@ -982,6 +990,13 @@ export function registerRepoHandlers(mainWindow: BrowserWindow, store: Store): v
if (check.rootPath) {
resolvedPath = check.rootPath
}
const existingAfterRootResolution = findExistingRemoteRepo(resolvedPath)
if (existingAfterRootResolution) {
// Why: users may browse inside a repo; store identity is the Git root,
// but different SSH targets can legitimately share that same path.
emitRepoAdded('folder_picker', true, true)
return { repo: existingAfterRootResolution }
}
} else {
return { error: `Not a valid git repository: ${args.remotePath}` }
}