Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 23 additions & 2 deletions src/workdir-setup.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<typeof actualFs.chmodSync>) => actualFs.chmodSync(...args),
);
(getRealUserHome as jest.Mock).mockReturnValue(tempDir);
});

Expand Down Expand Up @@ -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 });

Expand All @@ -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', () => {
Expand Down
5 changes: 3 additions & 2 deletions src/workdir-setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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})`);
}
}
}
Expand Down
Loading