diff --git a/src/workdir-setup.test.ts b/src/workdir-setup.test.ts index 63e043a98..ddd2dd057 100644 --- a/src/workdir-setup.test.ts +++ b/src/workdir-setup.test.ts @@ -37,6 +37,10 @@ function setupWorkdirFixture({ cleanupChrootHome = true } = {}) { tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'workdir-setup-test-')); jest.clearAllMocks(); (fs.chownSync as unknown as jest.Mock).mockImplementation(() => undefined); + // Restore chmodSync to its default passthrough (clearAllMocks doesn't reset implementations) + (fs.chmodSync as jest.Mock).mockImplementation( + (...args: Parameters) => actualFs.chmodSync(...args), + ); (getRealUserHome as jest.Mock).mockReturnValue(tempDir); }); @@ -158,7 +162,7 @@ describe('prepareWorkDirectories', () => { expect(() => prepareWorkDirectories(config, logPaths)).not.toThrow(); }); - it('rethrows non-EPERM errors when chmod on pre-existing mcp-logs dir fails', () => { + it('does not throw when chmod on pre-existing mcp-logs dir fails with EROFS', () => { const mcpLogsDir = '/tmp/gh-aw/mcp-logs'; fs.mkdirSync(mcpLogsDir, { recursive: true, mode: 0o700 }); @@ -172,7 +176,24 @@ describe('prepareWorkDirectories', () => { const config = buildConfig(); const logPaths = resolveLogPaths(config); - expect(() => prepareWorkDirectories(config, logPaths)).toThrow(erofs); + expect(() => prepareWorkDirectories(config, logPaths)).not.toThrow(); + }); + + it('rethrows non-EPERM/EROFS errors when chmod on pre-existing mcp-logs dir fails', () => { + const mcpLogsDir = '/tmp/gh-aw/mcp-logs'; + fs.mkdirSync(mcpLogsDir, { recursive: true, mode: 0o700 }); + + const eio = new Error("EIO: i/o error, chmod '/tmp/gh-aw/mcp-logs'") as NodeJS.ErrnoException; + eio.code = 'EIO'; + (fs.chmodSync as jest.Mock).mockImplementation((target: fs.PathLike, mode: fs.Mode) => { + if (target === mcpLogsDir) throw eio; + actualFs.chmodSync(target, mode); + }); + + const config = buildConfig(); + const logPaths = resolveLogPaths(config); + + expect(() => prepareWorkDirectories(config, logPaths)).toThrow(eio); }); it('falls back to world-writable squid logs when squid chown fails', () => { diff --git a/src/workdir-setup.ts b/src/workdir-setup.ts index 0574310a1..836142cd7 100644 --- a/src/workdir-setup.ts +++ b/src/workdir-setup.ts @@ -200,10 +200,11 @@ function prepareLogDirectories(logPaths: LogPaths): void { fs.chmodSync(mcpLogsDir, 0o777); logger.debug(`MCP logs directory permissions fixed at: ${mcpLogsDir}`); } catch (error) { - if ((error as NodeJS.ErrnoException).code !== 'EPERM') { + const code = (error as NodeJS.ErrnoException).code; + if (code !== 'EPERM' && code !== 'EROFS') { throw error; } - logger.debug(`MCP logs directory already exists at: ${mcpLogsDir} (chmod skipped, owned by another user)`); + logger.debug(`MCP logs directory already exists at: ${mcpLogsDir} (chmod skipped: ${code})`); } } }