Skip to content

Support self-hosted runner toolcache in chroot PATH resolution#3545

Merged
lpcox merged 4 commits into
mainfrom
copilot/support-toolcache-path-in-chroot
May 21, 2026
Merged

Support self-hosted runner toolcache in chroot PATH resolution#3545
lpcox merged 4 commits into
mainfrom
copilot/support-toolcache-path-in-chroot

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 21, 2026

Self-hosted runners can install Node.js under $HOME/work/_tool, which AWF did not mount or scan inside the chroot. That left node undiscoverable for Copilot-engine workflows even though the runtime was present on the host.

  • Volume mounts

    • Add a conditional read-only bind mount for the self-hosted runner toolcache at ${HOME}/work/_tool.
    • Keep the mount optional so hosted runners and environments without that directory remain unchanged.
  • Chroot PATH discovery

    • Extend toolcache scanning in containers/agent/entrypoint.sh to cover both:
      • /opt/hostedtoolcache
      • /home/runner/work/_tool
    • Preserve existing PATH ordering: explicit $GITHUB_PATH entries still win; discovered toolcache bins remain fallback entries.
  • Targeted coverage

    • Add a unit test for the conditional ${HOME}/work/_tool mount.
    • Extend the chroot PATH ordering shell test to verify _tool/node/.../bin is discovered and usable as a fallback.
append_toolcache_bins "/opt/hostedtoolcache"
append_toolcache_bins "/home/runner/work/_tool"

Copilot AI changed the title [WIP] Support self-hosted runner toolcache path in chroot Support self-hosted runner toolcache in chroot PATH resolution May 21, 2026
Copilot AI requested a review from lpcox May 21, 2026 18:07
Copilot finished work on behalf of lpcox May 21, 2026 18:07
@lpcox lpcox marked this pull request as ready for review May 21, 2026 18:15
Copilot AI review requested due to automatic review settings May 21, 2026 18:15
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 21, 2026

✅ Coverage Check Passed

Overall Coverage

Metric Base PR Delta
Lines 95.99% 96.07% 📈 +0.08%
Statements 95.81% 95.89% 📈 +0.08%
Functions 97.36% 97.36% ➡️ +0.00%
Branches 89.48% 89.53% 📈 +0.05%
📁 Per-file Coverage Changes (2 files)
File Lines (Before → After) Statements (Before → After)
src/services/agent-volumes.ts 91.6% → 91.9% (+0.31%) 91.8% → 92.1% (+0.29%)
src/config-writer.ts 83.0% → 85.6% (+2.54%) 83.0% → 85.6% (+2.54%)

Coverage comparison generated by scripts/ci/compare-coverage.ts

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

This was referenced May 21, 2026
@github-actions

This comment has been minimized.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extends AWF chroot support for self-hosted GitHub Actions runners where language runtimes (notably Node.js) may be installed under $HOME/work/_tool, by ensuring that directory is mounted into the agent container and included in chroot PATH discovery.

Changes:

  • Add an optional read-only bind mount for ${HOME}/work/_tool when present.
  • Refactor entrypoint.sh toolcache PATH scanning to handle multiple toolcache roots.
  • Extend chroot PATH-ordering tests to cover discovery and fallback resolution of node from the self-hosted toolcache layout.
Show a summary per file
File Description
tests/chroot-path-ordering.test.sh Adds fixtures and assertions to validate _tool/node/.../bin is appended and usable as a PATH fallback.
src/services/agent-volumes.ts Conditionally mounts $HOME/work/_tool into /host... for chroot visibility.
src/services/agent-volumes.test.ts Adds unit coverage for presence/absence of the optional toolcache bind mount.
containers/agent/entrypoint.sh Introduces append_toolcache_bins and scans both hosted + self-hosted toolcache roots for PATH fallback entries.

Copilot's findings

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 4/4 changed files
  • Comments generated: 3

