diff --git a/src/main/ghostty/index.test.ts b/src/main/ghostty/index.test.ts index 37d059c91..08902b537 100644 --- a/src/main/ghostty/index.test.ts +++ b/src/main/ghostty/index.test.ts @@ -114,6 +114,84 @@ background = #1a1a1a expect(result.unsupportedKeys).toEqual([]) }) + it('keeps successfully imported settings when a later config cannot be read', async () => { + delete process.env.XDG_CONFIG_HOME + const firstPath = '/Users/alice/.config/ghostty/config.ghostty' + const unreadablePath = '/Users/alice/.config/ghostty/config' + statMock.mockImplementation(async (p: string) => { + if (p === firstPath || p === unreadablePath) { + return { isFile: () => true, size: 128 } + } + throw Object.assign(new Error('ENOENT'), { code: 'ENOENT' }) + }) + readFileMock.mockImplementation(async (p: string) => { + if (p === firstPath) { + return 'font-size = 22\n' + } + throw new Error('EACCES') + }) + + const result = await previewGhosttyImport(createStore()) + + expect(result).toMatchObject({ + found: true, + configPath: firstPath, + configPaths: [firstPath], + diff: { terminalFontSize: 22 } + }) + }) + + it('keeps successfully imported settings when a later config is too large', async () => { + delete process.env.XDG_CONFIG_HOME + const firstPath = '/Users/alice/.config/ghostty/config.ghostty' + const oversizedPath = '/Users/alice/.config/ghostty/config' + statMock.mockImplementation(async (p: string) => { + if (p === firstPath) { + return { isFile: () => true, size: 128 } + } + if (p === oversizedPath) { + return { isFile: () => true, size: 1_000_001 } + } + throw Object.assign(new Error('ENOENT'), { code: 'ENOENT' }) + }) + readFileMock.mockResolvedValue('font-size = 22\n') + + const result = await previewGhosttyImport(createStore()) + + expect(result).toMatchObject({ + found: true, + configPath: firstPath, + configPaths: [firstPath], + diff: { terminalFontSize: 22 } + }) + expect(readFileMock).not.toHaveBeenCalledWith(oversizedPath, 'utf-8') + }) + + it('returns the first error when every discovered config fails to load', async () => { + delete process.env.XDG_CONFIG_HOME + const configPath = '/Users/alice/.config/ghostty/config.ghostty' + let configPathStatCalls = 0 + statMock.mockImplementation(async (p: string) => { + if (p === configPath) { + configPathStatCalls += 1 + if (configPathStatCalls === 1) { + return { isFile: () => true, size: 128 } + } + throw new Error('EACCES') + } + throw Object.assign(new Error('ENOENT'), { code: 'ENOENT' }) + }) + + const result = await previewGhosttyImport(createStore()) + + expect(result).toEqual({ + found: false, + diff: {}, + unsupportedKeys: [], + error: 'Could not read config: EACCES' + }) + }) + it('omits values that match current settings', async () => { statMock.mockImplementation(async (p: string) => { if (p === '/Users/alice/Library/Application Support/com.mitchellh.ghostty/config') { diff --git a/src/main/ghostty/index.ts b/src/main/ghostty/index.ts index 8557193fe..4827a2974 100644 --- a/src/main/ghostty/index.ts +++ b/src/main/ghostty/index.ts @@ -107,29 +107,37 @@ export async function previewGhosttyImport(store: Store): Promise = {} + const readPaths: string[] = [] + let firstError: string | null = null for (const configPath of configPaths) { let content: string try { const info = await stat(configPath) if (info.size > MAX_CONFIG_BYTES) { - return { - found: false, - diff: {}, - unsupportedKeys: [], - error: `Config file is too large to import (${info.size} bytes, limit ${MAX_CONFIG_BYTES}).` + if (firstError === null) { + firstError = `Config file is too large to import (${info.size} bytes, limit ${MAX_CONFIG_BYTES}).` } + continue } content = await readFile(configPath, 'utf-8') } catch (err) { const message = err instanceof Error ? err.message : 'Could not read config file' - return { - found: false, - diff: {}, - unsupportedKeys: [], - error: `Could not read config: ${message}` + if (firstError === null) { + firstError = `Could not read config: ${message}` } + continue } mergeParsedConfig(parsed, parseGhosttyConfig(content)) + readPaths.push(configPath) + } + + if (readPaths.length === 0) { + return { + found: false, + diff: {}, + unsupportedKeys: [], + error: firstError ?? 'Could not read config' + } } const themeUnsupportedKeys = await applyThemeReference(parsed) @@ -153,8 +161,8 @@ export async function previewGhosttyImport(store: Store): Promise