mirror of
https://github.com/stablyai/orca.git
synced 2026-09-30 00:03:15 +00:00
fix(cli): clean up legacy AppImage wrapper
This commit is contained in:
@@ -20,11 +20,9 @@ import {
|
||||
} from './cli-command-filesystem-transaction'
|
||||
import { DEV_LAUNCHER_DIR, LEGACY_LINUX_COMMAND_NAME } from './cli-install-constants'
|
||||
import { buildWindowsForwarder } from './cli-dev-launcher'
|
||||
import { isMissingError, isPermissionError } from './cli-install-errors'
|
||||
import { isPermissionError } from './cli-install-errors'
|
||||
import { isPathInsideOrEqual } from './cli-install-path-format'
|
||||
|
||||
const STABLE_LEGACY_INSPECTION_ATTEMPTS = 3
|
||||
|
||||
export class CliCommandInstallation extends CliCommandInspection {
|
||||
protected async installSymlink(status: CliInstallStatus): Promise<void> {
|
||||
const commandPath = status.commandPath
|
||||
@@ -194,34 +192,25 @@ export class CliCommandInstallation extends CliCommandInspection {
|
||||
})
|
||||
| null
|
||||
> {
|
||||
for (let attempt = 0; attempt < STABLE_LEGACY_INSPECTION_ATTEMPTS; attempt += 1) {
|
||||
const before = await readEntrySnapshot(commandPath)
|
||||
if (!before) {
|
||||
return null
|
||||
}
|
||||
let target: string | null = null
|
||||
try {
|
||||
target = before.isSymbolicLink ? await readlink(commandPath) : null
|
||||
} catch (error) {
|
||||
if (isMissingError(error)) {
|
||||
continue
|
||||
}
|
||||
throw error
|
||||
}
|
||||
const after = await readEntrySnapshot(commandPath)
|
||||
if (after && hasSameSnapshot(before, after)) {
|
||||
const resolvedTarget = target ? resolve(dirname(commandPath), target) : null
|
||||
return {
|
||||
fileSha256: null,
|
||||
rawSymlinkTarget: target,
|
||||
snapshot: after,
|
||||
managed: Boolean(
|
||||
resolvedTarget && this.isManagedLegacyLinuxTarget(resolvedTarget, launcherPath)
|
||||
)
|
||||
}
|
||||
}
|
||||
const inspected = await inspectStableCommand(commandPath, () =>
|
||||
this.inspectSymlink(commandPath, launcherPath)
|
||||
)
|
||||
if (!inspected.snapshot) {
|
||||
return null
|
||||
}
|
||||
const resolvedTarget = inspected.rawSymlinkTarget
|
||||
? resolve(dirname(commandPath), inspected.rawSymlinkTarget)
|
||||
: inspected.status.currentTarget
|
||||
return {
|
||||
fileSha256: inspected.fileSha256,
|
||||
rawSymlinkTarget: inspected.rawSymlinkTarget,
|
||||
snapshot: inspected.snapshot,
|
||||
managed: Boolean(
|
||||
resolvedTarget &&
|
||||
(this.isManagedLegacyLinuxTarget(resolvedTarget, launcherPath) ||
|
||||
(this.appImagePath && resolve(resolvedTarget) === resolve(this.appImagePath)))
|
||||
)
|
||||
}
|
||||
throw new Error(`The command at ${commandPath} changed while Orca inspected it.`)
|
||||
}
|
||||
|
||||
private async restoreQuarantinedCommand(
|
||||
|
||||
@@ -370,6 +370,56 @@ describe('CliInstaller', () => {
|
||||
}
|
||||
)
|
||||
|
||||
it.skipIf(process.platform === 'win32')(
|
||||
'removes a legacy AppImage wrapper only when it names the current AppImage',
|
||||
async () => {
|
||||
const fixture = await makeFixture()
|
||||
const homePath = join(fixture.root, 'home')
|
||||
const commandDir = join(homePath, '.local', 'bin')
|
||||
const legacyCommandPath = join(commandDir, 'orca')
|
||||
const appImagePath = join(fixture.root, 'Orca.AppImage')
|
||||
const foreignAppImagePath = join(fixture.root, 'Other.AppImage')
|
||||
const cacheRootPath = join(fixture.root, 'cache')
|
||||
await mkdir(commandDir, { recursive: true })
|
||||
await writeFile(appImagePath, '#!/usr/bin/env bash\n', {
|
||||
encoding: 'utf8',
|
||||
mode: 0o755
|
||||
})
|
||||
await writeFile(foreignAppImagePath, '#!/usr/bin/env bash\n', {
|
||||
encoding: 'utf8',
|
||||
mode: 0o755
|
||||
})
|
||||
await writeFile(legacyCommandPath, buildLegacyAppImageCliWrapper(appImagePath), {
|
||||
encoding: 'utf8',
|
||||
mode: 0o755
|
||||
})
|
||||
|
||||
const installer = new CliInstaller({
|
||||
platform: 'linux',
|
||||
isPackaged: true,
|
||||
userDataPath: fixture.userDataPath,
|
||||
appPath: fixture.appPath,
|
||||
appImagePath,
|
||||
appImageCacheRootPath: cacheRootPath,
|
||||
appImageExtractRunner: fakeAppImageExtractRunner,
|
||||
homePath,
|
||||
processPathEnv: commandDir
|
||||
})
|
||||
|
||||
await installer.install()
|
||||
await expect(lstat(legacyCommandPath)).rejects.toMatchObject({ code: 'ENOENT' })
|
||||
|
||||
await writeFile(legacyCommandPath, buildLegacyAppImageCliWrapper(foreignAppImagePath), {
|
||||
encoding: 'utf8',
|
||||
mode: 0o755
|
||||
})
|
||||
await installer.remove()
|
||||
await expect(readFile(legacyCommandPath, 'utf8')).resolves.toBe(
|
||||
buildLegacyAppImageCliWrapper(foreignAppImagePath)
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
// Why: the privilegedRunner is injectable so the EACCES→osascript path can be
|
||||
// exercised in integration without spawning osascript in unit tests.
|
||||
it.skipIf(process.platform === 'win32' || process.getuid?.() === 0)(
|
||||
|
||||
Reference in New Issue
Block a user