mirror of
https://github.com/stablyai/orca.git
synced 2026-11-02 01:57:57 +00:00
* feat(wsl): run Git for Windows for a WSL project on a Windows drive A project on C:\ whose runtime is WSL ran every Orca Git command through wsl.exe on /mnt/c: ~300x slower than Windows Git, and blind to Windows-side edits. The project's WSL runtime preference gains an optional `git: 'windows-host'`. With it, Orca's Git (and where new worktrees go) is Windows; terminals, agents, accounts and hooks still resolve to WSL, so nothing else changes. Older builds drop the field and go back to WSL Git. The conversion checks first, then the caller saves the returned preference through the normal project update: - Git for Windows must exist. - New worktrees must not be set to go inside the distro. - No linked worktree may have a Linux-path link. WSL Git made those; Windows Git sees them as prunable, and Orca's prune after a worktree removal would delete their metadata. - Optionally writes core.autocrlf=true into the repo when Windows Git uses it, so WSL Git in the project's terminals stops seeing CRLF changes. Never core.checkStat. IPC is refused off Windows and with the internal WSL hosts gate closed. Co-Authored-By: Claude <noreply@anthropic.com> * test(wsl): mock the drive-project IPC in the core handler registration test Co-Authored-By: Claude <noreply@anthropic.com> * test(wsl): pin WSL Git for a drive project that never chose Windows Git, and record which Git ran * fix(wsl): base-ref lookups and the remote-identity sweep run Git where the project's runtime does * fix(wsl): route base-ref and remote-identity reads by runtime only with WSL hosts on These reads ran host Git on main. Routing them into a WSL-runtime project's distro is part of D1c, so with the WSL hosts gate closed they run host Git exactly as before. (cherry picked from commit 2ed82987dc5b82c3a86ea7e03ce05e9efc3e77fb) * refactor(ipc): register the filesystem handlers in one call, keeping core registration under its line cap The optional commit-message resolvers pass straight through, so the call is the same with or without them. #34059 left this file at the 300-line cap and the WSL drive-project wiring adds to it. Co-Authored-By: Claude <noreply@anthropic.com> * fix(wsl): skip folder repos in the Windows Git check, and read worktree links with the prune guard's reader Folder repos run no Git, so rev-parse on them always failed the preview. The link reader and the WSL-authored test now come from worktree-prune-guard.ts; the copy's startsWith('/') wrongly refused //wsl.localhost links, which Windows Git writes itself. Co-Authored-By: Claude <noreply@anthropic.com> * fix(wsl): the remote-identity sweep checks the distro a drive project's Git runs in, so it never boots a stopped one --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
364 lines
11 KiB
TypeScript
364 lines
11 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import {
|
|
deriveGlobalWindowsRuntimeDefaultFromLegacySettings,
|
|
normalizeProjectRuntimePreference,
|
|
resolveProjectExecutionRuntime
|
|
} from './project-execution-runtime'
|
|
|
|
describe('normalizeProjectRuntimePreference', () => {
|
|
it('preserves valid project runtime preferences', () => {
|
|
expect(normalizeProjectRuntimePreference({ kind: 'inherit-global' })).toEqual({
|
|
kind: 'inherit-global'
|
|
})
|
|
expect(normalizeProjectRuntimePreference({ kind: 'windows-host' })).toEqual({
|
|
kind: 'windows-host'
|
|
})
|
|
expect(normalizeProjectRuntimePreference({ kind: 'wsl', distro: 'Ubuntu-24.04' })).toEqual({
|
|
kind: 'wsl',
|
|
distro: 'Ubuntu-24.04'
|
|
})
|
|
})
|
|
|
|
it('keeps Windows Git on a WSL preference and drops any other Git value', () => {
|
|
expect(
|
|
normalizeProjectRuntimePreference({ kind: 'wsl', distro: 'Ubuntu', git: 'windows-host' })
|
|
).toEqual({ kind: 'wsl', distro: 'Ubuntu', git: 'windows-host' })
|
|
expect(
|
|
normalizeProjectRuntimePreference({ kind: 'wsl', distro: 'Ubuntu', git: 'wsl' })
|
|
).toEqual({ kind: 'wsl', distro: 'Ubuntu' })
|
|
})
|
|
|
|
it('falls back malformed project runtime preferences to inherit-global', () => {
|
|
expect(normalizeProjectRuntimePreference(null)).toEqual({ kind: 'inherit-global' })
|
|
expect(normalizeProjectRuntimePreference({ kind: 'wsl', distro: ' ' })).toEqual({
|
|
kind: 'inherit-global'
|
|
})
|
|
expect(normalizeProjectRuntimePreference({ kind: 'bogus', distro: 'Ubuntu' })).toEqual({
|
|
kind: 'inherit-global'
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('deriveGlobalWindowsRuntimeDefaultFromLegacySettings', () => {
|
|
it('defaults malformed legacy settings to the host global default', () => {
|
|
expect(deriveGlobalWindowsRuntimeDefaultFromLegacySettings(null)).toEqual({
|
|
defaultRuntime: { kind: 'windows-host' },
|
|
fallbackReason: null
|
|
})
|
|
})
|
|
|
|
it('migrates existing host settings to the host global default', () => {
|
|
expect(
|
|
deriveGlobalWindowsRuntimeDefaultFromLegacySettings({
|
|
localAgentRuntime: 'host',
|
|
terminalWindowsShell: 'wsl.exe',
|
|
terminalWindowsWslDistro: 'Ubuntu'
|
|
})
|
|
).toEqual({
|
|
defaultRuntime: { kind: 'windows-host' },
|
|
fallbackReason: null
|
|
})
|
|
})
|
|
|
|
it('migrates existing WSL agent settings with their selected distro', () => {
|
|
expect(
|
|
deriveGlobalWindowsRuntimeDefaultFromLegacySettings({
|
|
localAgentRuntime: 'wsl',
|
|
localAgentWslDistro: 'Ubuntu-24.04',
|
|
terminalWindowsWslDistro: 'Debian'
|
|
})
|
|
).toEqual({
|
|
defaultRuntime: { kind: 'wsl', distro: 'Ubuntu-24.04' },
|
|
fallbackReason: null
|
|
})
|
|
})
|
|
|
|
it('uses the terminal WSL distro when the agent setting only selected WSL', () => {
|
|
expect(
|
|
deriveGlobalWindowsRuntimeDefaultFromLegacySettings({
|
|
localAgentRuntime: 'wsl',
|
|
terminalWindowsWslDistro: 'Debian'
|
|
})
|
|
).toEqual({
|
|
defaultRuntime: { kind: 'wsl', distro: 'Debian' },
|
|
fallbackReason: null
|
|
})
|
|
})
|
|
|
|
it('turns stale legacy WSL state into a migration host fallback when WSL is unavailable', () => {
|
|
expect(
|
|
deriveGlobalWindowsRuntimeDefaultFromLegacySettings(
|
|
{ localAgentRuntime: 'wsl', localAgentWslDistro: 'Ubuntu' },
|
|
{ wslAvailable: false, availableWslDistros: [] }
|
|
)
|
|
).toEqual({
|
|
defaultRuntime: { kind: 'windows-host' },
|
|
fallbackReason: 'legacy-wsl-unavailable'
|
|
})
|
|
})
|
|
|
|
it('turns stale legacy WSL distro state into a migration host fallback', () => {
|
|
expect(
|
|
deriveGlobalWindowsRuntimeDefaultFromLegacySettings(
|
|
{ localAgentRuntime: 'wsl', localAgentWslDistro: 'Ubuntu' },
|
|
{ wslAvailable: true, availableWslDistros: ['Debian'] }
|
|
)
|
|
).toEqual({
|
|
defaultRuntime: { kind: 'windows-host' },
|
|
fallbackReason: 'legacy-wsl-distro-missing'
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('resolveProjectExecutionRuntime', () => {
|
|
it('ignores local Windows WSL preferences on non-Windows platforms', () => {
|
|
expect(
|
|
resolveProjectExecutionRuntime({
|
|
appPlatform: 'darwin',
|
|
projectId: 'project-1',
|
|
projectRuntimePreference: { kind: 'wsl', distro: 'Ubuntu' },
|
|
globalWindowsRuntimeDefault: { kind: 'wsl', distro: 'Ubuntu' },
|
|
wslAvailable: true,
|
|
availableWslDistros: ['Ubuntu']
|
|
})
|
|
).toEqual({
|
|
status: 'resolved',
|
|
runtime: {
|
|
kind: 'local-host',
|
|
hostPlatform: 'darwin',
|
|
projectId: 'project-1',
|
|
reason: 'non-windows',
|
|
cacheKey: 'project-1:local-host:darwin'
|
|
}
|
|
})
|
|
})
|
|
|
|
it('resolves inherited Windows projects to the host global default', () => {
|
|
expect(
|
|
resolveProjectExecutionRuntime({
|
|
appPlatform: 'win32',
|
|
projectId: 'project-1',
|
|
projectRuntimePreference: { kind: 'inherit-global' },
|
|
globalWindowsRuntimeDefault: { kind: 'windows-host' },
|
|
wslAvailable: true,
|
|
availableWslDistros: ['Ubuntu']
|
|
})
|
|
).toEqual({
|
|
status: 'resolved',
|
|
runtime: {
|
|
kind: 'windows-host',
|
|
hostPlatform: 'win32',
|
|
projectId: 'project-1',
|
|
reason: 'global-default',
|
|
cacheKey: 'project-1:windows-host'
|
|
}
|
|
})
|
|
})
|
|
|
|
it('falls back malformed global defaults to Windows host', () => {
|
|
expect(
|
|
resolveProjectExecutionRuntime({
|
|
appPlatform: 'win32',
|
|
projectId: 'project-1',
|
|
projectRuntimePreference: { kind: 'inherit-global' },
|
|
globalWindowsRuntimeDefault: { kind: 'bogus', distro: 'Ubuntu' },
|
|
wslAvailable: true,
|
|
availableWslDistros: ['Ubuntu']
|
|
})
|
|
).toEqual({
|
|
status: 'resolved',
|
|
runtime: {
|
|
kind: 'windows-host',
|
|
hostPlatform: 'win32',
|
|
projectId: 'project-1',
|
|
reason: 'global-default',
|
|
cacheKey: 'project-1:windows-host'
|
|
}
|
|
})
|
|
})
|
|
|
|
it('resolves inherited Windows projects to the WSL global default', () => {
|
|
expect(
|
|
resolveProjectExecutionRuntime({
|
|
appPlatform: 'win32',
|
|
projectId: 'project-1',
|
|
projectRuntimePreference: { kind: 'inherit-global' },
|
|
globalWindowsRuntimeDefault: { kind: 'wsl', distro: 'Ubuntu' },
|
|
wslAvailable: true,
|
|
availableWslDistros: ['Ubuntu', 'Debian']
|
|
})
|
|
).toEqual({
|
|
status: 'resolved',
|
|
runtime: {
|
|
kind: 'wsl',
|
|
hostPlatform: 'wsl',
|
|
projectId: 'project-1',
|
|
distro: 'Ubuntu',
|
|
reason: 'global-default',
|
|
cacheKey: 'project-1:wsl:Ubuntu'
|
|
}
|
|
})
|
|
})
|
|
|
|
it('lets a project force host when the global default is WSL', () => {
|
|
expect(
|
|
resolveProjectExecutionRuntime({
|
|
appPlatform: 'win32',
|
|
projectId: 'project-1',
|
|
projectRuntimePreference: { kind: 'windows-host' },
|
|
globalWindowsRuntimeDefault: { kind: 'wsl', distro: 'Ubuntu' },
|
|
wslAvailable: true,
|
|
availableWslDistros: ['Ubuntu']
|
|
})
|
|
).toEqual({
|
|
status: 'resolved',
|
|
runtime: {
|
|
kind: 'windows-host',
|
|
hostPlatform: 'win32',
|
|
projectId: 'project-1',
|
|
reason: 'project-override',
|
|
cacheKey: 'project-1:windows-host'
|
|
}
|
|
})
|
|
})
|
|
|
|
it('carries Windows Git into the resolved WSL runtime and its cache key', () => {
|
|
expect(
|
|
resolveProjectExecutionRuntime({
|
|
appPlatform: 'win32',
|
|
projectId: 'project-1',
|
|
projectRuntimePreference: { kind: 'wsl', distro: 'Debian', git: 'windows-host' },
|
|
globalWindowsRuntimeDefault: { kind: 'windows-host' }
|
|
})
|
|
).toEqual({
|
|
status: 'resolved',
|
|
runtime: {
|
|
kind: 'wsl',
|
|
hostPlatform: 'wsl',
|
|
projectId: 'project-1',
|
|
distro: 'Debian',
|
|
reason: 'project-override',
|
|
git: 'windows-host',
|
|
cacheKey: 'project-1:wsl:Debian:git-windows-host'
|
|
}
|
|
})
|
|
})
|
|
|
|
it('lets a project force WSL when the global default is host', () => {
|
|
expect(
|
|
resolveProjectExecutionRuntime({
|
|
appPlatform: 'win32',
|
|
projectId: 'project-1',
|
|
projectRuntimePreference: { kind: 'wsl', distro: 'Debian' },
|
|
globalWindowsRuntimeDefault: { kind: 'windows-host' },
|
|
wslAvailable: true,
|
|
availableWslDistros: ['Ubuntu', 'Debian']
|
|
})
|
|
).toEqual({
|
|
status: 'resolved',
|
|
runtime: {
|
|
kind: 'wsl',
|
|
hostPlatform: 'wsl',
|
|
projectId: 'project-1',
|
|
distro: 'Debian',
|
|
reason: 'project-override',
|
|
cacheKey: 'project-1:wsl:Debian'
|
|
}
|
|
})
|
|
})
|
|
|
|
it('returns repair state instead of silently falling back when WSL is unavailable', () => {
|
|
expect(
|
|
resolveProjectExecutionRuntime({
|
|
appPlatform: 'win32',
|
|
projectId: 'project-1',
|
|
projectRuntimePreference: { kind: 'wsl', distro: 'Ubuntu' },
|
|
globalWindowsRuntimeDefault: { kind: 'windows-host' },
|
|
wslAvailable: false,
|
|
availableWslDistros: []
|
|
})
|
|
).toEqual({
|
|
status: 'repair-required',
|
|
repair: {
|
|
projectId: 'project-1',
|
|
preferredRuntime: { kind: 'wsl', distro: 'Ubuntu' },
|
|
reason: 'wsl-unavailable',
|
|
source: 'project-override',
|
|
cacheKey: 'project-1:repair:wsl-unavailable:Ubuntu'
|
|
}
|
|
})
|
|
})
|
|
|
|
it('returns repair state when WSL is selected without a distro', () => {
|
|
expect(
|
|
resolveProjectExecutionRuntime({
|
|
appPlatform: 'win32',
|
|
projectId: 'project-1',
|
|
projectRuntimePreference: { kind: 'inherit-global' },
|
|
globalWindowsRuntimeDefault: { kind: 'wsl', distro: null },
|
|
wslAvailable: true,
|
|
availableWslDistros: ['Ubuntu']
|
|
})
|
|
).toEqual({
|
|
status: 'repair-required',
|
|
repair: {
|
|
projectId: 'project-1',
|
|
preferredRuntime: { kind: 'wsl', distro: null },
|
|
reason: 'wsl-distro-required',
|
|
source: 'global-default',
|
|
cacheKey: 'project-1:repair:wsl-distro-required:default'
|
|
}
|
|
})
|
|
})
|
|
|
|
it('keeps two projects with different runtime preferences isolated', () => {
|
|
const hostProject = resolveProjectExecutionRuntime({
|
|
appPlatform: 'win32',
|
|
projectId: 'host-project',
|
|
projectRuntimePreference: { kind: 'windows-host' },
|
|
globalWindowsRuntimeDefault: { kind: 'wsl', distro: 'Ubuntu' },
|
|
wslAvailable: true,
|
|
availableWslDistros: ['Ubuntu']
|
|
})
|
|
const wslProject = resolveProjectExecutionRuntime({
|
|
appPlatform: 'win32',
|
|
projectId: 'wsl-project',
|
|
projectRuntimePreference: { kind: 'inherit-global' },
|
|
globalWindowsRuntimeDefault: { kind: 'wsl', distro: 'Ubuntu' },
|
|
wslAvailable: true,
|
|
availableWslDistros: ['Ubuntu']
|
|
})
|
|
|
|
expect(hostProject).toMatchObject({
|
|
status: 'resolved',
|
|
runtime: { kind: 'windows-host', cacheKey: 'host-project:windows-host' }
|
|
})
|
|
expect(wslProject).toMatchObject({
|
|
status: 'resolved',
|
|
runtime: { kind: 'wsl', distro: 'Ubuntu', cacheKey: 'wsl-project:wsl:Ubuntu' }
|
|
})
|
|
})
|
|
|
|
it('returns repair state when the selected distro is missing', () => {
|
|
expect(
|
|
resolveProjectExecutionRuntime({
|
|
appPlatform: 'win32',
|
|
projectId: 'project-1',
|
|
projectRuntimePreference: { kind: 'wsl', distro: 'Ubuntu' },
|
|
globalWindowsRuntimeDefault: { kind: 'windows-host' },
|
|
wslAvailable: true,
|
|
availableWslDistros: ['Debian']
|
|
})
|
|
).toEqual({
|
|
status: 'repair-required',
|
|
repair: {
|
|
projectId: 'project-1',
|
|
preferredRuntime: { kind: 'wsl', distro: 'Ubuntu' },
|
|
reason: 'wsl-distro-missing',
|
|
source: 'project-override',
|
|
cacheKey: 'project-1:repair:wsl-distro-missing:Ubuntu'
|
|
}
|
|
})
|
|
})
|
|
})
|