mirror of
https://github.com/stablyai/orca.git
synced 2026-09-27 00:02:37 +00:00
fix: preserve packaged windows cli launcher (#4180)
This commit is contained in:
@@ -668,6 +668,46 @@ describe('CliInstaller', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('does not overwrite the packaged Windows launcher while registering PATH', async () => {
|
||||
const fixture = await makeFixture()
|
||||
const localAppDataPath = fixture.root
|
||||
const resourcesPath = join(localAppDataPath, 'Programs', 'Orca', 'resources')
|
||||
const bundledLauncher = join(resourcesPath, 'bin', 'orca.cmd')
|
||||
const bundledContent = '@echo off\r\necho bundled-orca %*\r\n'
|
||||
await mkdir(dirname(bundledLauncher), { recursive: true })
|
||||
await writeFile(bundledLauncher, bundledContent, 'utf8')
|
||||
|
||||
let userPath: string | null = null
|
||||
const installer = new CliInstaller({
|
||||
platform: 'win32',
|
||||
isPackaged: true,
|
||||
resourcesPath,
|
||||
localAppDataPath,
|
||||
userDataPath: fixture.userDataPath,
|
||||
execPath: join(localAppDataPath, 'Programs', 'Orca', 'Orca.exe'),
|
||||
appPath: fixture.appPath,
|
||||
userPathReader: async () => userPath,
|
||||
userPathWriter: async (value) => {
|
||||
userPath = value
|
||||
}
|
||||
})
|
||||
|
||||
const installed = await installer.install()
|
||||
|
||||
expect(installed.state).toBe('installed')
|
||||
expect(installed.pathConfigured).toBe(true)
|
||||
expect(installed.commandPath).toBe(bundledLauncher)
|
||||
expect(userPath).toBe(dirname(bundledLauncher))
|
||||
await expect(readFile(bundledLauncher, 'utf8')).resolves.toBe(bundledContent)
|
||||
|
||||
const removed = await installer.remove()
|
||||
|
||||
expect(removed.state).toBe('not_installed')
|
||||
expect(removed.pathConfigured).toBe(false)
|
||||
expect(userPath).toBe('')
|
||||
await expect(readFile(bundledLauncher, 'utf8')).resolves.toBe(bundledContent)
|
||||
})
|
||||
|
||||
// Why: the arm64 fallback must apply for packaged builds, not just dev launchers.
|
||||
it.skipIf(process.platform === 'win32')(
|
||||
'resolves to ~/.local/bin/orca on arm64 even when isPackaged is true',
|
||||
|
||||
@@ -173,6 +173,9 @@ export class CliInstaller {
|
||||
} else if (this.isLinuxAppImage()) {
|
||||
await this.installAppImageWrapper(status.commandPath, status.launcherPath)
|
||||
await this.removeLegacyLinuxCommandIfManaged(status.launcherPath)
|
||||
} else if (this.isWindowsPackagedBundledCommand(status.commandPath, status.launcherPath)) {
|
||||
// Why: packaged Windows already ships resources/bin/orca.cmd. Registration
|
||||
// only owns the user PATH entry; rewriting the asset makes it recurse.
|
||||
} else {
|
||||
// Why: mkdir stays here for the Windows wrapper path — the target dir is
|
||||
// user-writable (%LOCALAPPDATA%) so EACCES cannot occur. The symlink path
|
||||
@@ -215,6 +218,8 @@ export class CliInstaller {
|
||||
if (status.installMethod === 'symlink') {
|
||||
await this.removeSymlink(status.commandPath)
|
||||
await this.removeLegacyLinuxCommandIfManaged(status.launcherPath)
|
||||
} else if (this.isWindowsPackagedBundledCommand(status.commandPath, status.launcherPath)) {
|
||||
await this.removeWindowsPathEntry(dirname(status.commandPath))
|
||||
} else {
|
||||
await unlink(status.commandPath)
|
||||
await this.removeWindowsPathEntry(dirname(status.commandPath))
|
||||
@@ -571,6 +576,19 @@ export class CliInstaller {
|
||||
return this.platform === 'linux' && Boolean(this.appImagePath)
|
||||
}
|
||||
|
||||
private isWindowsPackagedBundledCommand(
|
||||
commandPath: string | null,
|
||||
launcherPath: string | null
|
||||
): commandPath is string {
|
||||
return (
|
||||
this.platform === 'win32' &&
|
||||
this.isPackaged &&
|
||||
commandPath !== null &&
|
||||
launcherPath !== null &&
|
||||
samePathEntry('win32', commandPath, launcherPath)
|
||||
)
|
||||
}
|
||||
|
||||
private async inspectWindowsWrapper(
|
||||
commandPath: string,
|
||||
launcherPath: string
|
||||
@@ -589,6 +607,18 @@ export class CliInstaller {
|
||||
})
|
||||
}
|
||||
|
||||
if (this.isWindowsPackagedBundledCommand(commandPath, launcherPath)) {
|
||||
return this.buildStatus({
|
||||
commandPath,
|
||||
launcherPath,
|
||||
installMethod: 'wrapper',
|
||||
supported: true,
|
||||
state: 'installed',
|
||||
currentTarget: launcherPath,
|
||||
detail: `Registered at ${commandPath}.`
|
||||
})
|
||||
}
|
||||
|
||||
const currentContent = await readFile(commandPath, 'utf8')
|
||||
const expectedContent = buildWindowsForwarder(launcherPath)
|
||||
return this.buildStatus({
|
||||
@@ -657,6 +687,21 @@ export class CliInstaller {
|
||||
pathDirectory: string,
|
||||
pathConfigured: boolean
|
||||
): CliInstallStatus {
|
||||
if (
|
||||
this.isWindowsPackagedBundledCommand(status.commandPath, status.launcherPath) &&
|
||||
status.state === 'installed' &&
|
||||
!pathConfigured
|
||||
) {
|
||||
return {
|
||||
...status,
|
||||
pathDirectory,
|
||||
pathConfigured,
|
||||
state: 'not_installed',
|
||||
currentTarget: null,
|
||||
detail: `Register ${status.commandPath} to use Orca from Command Prompt or PowerShell.`
|
||||
}
|
||||
}
|
||||
|
||||
if (status.state !== 'installed') {
|
||||
return {
|
||||
...status,
|
||||
|
||||
Reference in New Issue
Block a user