Skip to content

Commit 7be269e

Browse files
CopilotCopilot
andcommitted
python: remove implicit PATH fallback for CLI executable resolution
The runtime start path fell back to shutil.which(cli_path) whenever the resolved cli_path did not exist as a file. This let the SDK implicitly select an arbitrary same-named executable found on the host PATH instead of failing when the SDK-managed runtime or an explicitly configured path was not actually present, re-introducing the PATH-search behavior originally added by 05dd60e that #2524 asks to remove. Resolution order is unchanged (explicit path > COPILOT_CLI_PATH > SDK-managed downloaded runtime, see _resolve_runtime_entrypoint); only the undocumented PATH-search fallback at process start is removed. A missing resolved path now raises a RuntimeError up front instead of silently searching PATH. Adds a regression test asserting that a same-named executable placed on PATH is not used when the configured path does not exist. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent bba92dd commit 7be269e

2 files changed

Lines changed: 27 additions & 5 deletions

File tree

‎python/copilot/client.py‎

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import logging
2121
import os
2222
import re
23-
import shutil
2423
import subprocess
2524
import sys
2625
import threading
@@ -4352,11 +4351,15 @@ async def _start_cli_server(self) -> None:
43524351
cli_path = conn.path
43534352
assert cli_path is not None # resolved in __init__
43544353

4355-
# Verify CLI exists
4354+
# Verify the resolved CLI path exists. `cli_path` must already come from
4355+
# an explicit override, COPILOT_CLI_PATH, or the SDK-managed downloaded
4356+
# runtime (see `_resolve_runtime_entrypoint`); the SDK never falls back to
4357+
# searching PATH for an arbitrary system installation.
43564358
if not os.path.exists(cli_path):
4357-
original_path = cli_path
4358-
if (cli_path := shutil.which(cli_path)) is None:
4359-
raise RuntimeError(f"Copilot CLI not found at {original_path}")
4359+
raise RuntimeError(
4360+
f"Copilot CLI not found at {cli_path!r}. Set an explicit path, "
4361+
"COPILOT_CLI_PATH, or ensure the SDK-managed runtime download succeeded."
4362+
)
43604363

43614364
# Start with user-provided args, then add SDK-managed args
43624365
args = list(conn.args) + [

‎python/test_client.py‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,25 @@ def test_copilot_cli_path_does_not_require_runtime_bundle(tmp_path):
8383
assert connection.path == str(explicit)
8484

8585

86+
@pytest.mark.asyncio
87+
async def test_missing_cli_path_does_not_fall_back_to_path_search(tmp_path):
88+
"""A missing resolved CLI path must fail rather than silently resolving an
89+
arbitrary same-named executable found on PATH (see #2524 / commit 05dd60e)."""
90+
path_dir = tmp_path / "on_path"
91+
path_dir.mkdir()
92+
decoy = path_dir / "copilot"
93+
decoy.write_text("#!/bin/sh\necho decoy\n")
94+
decoy.chmod(0o755)
95+
96+
missing = tmp_path / "copilot"
97+
connection = RuntimeConnection.for_stdio(path=str(missing))
98+
client = CopilotClient(connection=connection, env={"PATH": str(path_dir)})
99+
100+
with patch.dict(os.environ, {"PATH": str(path_dir)}):
101+
with pytest.raises(RuntimeError, match="Copilot CLI not found"):
102+
await client._start_cli_server()
103+
104+
86105
class TestBuiltinPluginDirectories:
87106
@staticmethod
88107
async def _start_client(paths=None):

0 commit comments

Comments
 (0)