Skip to content

Commit 8db8085

Browse files
zzdclaude
authored andcommitted
Wire DockerSandbox to Verifier L2 — container-isolated test execution
L2TestRunner now checks for Docker before running pytest: - Docker available → `docker run --rm --network none --memory 512m -v $project:/workspace:ro python:3.10-slim pytest tests/ -q` - No Docker → local subprocess fallback (current behavior) Safety: workspace mounted read-only, container auto-destroyed. Co-Authored-By: Claude Code <noreply@anthropic.com>
1 parent dad495c commit 8db8085

1 file changed

Lines changed: 31 additions & 28 deletions

File tree

core/verifier/verifier.py

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -154,40 +154,17 @@ def run(self, project_root: str) -> L2Result:
154154
logger.debug("L2: pytest not available")
155155
return L2Result(passed=True, tests_passed=0, tests_total=0)
156156

157-
# Run pytest
158-
env = os.environ.copy()
159-
# Add project_root to PYTHONPATH so test imports work
160-
existing_pythonpath = env.get("PYTHONPATH", "")
161-
if existing_pythonpath:
162-
env["PYTHONPATH"] = f"{root}{os.pathsep}{existing_pythonpath}"
163-
else:
164-
env["PYTHONPATH"] = str(root)
165-
166-
try:
167-
proc = subprocess.run(
168-
[sys.executable, "-m", "pytest", "tests/", "--tb=short", "-q"],
169-
cwd=root,
170-
capture_output=True,
171-
timeout=self.TIMEOUT,
172-
text=True,
173-
env=env,
174-
)
175-
except subprocess.TimeoutExpired:
176-
return L2Result(
177-
passed=False,
178-
error_type="TEST_FAILURE",
179-
error_detail="pytest timed out after 60s",
180-
)
157+
# Run pytest — prefer Docker sandbox, fall back to local
158+
pytest_cmd = f"{sys.executable} -m pytest tests/ --tb=short -q"
159+
stdout, stderr, rc = self._run_in_sandbox(root, pytest_cmd)
181160

182-
combined = proc.stdout + "\n" + proc.stderr
161+
combined = stdout + "\n" + stderr
183162
passed, total = self._parse_counts(combined)
184163

185-
if proc.returncode == 0:
164+
if rc == 0:
186165
return L2Result(passed=True, tests_passed=passed, tests_total=total)
187166

188-
# Failure — classify
189167
error_type, error_detail = classify_error(combined)
190-
191168
return L2Result(
192169
passed=False,
193170
tests_passed=passed,
@@ -196,6 +173,32 @@ def run(self, project_root: str) -> L2Result:
196173
error_detail=error_detail,
197174
)
198175

176+
@staticmethod
177+
def _run_in_sandbox(root, command: str) -> tuple:
178+
"""Run command in Docker sandbox if available, otherwise locally."""
179+
try:
180+
from core.docker_sandbox import DockerSandbox
181+
sandbox = DockerSandbox()
182+
if sandbox.is_available():
183+
logger.info("[L2] Running in Docker sandbox")
184+
return sandbox.run_command(command=command, workspace=str(root))
185+
except Exception as e:
186+
logger.debug("[L2] Docker sandbox unavailable: %s", e)
187+
188+
# Local fallback
189+
env = os.environ.copy()
190+
existing = env.get("PYTHONPATH", "")
191+
env["PYTHONPATH"] = f"{root}{os.pathsep}{existing}" if existing else str(root)
192+
try:
193+
proc = subprocess.run(
194+
command.split(),
195+
cwd=root, capture_output=True, text=True, timeout=120,
196+
env=env,
197+
)
198+
return proc.stdout, proc.stderr, proc.returncode
199+
except subprocess.TimeoutExpired:
200+
return "", "pytest timed out after 120s", -1
201+
199202
@staticmethod
200203
def _parse_counts(output: str) -> tuple[int, int]:
201204
"""Extract passed/total test counts from pytest output."""

0 commit comments

Comments
 (0)