Skip to content

Commit d1ca418

Browse files
author
Your Name
committed
General fixes to running commands on windows:
- base64 encode powershell commands - fallback to run_cmd_subprocess in run_cmd_async because of event loop shenanigans - silence additional powershell outputs
1 parent ffaf8dc commit d1ca418

3 files changed

Lines changed: 55 additions & 7 deletions

File tree

cecli/run_cmd.py

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import asyncio
2+
import base64
23
import os
34
import platform
45
import subprocess
@@ -54,8 +55,11 @@ def run_cmd_subprocess(
5455
if platform.system() == "Windows":
5556
parent_process = get_windows_parent_process_name()
5657
if parent_process == "powershell.exe":
57-
command = f"powershell -Command {command}"
58-
58+
# Silence progress/error streams at the source to prevent CLIXML
59+
silenced_command = f"$ProgressPreference='SilentlyContinue'; {command}"
60+
cmd_bytes = silenced_command.encode("utf-16-le")
61+
encoded = base64.b64encode(cmd_bytes).decode()
62+
command = f"powershell -NoProfile -NonInteractive -OutputFormat Text -EncodedCommand {encoded}"
5963
if verbose:
6064
print("Running command:", command)
6165
print("SHELL:", shell)
@@ -93,7 +97,7 @@ def run_cmd_subprocess(
9397
print(line, end="", flush=True)
9498

9599
process.wait()
96-
return process.returncode, "".join(output)
100+
return process.returncode, _clean_output("".join(output))
97101
except Exception as e:
98102
return 1, str(e)
99103

@@ -116,7 +120,11 @@ async def run_cmd_async(
116120
if platform.system() == "Windows":
117121
parent_process = get_windows_parent_process_name()
118122
if parent_process == "powershell.exe":
119-
command = f"powershell -Command {command}"
123+
# Silence progress/error streams at the source to prevent CLIXML
124+
silenced_command = f"$ProgressPreference='SilentlyContinue'; {command}"
125+
cmd_bytes = silenced_command.encode("utf-16-le")
126+
encoded = base64.b64encode(cmd_bytes).decode()
127+
command = f"powershell -NoProfile -NonInteractive -OutputFormat Text -EncodedCommand {encoded}"
120128

121129
if verbose:
122130
print("Running command:", command)
@@ -131,6 +139,19 @@ async def run_cmd_async(
131139
stderr=asyncio.subprocess.STDOUT,
132140
cwd=cwd,
133141
)
142+
except NotImplementedError:
143+
# On Windows with SelectorEventLoop, asyncio does not support subprocesses.
144+
# Fall back to synchronous subprocess via loop.run_in_executor.
145+
loop = asyncio.get_running_loop()
146+
return await loop.run_in_executor(
147+
None,
148+
run_cmd_subprocess,
149+
command,
150+
verbose,
151+
cwd,
152+
encoding,
153+
should_print,
154+
)
134155
except FileNotFoundError:
135156
return 1, f"Command not found: {command}"
136157

@@ -175,7 +196,7 @@ async def read_stream(stream):
175196
if not reader_task.done():
176197
await reader_task
177198

178-
return process.returncode, "".join(output)
199+
return process.returncode, _clean_output("".join(output))
179200

180201

181202
def run_cmd_pexpect(command, verbose=False, cwd=None, should_print=True):
@@ -222,3 +243,26 @@ def output_callback(b):
222243
except (pexpect.ExceptionPexpect, TypeError, ValueError) as e:
223244
error_msg = f"Error running command {command}: {e}"
224245
return 1, error_msg
246+
247+
248+
def _clean_output(output):
249+
"""Remove CLIXML progress output from PowerShell commands."""
250+
if platform.system() != "Windows":
251+
return output
252+
253+
if output.startswith("#< CLIXML"):
254+
lines = output.splitlines()
255+
filtered = []
256+
for line in lines:
257+
# Skip the CLIXML header line
258+
if line.startswith("#< CLIXML"):
259+
continue
260+
# Skip CLIXML XML object tags (progress messages)
261+
stripped = line.strip()
262+
if stripped.startswith("<Objs ") or stripped == "</Objs>":
263+
continue
264+
if stripped.startswith("<Obj "):
265+
continue
266+
filtered.append(line)
267+
return "\n".join(filtered)
268+
return output

tests/basic/test_run_cmd.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44

55

66
def test_run_cmd_echo():
7-
command = "echo Hello, World!"
7+
command = "echo 'Hello World'"
88
exit_code, output = run_cmd(command)
99

1010
assert exit_code == 0
11-
assert output.strip() == "Hello, World!"
11+
assert output.strip() == "Hello World"

tests/helpers/monorepo/test_repomap_workspace.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ def mock_workspace(tmp_path):
1818
p1_dir = workspace_root / "p1" / "main"
1919
p1_dir.mkdir(parents=True)
2020
subprocess.run(["git", "init"], cwd=p1_dir, check=True)
21+
subprocess.run(["git", "config", "user.email", "test@test.com"], cwd=p1_dir, check=True)
22+
subprocess.run(["git", "config", "user.name", "Test"], cwd=p1_dir, check=True)
2123
(p1_dir / "file1.py").write_text("def func1(): pass")
2224
subprocess.run(["git", "add", "file1.py"], cwd=p1_dir, check=True)
2325
subprocess.run(["git", "commit", "-m", "p1 init"], cwd=p1_dir, check=True)
@@ -26,6 +28,8 @@ def mock_workspace(tmp_path):
2628
p2_dir = workspace_root / "p2" / "main"
2729
p2_dir.mkdir(parents=True)
2830
subprocess.run(["git", "init"], cwd=p2_dir, check=True)
31+
subprocess.run(["git", "config", "user.email", "test@test.com"], cwd=p2_dir, check=True)
32+
subprocess.run(["git", "config", "user.name", "Test"], cwd=p2_dir, check=True)
2933
(p2_dir / "file2.py").write_text("def func2(): pass")
3034
subprocess.run(["git", "add", "file2.py"], cwd=p2_dir, check=True)
3135
subprocess.run(["git", "commit", "-m", "p2 init"], cwd=p2_dir, check=True)

0 commit comments

Comments
 (0)