From 3695bfc4cb7b61b02461ae9137a4e7bb91cb41fb Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Sat, 29 Aug 2026 23:31:27 -0700 Subject: [PATCH] fix(linux): preserve unverified external displays --- .../startup/ensure-virtual-display.test.ts | 37 ++++++------------- src/main/startup/ensure-virtual-display.ts | 14 ++++++- 2 files changed, 24 insertions(+), 27 deletions(-) diff --git a/src/main/startup/ensure-virtual-display.test.ts b/src/main/startup/ensure-virtual-display.test.ts index c5aeb1b92df..bfd25c5097a 100644 --- a/src/main/startup/ensure-virtual-display.test.ts +++ b/src/main/startup/ensure-virtual-display.test.ts @@ -119,34 +119,21 @@ describe('ensureVirtualDisplayForHeadlessServe', () => { expect(MISSING_LINUX_DISPLAY_MESSAGE).toContain('`xorg-x11-server-Xvfb`') }) - it('starts Xvfb when the configured local display is stale', async () => { + it('leaves an externally configured stale display untouched', async () => { setPlatform('linux') process.env.DISPLAY = ':77' - spawnSyncMock.mockReturnValue({ status: 0 }) // `which Xvfb` succeeds - // First existsSync (stale-socket check) false; later (socket-ready poll) true. - existsSyncMock.mockReturnValueOnce(false).mockReturnValue(true) - spawnMock.mockReturnValue({ once: vi.fn(), kill: vi.fn(), killed: false }) - const processOnceSpy = vi.spyOn(process, 'once') - const processRemoveListenerSpy = vi.spyOn(process, 'removeListener') - const { ensureVirtualDisplayForHeadlessServe, stopVirtualDisplay } = - await import('./ensure-virtual-display') + statSyncMock.mockReturnValue({ isSocket: () => true }) + existsSyncMock.mockReturnValue(true) + readFileSyncMock.mockImplementation(() => { + throw new Error('display lock is outside this namespace') + }) + const { ensureVirtualDisplayForHeadlessServe } = await import('./ensure-virtual-display') - expect(ensureVirtualDisplayForHeadlessServe({ isServeMode: true })).toBe(true) - expect(spawnMock).toHaveBeenCalledWith( - 'Xvfb', - expect.arrayContaining([':99', '-terminate']), - expect.objectContaining({ detached: true }) - ) - expect(process.env.DISPLAY).toBe(':99') - expect(appMock.disableHardwareAcceleration).toHaveBeenCalled() - expect(appMock.commandLine.appendSwitch).toHaveBeenCalledWith('disable-dev-shm-usage') - expect(appMock.commandLine.appendSwitch).toHaveBeenCalledWith('disable-gpu') - expect(processOnceSpy).toHaveBeenCalledWith('exit', stopVirtualDisplay) - const readyHandler = appMock.once.mock.calls.find(([event]) => event === 'ready')?.[1] - expect(readyHandler).toBeTypeOf('function') - readyHandler() - expect(processRemoveListenerSpy).toHaveBeenCalledWith('exit', stopVirtualDisplay) - expect(appMock.once.mock.calls.some(([event]) => event === 'will-quit')).toBe(false) + expect(ensureVirtualDisplayForHeadlessServe({ isServeMode: true })).toBe(false) + expect(spawnSyncMock).not.toHaveBeenCalled() + expect(spawnMock).not.toHaveBeenCalled() + expect(rmSyncMock).not.toHaveBeenCalled() + expect(process.env.DISPLAY).toBe(':77') }) it('reuses an existing virtual display only when its X server is alive', async () => { diff --git a/src/main/startup/ensure-virtual-display.ts b/src/main/startup/ensure-virtual-display.ts index a817650e90f..b04ec4e4228 100644 --- a/src/main/startup/ensure-virtual-display.ts +++ b/src/main/startup/ensure-virtual-display.ts @@ -174,8 +174,18 @@ export function ensureVirtualDisplayForHeadlessServe(options: { isServeMode: boo configureHeadlessServeChromiumFlags() // Offscreen serve windows require X11; Wayland alone still needs Xvfb. - if (hasUsableXDisplay(process.env.DISPLAY)) { - return true + // Never delete artifacts from an externally managed display: a container may + // expose its socket without the host lock/PID being visible here. + const configuredDisplay = process.env.DISPLAY?.trim() + if (configuredDisplay) { + if (hasUsableXDisplay(configuredDisplay)) { + return true + } + console.warn( + `[serve] DISPLAY=${configuredDisplay} is not verifiably live; leaving it untouched. ` + + 'Unset DISPLAY to let Orca start its own Xvfb.' + ) + return false } if (!hasXvfbBinary()) {