Comment thread containers/agent/entrypoint.sh Outdated
Comment on lines +771 to +795
@@ -786,7 +790,9 @@ if [ -d "/opt/hostedtoolcache" ]; then
done
done
done
fi
}
append_toolcache_bins "/opt/hostedtoolcache"
append_toolcache_bins "/home/runner/work/_tool"
Comment thread containers/agent/entrypoint.sh Outdated
echo "[entrypoint][ERROR] Ensure Node.js is installed on the runner and reachable from PATH inside the chroot." >&2
echo "[entrypoint][ERROR] If using setup-node or nvm, verify the install path is present and bind-mounted into /host." >&2
echo "[entrypoint][ERROR] Example locations include /opt/hostedtoolcache/... and $HOME/.nvm/..." >&2
echo "[entrypoint][ERROR] Example locations include /opt/hostedtoolcache/..., /home/runner/work/_tool/..., and $HOME/.nvm/..." >&2
Comment thread src/services/agent-volumes.ts Outdated
Comment on lines +208 to +209
if (fs.existsSync(runnerToolCacheDir)) {
agentVolumes.push(`${runnerToolCacheDir}:/host${runnerToolCacheDir}:ro`);
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@lpcox
Copy link
Copy Markdown
Collaborator

lpcox commented May 21, 2026

@copilot address review feedback

Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 21, 2026

@copilot address review feedback

Addressed all three review findings in commit fix: use HOME/work/_tool in entrypoint; reject symlinks in volumes:

  1. Hard-coded path in toolcache scan (entrypoint.sh:795): append_toolcache_bins "/home/runner/work/_tool"append_toolcache_bins "${HOME}/work/_tool". Since the call is inside a single-quoted heredoc, ${HOME} expands at chroot-script runtime, matching whichever home directory the actual mount uses.

  2. Hard-coded path in Node.js diagnostic (entrypoint.sh:822): Updated the example path in the error message from /home/runner/work/_tool/... to $HOME/work/_tool/....

  3. Symlink injection via existsSync (agent-volumes.ts:208-209): Replaced fs.existsSync() with fs.lstatSync() + stat.isDirectory(). Because lstatSync does not follow symlinks, a symlink at $HOME/work/_tool returns isDirectory() === false and is skipped — preventing an attacker from redirecting the bind-mount. Added a corresponding unit test to cover this case.

Copilot finished work on behalf of lpcox May 21, 2026 18:43
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@lpcox
Copy link
Copy Markdown
Collaborator

lpcox commented May 21, 2026

Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 21, 2026

@copilot fix this failed ci check https://github.com/github/gh-aw-firewall/actions/runs/26246016695/job/77245065921?pr=3545

The tests/chroot-path-ordering.test.sh sed pattern was broken by our ${HOME}/work/_tool refactor — the test was still substituting the old hardcoded /home/runner/work/_tool literal, which no longer appeared in entrypoint.sh. Fixed in 2e927a7 by changing the sed pattern to s|\${HOME}/work/_tool|${RUNNER_TOOLCACHE}|g. All 8 shell tests and 47 unit tests pass.

The Smoke Claude CI failure (job 77245065921) is unrelated to our code changes — our changes only affect chroot mode and the $HOME/work/_tool mount, neither of which Claude's smoke workflow uses. The "Execute Claude Code CLI" step ran for only 34 seconds, pointing to a transient Anthropic API issue; Smoke Copilot, Gemini, Codex, and Chroot all passed on the same commit.

Copilot finished work on behalf of lpcox May 21, 2026 20:13
@github-actions
Copy link
Copy Markdown
Contributor

Smoke Test Results

GitHub API: 2 PR entries confirmed
GitHub check: playwright_check=✅ PASS
File verify: smoke-test validated

OVERALL: PASS

💥 [THE END] — Illustrated by Smoke Claude

@github-actions
Copy link
Copy Markdown
Contributor

🔥 Smoke Test: Copilot BYOK (Offline) Mode

Test Result
GitHub MCP connectivity ✅ PR listed successfully
GitHub.com HTTP ⚠️ Pre-step data unavailable (template not expanded)
File write/read ⚠️ Pre-step data unavailable (template not expanded)
BYOK inference (api-proxy → api.githubcopilot.com) ✅ Responding in BYOK offline mode

Running in BYOK offline mode (COPILOT_OFFLINE=true) via api-proxy → api.githubcopilot.com.

Overall: PASS (core BYOK path verified ✅)

PR by @Copilot — assignees: @lpcox, @Copilot

🔑 BYOK report filed by Smoke Copilot BYOK

@github-actions
Copy link
Copy Markdown
Contributor

🧪 Smoke Test Results

Test Status
GitHub MCP connectivity
GitHub.com HTTP connectivity ⚠️ (pre-step data not expanded)
File write/read ⚠️ (pre-step data not expanded)

PR: "Support self-hosted runner toolcache in chroot PATH resolution"
Author: @Copilot | Assignees: @lpcox, @Copilot

Overall: PARTIAL — MCP ✅, pre-step outputs were not substituted (template vars literal).

📰 BREAKING: Report filed by Smoke Copilot

@github-actions
Copy link
Copy Markdown
Contributor

Smoke Test Results

Overall status: FAIL

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • localhost

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "localhost"

See Network Configuration for more information.

💎 Faceted by Smoke Gemini

@github-actions
Copy link
Copy Markdown
Contributor

🏗️ Build Test Suite Results

Ecosystem Project Build/Install Tests Status
Bun elysia 1/1 passed ✅ PASS
Bun hono 1/1 passed ✅ PASS
C++ fmt N/A ✅ PASS
C++ json N/A ✅ PASS
Deno oak N/A 1/1 passed ✅ PASS
Deno std N/A 1/1 passed ✅ PASS
.NET hello-world N/A ✅ PASS
.NET json-parse N/A ✅ PASS
Go color 1/1 passed ✅ PASS
Go env 1/1 passed ✅ PASS
Go uuid 1/1 passed ✅ PASS
Java gson 1/1 passed ✅ PASS
Java caffeine 1/1 passed ✅ PASS
Node.js clsx All passed ✅ PASS
Node.js execa All passed ✅ PASS
Node.js p-limit All passed ✅ PASS
Rust fd 1/1 passed ✅ PASS
Rust zoxide 1/1 passed ✅ PASS

Overall: 8/8 ecosystems passed — ✅ PASS

Generated by Build Test Suite for issue #3545 · ● 4.2M ·

@github-actions
Copy link
Copy Markdown
Contributor

Smoke Codex: FAIL

PRs: docs: credential exclusion requires apiProxy.enabled; fix(squid): chown bind-mounted log dirs to proxy user on startup
GitHub merged PR review: ✅
Safeinputs-gh PR query: ❌ tool unavailable
Playwright title check: ✅
Tavily search: ❌ no tools available
File/bash/discussion/build: ✅
Overall status: FAIL

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • registry.npmjs.org

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "registry.npmjs.org"

See Network Configuration for more information.

🔮 The oracle has spoken through Smoke Codex

@github-actions
Copy link
Copy Markdown
Contributor

Chroot Version Comparison Results

Runtime Host Version Chroot Version Match?
Python Python 3.12.13 Python 3.12.3
Node.js v24.15.0 v20.20.2
Go go1.22.12 go1.22.12

Overall: ❌ Not all versions match — Python and Node.js differ between host and chroot environments.

Tested by Smoke Chroot

@github-actions
Copy link
Copy Markdown
Contributor

Smoke Test Results — FAIL

Check Result
Redis PING ❌ Timeout/no response
PostgreSQL pg_isready ❌ No response
PostgreSQL SELECT 1 ❌ Timeout/no response

host.docker.internal is not reachable from this environment. All three checks failed.

Overall: ❌ FAIL

🔌 Service connectivity validated by Smoke Services

@lpcox lpcox merged commit 34d8fd4 into main May 21, 2026
63 of 68 checks passed
@lpcox lpcox deleted the copilot/support-toolcache-path-in-chroot branch May 21, 2026 21:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

3 participants