Skip to content

Ensure Copilot bootstrap can find Node.js inside AWF chroot#2160

Merged
lpcox merged 6 commits intomainfrom
copilot/fix-copilot-cli-node-error
Apr 23, 2026
Merged

Ensure Copilot bootstrap can find Node.js inside AWF chroot#2160
lpcox merged 6 commits intomainfrom
copilot/fix-copilot-cli-node-error

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 23, 2026

Copilot-engine workflows were failing before inference because node was not available inside the chrooted agent environment (node: command not found). The failure path had no turns/tool calls, so startup needed a deterministic runtime fix and clearer diagnostics.

  • Node runtime visibility in chroot

    • Added ~/.nvm to agent bind mounts so nvm-managed Node installs are available in /host chroot.
    • Added ~/.nvm to pre-created allowed home subdirectories to avoid root-owned auto-created mount sources.
  • Fail-fast bootstrap guard for Copilot

    • Added AWF_REQUIRE_NODE signal in compose generation when the invoked executable is copilot (or Copilot auth env is configured).
    • Added an entrypoint preflight check gated by AWF_REQUIRE_NODE:
      • validates node is resolvable in chroot before running the user command
      • exits with a targeted, actionable error instead of a generic shell failure
  • Targeted test coverage

    • Extended docker-manager tests to validate:
      • .nvm mount inclusion
      • .nvm pre-creation
      • AWF_REQUIRE_NODE set for Copilot invocations and unset for non-Copilot commands
const commandExecutable = config.agentCommand.trim().split(/\s+/, 1)[0] || '';
const commandExecutableBase = path.posix.basename(commandExecutable.replace(/\\/g, '/'));
const isCopilotCommand = commandExecutableBase.toLowerCase() === 'copilot';
if (config.copilotGithubToken || config.copilotApiKey || isCopilotCommand) {
  environment.AWF_REQUIRE_NODE = '1';
}

Copilot AI changed the title [WIP] Fix Copilot CLI fails with node not found on runner bootstrap Ensure Copilot bootstrap can find Node.js inside AWF chroot Apr 23, 2026
Copilot AI requested a review from lpcox April 23, 2026 02:49
@lpcox lpcox marked this pull request as ready for review April 23, 2026 14:52
@lpcox lpcox requested a review from Mossaka as a code owner April 23, 2026 14:52
Copilot AI review requested due to automatic review settings April 23, 2026 14:52
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions
Copy link
Copy Markdown
Contributor

Security Review

One finding — ~/.nvm mount uses rw when ro suffices

File: src/docker-manager.ts, line 1234

agentVolumes.push(`\$\{effectiveHome}/.nvm:/host\$\{effectiveHome}/.nvm:rw`);

Issue: The stated purpose is to make nvm-managed Node.js binaries readable inside the chroot. A read-only mount achieves that. With rw, a sandboxed agent can write to the host's ~/.nvm directory—replacing executables (e.g. ~/.nvm/versions/node/.../bin/node) or injecting load-path scripts—and those changes persist on the host after AWF exits.

This differs from the .npm (cache, rw) and workspace (rw) mounts where write access is genuinely needed at runtime. nvm's version directories are not written to during normal agent execution.

Suggested fix: Change the mount to read-only:

agentVolumes.push(`\$\{effectiveHome}/.nvm:/host\$\{effectiveHome}/.nvm:ro`);

All other changes (AWF_REQUIRE_NODE detection logic in docker-manager.ts, preflight check in entrypoint.sh) are diagnostic-only with no security impact. The heredoc uses a single-quoted delimiter (<< 'AWFEOF'), preventing any injection risk.

Generated by Security Guard for issue #2160 · ● 108.3K ·

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@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

Fixes Copilot-engine startup failures in AWF chroot environments by ensuring Node.js installations (notably nvm-managed) are visible inside the chroot and by adding a fail-fast bootstrap guard with clearer diagnostics.

Changes:

  • Set AWF_REQUIRE_NODE=1 in generated compose env for Copilot-related invocations to enable a Node.js preflight guard.
  • Bind-mount ~/.nvm into the agent chroot and pre-create .nvm in the allowed home subdirectories to avoid root-owned mount sources.
  • Add/extend tests to validate .nvm mount + pre-creation and AWF_REQUIRE_NODE behavior for Copilot command invocations.
Show a summary per file
File Description
src/docker-manager.ts Adds Copilot/Node preflight signaling (AWF_REQUIRE_NODE), mounts ~/.nvm, and pre-creates .nvm for chroot bind mounts.
src/docker-manager.test.ts Extends tests to assert .nvm mount + pre-creation and AWF_REQUIRE_NODE set/unset behavior for Copilot vs non-Copilot commands.
containers/agent/entrypoint.sh Adds an AWF_REQUIRE_NODE-gated node preflight check with a targeted error message and exit code.

Copilot's findings

Tip

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

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

Comment thread src/docker-manager.ts
Comment on lines +821 to +825
const commandExecutableBase = path.posix.basename(commandExecutable.replace(/\\/g, '/'));
const isCopilotCommand = commandExecutableBase.toLowerCase() === 'copilot';
if (config.copilotGithubToken || config.copilotApiKey || isCopilotCommand) {
environment.AWF_REQUIRE_NODE = '1';
}
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

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

The new AWF_REQUIRE_NODE behavior is also enabled when Copilot auth env is present (copilotGithubToken/copilotApiKey), not just when the command executable is copilot. There are tests for the copilot ... command case, but none asserting the env-var-driven case; adding a test would help prevent regressions and ensure this broader gating remains intentional.

