Skip to content

Support self-hosted runner toolcache path ($HOME/work/_tool) in chroot #3544

@lpcox

Description

@lpcox

Problem

Copilot-engine workflows on self-hosted runners (including ARC/DinD) fail with exit 127 because node is not discoverable inside AWF chroot. The Node.js binary lives in $HOME/work/_tool/node/... on these runners, but AWF only mounts and scans /opt/hostedtoolcache.

Related upstream fix: github/gh-aw#33219 — works around this from the caller side by passing extra --mount args, but AWF should handle it natively.

Current State

AWF already handles the hosted runner case:

  • /opt is bind-mounted as /host/opt:ro (covers /opt/hostedtoolcache)
  • entrypoint.sh dynamically scans /opt/hostedtoolcache/*/ for bin dirs and appends to PATH

Gap

Self-hosted runners (and some ARC configurations) store toolcache under:

$HOME/work/_tool/node/<version>/<arch>/bin/
/home/runner/work/_tool/node/<version>/<arch>/bin/

This path is:

  1. Not bind-mounted — AWF only mounts whitelisted $HOME subdirs (.cache, .config, .local, etc.)
  2. Not scanned for PATHentrypoint.sh only scans /opt/hostedtoolcache

Implementation Strategy

1. Bind-mount the toolcache directory (src/services/agent-volumes.ts)

Add a conditional mount for the runner toolcache when it exists:

// Self-hosted runner toolcache (e.g., ARC runners store tools here)
// Only mount if the directory actually exists on the host
const runnerToolDir = path.join(config.homeDir || /home/runner, work/_tool);
// Add: `${runnerToolDir}:/host${runnerToolDir}:ro`

This should be conditional — only add the mount if the source path exists (similar to how other optional mounts work).

2. Extend PATH scanning in entrypoint.sh

Add the self-hosted toolcache to the existing scan loop:

# After the /opt/hostedtoolcache scan (line ~776):
# Also scan self-hosted runner toolcache location
if [ -d "/home/runner/work/_tool" ]; then
  for tool_dir in /home/runner/work/_tool/*/; do
    for version_dir in "$tool_dir"*/; do
      for arch_dir in "$version_dir"*/; do
        if [ -d "${arch_dir}bin" ]; then
          case ":${PATH}:" in
            *":${arch_dir}bin:"*) ;;
            *) export PATH="${PATH}:${arch_dir}bin" ;;
          esac
        fi
      done
    done
  done
fi

Alternatively, generalize the scan to use an env var like AWF_EXTRA_TOOLCACHE_DIRS so the caller can pass additional scan roots.

3. Tests

  • Unit test in agent-volumes.test.ts asserting the conditional mount appears when homeDir is set
  • Shell test in tests/chroot-path-ordering.test.sh verifying the _tool path is scanned

Acceptance Criteria

  • node is discoverable inside chroot on self-hosted runners using $HOME/work/_tool
  • No regression on hosted runners (existing /opt/hostedtoolcache flow unchanged)
  • gh-aw#33219 workaround becomes unnecessary once this ships

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions