From f1a9322db812ee1f687fd9d9a112a502e163222b Mon Sep 17 00:00:00 2001 From: Landon Cox Date: Tue, 30 Jun 2026 16:42:15 -0700 Subject: [PATCH 1/2] fix: sysroot filter should not drop workspace custom mounts The home directory filter was too broad - it dropped any mount where source started with effectiveHome. This caught custom --mount flags targeting workspace paths like /home/runner/_work/_temp/gh-aw which are daemon-visible on ARC/DinD (shared work volume). Narrow the filter to only drop dot-directory mounts (.cache, .config, etc.) that the sysroot volume provides, while keeping workspace/_work paths that are user-supplied custom mounts. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- src/compose-generator.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/compose-generator.ts b/src/compose-generator.ts index 4d0b89a3a..580ddd560 100644 --- a/src/compose-generator.ts +++ b/src/compose-generator.ts @@ -149,8 +149,13 @@ export function generateDockerCompose( // Drop mounts sourced from AWF workDir (runner's unshared /tmp/awf-*) if (source.startsWith(workDirPrefix)) return false; - // Drop home directory mounts targeting /host/home/... — sysroot provides them - if (source.startsWith(effectiveHome) && target.startsWith(hostHomeMountPrefix)) return false; + // Drop home dot-directory mounts (e.g. .cache, .config) — sysroot provides them. + // Keep workspace/work paths (e.g. _work/_temp/gh-aw) since those are user-supplied + // custom mounts or tool-cache mounts that the sysroot doesn't provide. + if (source.startsWith(effectiveHome) && target.startsWith(hostHomeMountPrefix)) { + const relPath = source.slice(effectiveHome.length); + if (relPath.startsWith('/.') || relPath === '') return false; + } return true; }); From f7beaeb06e3e11531c18685cb9a47a2bc8f6eef2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 1 Jul 2026 05:38:02 +0000 Subject: [PATCH 2/2] fix: handle trailing slash home mount filtering --- src/compose-generator.test.ts | 29 ++++++++++++++++++++++++----- src/compose-generator.ts | 3 ++- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/compose-generator.test.ts b/src/compose-generator.test.ts index da710d1c4..c9fe7b86c 100644 --- a/src/compose-generator.test.ts +++ b/src/compose-generator.test.ts @@ -1,4 +1,5 @@ import { generateDockerCompose } from './compose-generator'; +import { getRealUserHome } from './host-identity'; import { ACT_PRESET_BASE_IMAGE } from './host-identity'; import { logger } from './logger'; import { WrapperConfig } from './types'; @@ -496,7 +497,7 @@ describe('generateDockerCompose', () => { expect(result.volumes).toEqual({ sysroot: {} }); }); - it('filters out workDir-based and home-based bind mounts on split-fs', () => { + it('filters out workDir and home dot-directory bind mounts on split-fs', () => { const config = { ...mockConfig, runnerTopology: 'arc-dind' as const, @@ -504,16 +505,34 @@ describe('generateDockerCompose', () => { }; const result = generateDockerCompose(config, mockNetworkConfig); const volumes = result.services.agent.volumes as string[]; + const workspaceDir = process.env.GITHUB_WORKSPACE || process.cwd(); // workDir-based mounts should be dropped expect(volumes.some(v => v.startsWith('/tmp/awf-12345'))).toBe(false); - // Home-based mounts targeting /host/home should be dropped - const homeTargets = volumes.filter(v => { + // Home dot-directory mounts should be dropped, but workspace mounts under home should remain. + const homeTargets = volumes + .filter(v => { + const target = v.split(':')[1]; + return target.startsWith('/host/home') && !v.startsWith('/dev/null'); + }) + .map(v => v.split(':')[1]); + + expect(homeTargets).toContain(`/host${workspaceDir}`); + expect(homeTargets.some(target => target.startsWith('/host/home/runner/.'))).toBe(false); + + // Home root mounts (including trailing slash source) should be dropped. + const effectiveHome = getRealUserHome(); + const configWithHomeRootMount = { + ...config, + volumeMounts: [`${effectiveHome}/:/host${effectiveHome}:rw`], + }; + const resultWithHomeRootMount = generateDockerCompose(configWithHomeRootMount, mockNetworkConfig); + const homeRootMounts = (resultWithHomeRootMount.services.agent.volumes as string[]).filter(v => { const target = v.split(':')[1]; - return target.startsWith('/host/home') && !v.startsWith('/dev/null'); + return target === `/host${effectiveHome}` || target === `/host${effectiveHome}/`; }); - expect(homeTargets).toHaveLength(0); + expect(homeRootMounts).toHaveLength(0); // Should still have /tmp:/tmp, /sys, /dev, sysroot volume expect(volumes).toContain('/tmp:/tmp:rw'); diff --git a/src/compose-generator.ts b/src/compose-generator.ts index 580ddd560..772885916 100644 --- a/src/compose-generator.ts +++ b/src/compose-generator.ts @@ -153,7 +153,8 @@ export function generateDockerCompose( // Keep workspace/work paths (e.g. _work/_temp/gh-aw) since those are user-supplied // custom mounts or tool-cache mounts that the sysroot doesn't provide. if (source.startsWith(effectiveHome) && target.startsWith(hostHomeMountPrefix)) { - const relPath = source.slice(effectiveHome.length); + const normalizedSource = source.replace(/\/+$/, '') || '/'; + const relPath = normalizedSource.slice(effectiveHome.length); if (relPath.startsWith('/.') || relPath === '') return false; }