Copilot uses AI. Check for mistakes.
Comment thread containers/agent/entrypoint.sh Outdated
Comment on lines +712 to +715
echo "[entrypoint][ERROR] Copilot CLI requires Node.js, but 'node' is not available inside AWF chroot."
echo "[entrypoint][ERROR] Ensure Node.js is installed on the runner and reachable from PATH inside the chroot."
echo "[entrypoint][ERROR] If using setup-node or nvm, verify the install path is present and bind-mounted into /host."
echo "[entrypoint][ERROR] Example locations include /opt/hostedtoolcache/... and $HOME/.nvm/..."
Copy link

Copilot AI Apr 23, 2026

Choose a reason for hiding this comment

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

These preflight error messages are emitted via echo to stdout; for a fail-fast guard like this, it’s more reliable to write the diagnostics to stderr (e.g., redirect each line or the whole block) so callers and log parsers treat it as an error stream.

Suggested change
echo "[entrypoint][ERROR] Copilot CLI requires Node.js, but 'node' is not available inside AWF chroot."
echo "[entrypoint][ERROR] Ensure Node.js is installed on the runner and reachable from PATH inside the chroot."
echo "[entrypoint][ERROR] If using setup-node or nvm, verify the install path is present and bind-mounted into /host."
echo "[entrypoint][ERROR] Example locations include /opt/hostedtoolcache/... and $HOME/.nvm/..."
echo "[entrypoint][ERROR] Copilot CLI requires Node.js, but 'node' is not available inside AWF chroot." >&2
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

Copilot uses AI. Check for mistakes.
@github-actions

This comment has been minimized.

@lpcox
Copy link
Copy Markdown
Collaborator

lpcox commented Apr 23, 2026

@copilot please address this review feedback #2160 (review)

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 23, 2026

@copilot please address this review feedback #2160 (review)

Addressed in 2ffe7f2.

I applied the requested review feedback by:

  • adding coverage for the env-var-driven AWF_REQUIRE_NODE paths (copilotGithubToken and copilotApiKey), and
  • sending the node preflight diagnostics in entrypoint.sh to stderr (>&2).

@github-actions
Copy link
Copy Markdown
Contributor

Smoke Test Results:

  • ✅ GitHub MCP: Retrieved 2 merged PRs
  • ✅ Playwright: GitHub page title verified
  • ✅ File I/O: Test file created and read
  • ✅ Bash: File verification successful

Overall: PASS

💥 [THE END] — Illustrated by Smoke Claude

@github-actions github-actions Bot mentioned this pull request Apr 23, 2026
@github-actions
Copy link
Copy Markdown
Contributor

Smoke Test: Copilot BYOK (Offline) Mode

Test Result
GitHub MCP (latest merged PR: #2155)
GitHub.com connectivity (HTTP 200)
File write/read ⚠️ Pre-step template vars not substituted
BYOK inference (responding via api-proxy → api.githubcopilot.com)

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

Author: @copilot-swe-agent | Assignees: @lpcox, @Copilot

Overall: PASS

🔑 BYOK report filed by Smoke Copilot BYOK

@github-actions
Copy link
Copy Markdown
Contributor

Smoke test results
PR titles:

  • Harden api-proxy startup healthcheck to reduce early unhealthy flaps
  • chore: bump Copilot CLI to v1.0.34 in smoke-copilot
  • [WIP] docs: clarifications and review on token efficieny
  • fix: correct firewall issue dispatcher tracking issue link format
    Test 1 ✅ Test 2 ❌ Test 3 ✅ Test 4 ❌
    Test 5 ✅ Test 6 ✅ Test 7 ✅ Test 8 ✅
    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

🔥 Smoke Test Results

Test Status
GitHub MCP (last merged PR: "Harden api-proxy startup healthcheck to reduce early unhealthy flaps")
GitHub.com connectivity
File write/read (smoke-test-copilot-24843516967.txt)

Overall: PASS

PR by @app/copilot-swe-agent · Assignees: @lpcox, @Copilot

📰 BREAKING: Report filed by Smoke Copilot

@github-actions
Copy link
Copy Markdown
Contributor

Smoke Test Results: GitHub Actions Services Connectivity

Check Status Detail
Redis PING (host.docker.internal:6379) ❌ FAIL redis-cli not installed; raw socket timed out (port unreachable)
PostgreSQL pg_isready (host.docker.internal:5432) ❌ FAIL "no response" — port unreachable
PostgreSQL SELECT 1 (smoketest db) ❌ FAIL Skipped — host unreachable

All 3 checks failed. host.docker.internal resolves to 172.17.0.1 but neither port 6379 nor 5432 is reachable from the agent container. The service containers may not be running or may not be bound to the host bridge interface.

🔌 Service connectivity validated by Smoke Services

@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 ok ✅ PASS
Go env ok ✅ PASS
Go uuid ok ✅ PASS
Java gson 1/1 passed ✅ PASS
Java caffeine 1/1 passed ✅ PASS
Node.js clsx passed ✅ PASS
Node.js execa passed ✅ PASS
Node.js p-limit 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 #2160 · ● 5.1M ·

@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 ❌ No
Node.js v24.15.0 v20.20.2 ❌ No
Go go1.22.12 go1.22.12 ✅ Yes

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

Tested by Smoke Chroot

@lpcox lpcox merged commit 6e31d9f into main Apr 23, 2026
60 of 64 checks passed
@lpcox lpcox deleted the copilot/fix-copilot-cli-node-error branch April 23, 2026 17:10
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.

[awf] Agent container: Copilot CLI fails with node: command not found on runner bootstrap

3 participants