mirror of
https://github.com/stablyai/orca.git
synced 2026-10-20 08:03:28 +00:00
fix(worktrees): preserve folder PTY owner scope
This commit is contained in:
@@ -8380,6 +8380,8 @@ describe('registerWorktreeHandlers', () => {
|
||||
connectionId: 'conn-1'
|
||||
})
|
||||
getSshPtyProviderMock.mockReturnValue(sshPtyProvider)
|
||||
// One global meta key can describe the same-id local copy; the resolved repo still owns this delete.
|
||||
store.getWorktreeMeta.mockReturnValue(makeWorktreeMeta({ hostId: 'local' }))
|
||||
|
||||
await handlers['worktrees:remove'](null, { worktreeId })
|
||||
|
||||
@@ -8395,6 +8397,37 @@ describe('registerWorktreeHandlers', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('fences a mirrored runtime folder workspace sweep to its environment', async () => {
|
||||
const runtimePtyProvider = {} as never
|
||||
const worktreeId = 'repo-folder::/runtime/folder::workspace:child-1'
|
||||
store.getRepo.mockReturnValue({
|
||||
id: 'repo-folder',
|
||||
path: '/runtime/folder',
|
||||
displayName: 'folder',
|
||||
badgeColor: '#000',
|
||||
addedAt: 0,
|
||||
kind: 'folder',
|
||||
executionHostId: 'runtime:env-1'
|
||||
})
|
||||
getLocalPtyProviderMock.mockReturnValue(runtimePtyProvider)
|
||||
|
||||
await handlers['worktrees:remove'](null, {
|
||||
worktreeId,
|
||||
hostId: 'runtime:env-1'
|
||||
})
|
||||
|
||||
expect(killAllProcessesForWorktreeMock).toHaveBeenCalledWith(worktreeId, {
|
||||
runtime: runtimeStub,
|
||||
resolvedWorktreeId: worktreeId,
|
||||
resolvedRuntimeEnvironmentId: 'env-1',
|
||||
localProvider: runtimePtyProvider,
|
||||
onPtyStopped: clearProviderPtyStateMock,
|
||||
includeProviderInventory: false,
|
||||
includeLocalRegistry: false
|
||||
})
|
||||
expect(getSshPtyProviderMock).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('runs the archive hook on remove when skipArchive is not set', async () => {
|
||||
mockKnownFeatureWorktree()
|
||||
removeWorktreeMock.mockResolvedValue(undefined)
|
||||
|
||||
@@ -2297,9 +2297,9 @@ export function registerWorktreeHandlers(
|
||||
await withWorktreeRemoveStageSpan('pty_sweep', 'folder', async () => {
|
||||
// Folder projects can be SSH-backed, so fence the sweep to the owning host exactly
|
||||
// like the git paths — the local inventory must never reach a remote workspace's id.
|
||||
const ownerHost = parseExecutionHostId(
|
||||
resolveWorktreeRemovalOwnerHostId(store, args.worktreeId, repo, args.hostId)
|
||||
)
|
||||
// The resolved repo is authoritative here: path-derived metadata is shared by
|
||||
// same-id host copies and can describe a different owner's workspace.
|
||||
const ownerHost = parseExecutionHostId(removalHostId)
|
||||
const sshPtyProvider =
|
||||
ownerHost?.kind === 'ssh' ? getSshPtyProvider(ownerHost.targetId) : undefined
|
||||
const externalHost = ownerHost?.kind === 'ssh' || ownerHost?.kind === 'runtime'
|
||||
|
||||
@@ -3943,7 +3943,9 @@ describe('OrcaRuntimeService', () => {
|
||||
displayName: 'Folder',
|
||||
badgeColor: 'blue',
|
||||
addedAt: 1,
|
||||
kind: 'folder' as const
|
||||
kind: 'folder' as const,
|
||||
// removeManagedWorktree executes inside this selected runtime, where PTYs are local ids.
|
||||
executionHostId: 'runtime:env-1' as const
|
||||
}
|
||||
const rootWorktreeId = 'folder-repo::/workspace/folder'
|
||||
const rootPriorWorktreeIds = ['folder-repo::/workspace/old-folder']
|
||||
|
||||
@@ -23511,31 +23511,25 @@ export class OrcaRuntimeService {
|
||||
'Cannot delete the project root workspace. Remove the folder project instead.'
|
||||
)
|
||||
}
|
||||
// Folder projects can be SSH-backed, so resolve the owner before sweeping.
|
||||
const folderHost = parseExecutionHostId(
|
||||
store.getWorktreeMeta(removalTarget.id)?.hostId ?? getRepoExecutionHostId(repo)
|
||||
)
|
||||
const folderSshPtyProvider =
|
||||
folderHost?.kind === 'ssh' ? this.getSshProviderFn?.(folderHost.targetId) : undefined
|
||||
const externalFolderHost = folderHost?.kind === 'ssh' || folderHost?.kind === 'runtime'
|
||||
// This service runs inside the selected runtime, so runtime-stamped repos use its
|
||||
// local PTY namespace; only a direct SSH connection is external from here.
|
||||
const folderConnectionId = repo.connectionId?.trim() || null
|
||||
const folderSshPtyProvider = folderConnectionId
|
||||
? this.getSshProviderFn?.(folderConnectionId)
|
||||
: undefined
|
||||
const folderPtyProvider = folderSshPtyProvider ?? this.getLocalProvider()
|
||||
if (folderPtyProvider) {
|
||||
// Why: folder workspace deletion has no Git removal phase where PTYs
|
||||
// would otherwise be swept; tear them down before hiding the workspace.
|
||||
await killAllProcessesForWorktree(removalTarget.id, {
|
||||
runtime: this,
|
||||
// External host inventories must never sweep a same-id local workspace.
|
||||
resolvedWorktreeId: removalTarget.id,
|
||||
...(folderHost?.kind === 'ssh' ? { resolvedConnectionId: folderHost.targetId } : {}),
|
||||
...(folderHost?.kind === 'runtime'
|
||||
? { resolvedRuntimeEnvironmentId: folderHost.environmentId }
|
||||
: {}),
|
||||
...(folderConnectionId ? { resolvedConnectionId: folderConnectionId } : {}),
|
||||
localProvider: folderPtyProvider,
|
||||
onPtyStopped: this.onPtyStopped ?? undefined,
|
||||
...(externalFolderHost
|
||||
...(folderConnectionId
|
||||
? {
|
||||
includeProviderInventory:
|
||||
folderHost?.kind === 'ssh' && Boolean(folderSshPtyProvider),
|
||||
includeProviderInventory: Boolean(folderSshPtyProvider),
|
||||
includeLocalRegistry: false
|
||||
}
|
||||
: {})
|
||||
|
||||
Reference in New Issue
Block a user