Skip to content

Commit ab9db34

Browse files
committed
feat: update command execution to use isolated working directory and add tests
1 parent 40442a4 commit ab9db34

File tree

3 files changed

+54
-8
lines changed

3 files changed

+54
-8
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111

1212
### Changed
1313

14+
- Changed command execution working directory in wish-command-execution:
15+
- Modified BashBackend to execute commands in `/app/{runId}/` directory
16+
- Added automatic directory creation with `os.makedirs`
17+
- Updated tests to verify correct working directory is used
18+
- Improved isolation between command execution and application source code
19+
1420
### Fixed
1521

22+
- Fixed OpenAI API errors in wish-log-analysis-api:
23+
- Isolated command execution from application source code to prevent large outputs
24+
- Prevented `.venv` and other source code files from being included in command output
25+
1626
### Removed
1727

1828

wish-command-execution/src/wish_command_execution/backend/bash.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,10 @@ async def execute_command(self, wish: Wish, command: str, cmd_num: int, log_file
128128
cmd_num: The command number.
129129
log_files: The log files to write output to.
130130
timeout_sec: The timeout in seconds for this command.
131+
132+
Note:
133+
Commands are executed in the working directory /app/{run_id}/ to isolate
134+
command execution from the application source code.
131135
"""
132136
# Create command result
133137
result = CommandResult.create(cmd_num, command, log_files, timeout_sec)
@@ -139,13 +143,21 @@ async def execute_command(self, wish: Wish, command: str, cmd_num: int, log_file
139143

140144
with open(log_files.stdout, "w") as stdout_file, open(log_files.stderr, "w") as stderr_file:
141145
try:
146+
# 作業ディレクトリを設定
147+
cwd = f"/app/{self.run_id or wish.id}/"
148+
149+
# ディレクトリが存在することを確認
150+
import os
151+
os.makedirs(cwd, exist_ok=True)
152+
142153
# Start the process (this is still synchronous, but the interface is async)
143154
process = subprocess.Popen(
144155
command,
145156
stdout=stdout_file,
146157
stderr=stderr_file,
147158
shell=True,
148-
text=True
159+
text=True,
160+
cwd=cwd # 作業ディレクトリを指定
149161
)
150162

151163
# Store in running commands dict with timeout information

wish-command-execution/tests/backend/test_bash_backend.py

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ def log_files(self):
3636
@pytest.mark.asyncio
3737
@patch("subprocess.Popen")
3838
@patch("builtins.open")
39-
async def test_execute_command(self, mock_open, mock_popen, backend, wish, log_files):
39+
@patch("os.makedirs")
40+
async def test_execute_command(self, mock_makedirs, mock_open, mock_popen, backend, wish, log_files):
4041
"""Test execute_command method.
4142
4243
This test verifies that the execute_command method correctly executes
@@ -57,13 +58,20 @@ async def test_execute_command(self, mock_open, mock_popen, backend, wish, log_f
5758
timeout_sec = 60
5859
await backend.execute_command(wish, cmd, cmd_num, log_files, timeout_sec)
5960

60-
# Verify that Popen was called with the expected command
61+
# Expected working directory
62+
expected_cwd = f"/app/{backend.run_id or wish.id}/"
63+
64+
# Verify that os.makedirs was called to ensure the directory exists
65+
mock_makedirs.assert_called_once_with(expected_cwd, exist_ok=True)
66+
67+
# Verify that Popen was called with the expected command and cwd
6168
mock_popen.assert_any_call(
6269
cmd,
6370
stdout=mock_stdout,
6471
stderr=mock_stderr,
6572
shell=True,
66-
text=True
73+
text=True,
74+
cwd=expected_cwd
6775
)
6876

6977
# Verify that the command result was added to the wish
@@ -78,7 +86,8 @@ async def test_execute_command(self, mock_open, mock_popen, backend, wish, log_f
7886
@pytest.mark.asyncio
7987
@patch("subprocess.Popen")
8088
@patch("builtins.open")
81-
async def test_execute_command_subprocess_error(self, mock_open, mock_popen, backend, wish, log_files):
89+
@patch("os.makedirs")
90+
async def test_execute_command_subprocess_error(self, mock_makedirs, mock_open, mock_popen, backend, wish, log_files):
8291
"""Test execute_command method with subprocess error."""
8392
# Set up the mock Popen to raise a subprocess error
8493
mock_popen.side_effect = subprocess.SubprocessError("Mock error")
@@ -93,6 +102,12 @@ async def test_execute_command_subprocess_error(self, mock_open, mock_popen, bac
93102
cmd_num = 1
94103
timeout_sec = 60
95104
await backend.execute_command(wish, cmd, cmd_num, log_files, timeout_sec)
105+
106+
# Expected working directory
107+
expected_cwd = f"/app/{backend.run_id or wish.id}/"
108+
109+
# Verify that os.makedirs was called to ensure the directory exists
110+
mock_makedirs.assert_called_once_with(expected_cwd, exist_ok=True)
96111

97112
# Verify that the command result was added to the wish
98113
assert len(wish.command_results) == 1
@@ -177,7 +192,8 @@ async def test_cancel_command_not_running(self, backend, wish):
177192
@pytest.mark.asyncio
178193
@patch("subprocess.Popen")
179194
@patch("builtins.open")
180-
async def test_execute_command_without_variable_replacement(self, mock_open, mock_popen, backend, wish, log_files):
195+
@patch("os.makedirs")
196+
async def test_execute_command_without_variable_replacement(self, mock_makedirs, mock_open, mock_popen, backend, wish, log_files):
181197
"""Test execute_command method without variable replacement."""
182198
# Set up the mock Popen
183199
mock_process = MagicMock()
@@ -194,13 +210,21 @@ async def test_execute_command_without_variable_replacement(self, mock_open, moc
194210
timeout_sec = 60
195211
await backend.execute_command(wish, cmd, cmd_num, log_files, timeout_sec)
196212

197-
# Verify that Popen was called with the original command
213+
# Expected working directory
214+
expected_cwd = f"/app/{backend.run_id or wish.id}/"
215+
216+
# Verify that os.makedirs was called to ensure the directory exists
217+
mock_makedirs.assert_called_once_with(expected_cwd, exist_ok=True)
218+
219+
# Verify that Popen was called with the original command and correct cwd
198220
mock_popen.assert_any_call(
199221
cmd,
200222
stdout=mock_stdout,
201223
stderr=mock_stderr,
202224
shell=True,
203-
text=True
225+
text=True,
226+
cwd=expected_cwd
204227
)
205228
args, kwargs = mock_popen.call_args
206229
assert args[0] == cmd
230+
assert kwargs['cwd'] == expected_cwd

0 commit comments

Comments
 (0)