Spaces:
Sleeping
Sleeping
Download tools/docker_tool.py from Tanish2416/RepoRepair: direct link, hf CLI and curl.
- Browser
- Download file 6.54 kB
-
https://huggingface.co/spaces/Tanish2416/RepoRepair/resolve/main/tools/docker_tool.py
- Command line
-
hf download hf://spaces/Tanish2416/RepoRepair/tools/docker_tool.py
-
curl -L -o docker_tool.py https://huggingface.co/spaces/Tanish2416/RepoRepair/resolve/main/tools/docker_tool.py
6.54 kB
| """Docker-based test execution tool.""" | |
| import logging | |
| import time | |
| from pathlib import Path | |
| from typing import Optional | |
| import docker | |
| from docker.errors import DockerException | |
| from models import TestResult | |
| logger = logging.getLogger(__name__) | |
| class DockerTool: | |
| """Tool for running tests in Docker containers.""" | |
| def __init__(self): | |
| """Initialize Docker tool.""" | |
| try: | |
| self.client = docker.from_env() | |
| self.client.ping() | |
| logger.info("Docker tool initialized") | |
| except DockerException as e: | |
| logger.error(f"Docker not available: {e}") | |
| raise | |
| def run_tests( | |
| self, | |
| repo_path: Path, | |
| test_command: Optional[str] = None, | |
| timeout: int = 300, | |
| ) -> TestResult: | |
| """ | |
| Run tests in a Docker container. | |
| Args: | |
| repo_path: Path to repository | |
| test_command: Test command to run (auto-detect if None) | |
| timeout: Timeout in seconds | |
| Returns: | |
| TestResult object | |
| """ | |
| start_time = time.time() | |
| try: | |
| if test_command is None: | |
| test_command = self._detect_test_command(repo_path) | |
| if not test_command: | |
| logger.warning("No test command detected, skipping tests") | |
| return TestResult( | |
| success=True, | |
| output="No tests detected in repository", | |
| exit_code=0, | |
| duration=0.0, | |
| ) | |
| logger.info(f"Running tests with command: {test_command}") | |
| dockerfile_content = self._generate_dockerfile(repo_path) | |
| dockerfile_path = repo_path / "Dockerfile.test" | |
| dockerfile_path.write_text(dockerfile_content) | |
| try: | |
| image, build_logs = self.client.images.build( | |
| path=str(repo_path), | |
| dockerfile="Dockerfile.test", | |
| tag="reporepair-test:latest", | |
| rm=True, | |
| ) | |
| logger.info("Docker image built successfully") | |
| container = self.client.containers.run( | |
| image=image.id, | |
| command=test_command, | |
| detach=True, | |
| remove=True, | |
| ) | |
| result = container.wait(timeout=timeout) | |
| output = container.logs().decode("utf-8") | |
| exit_code = result["StatusCode"] | |
| duration = time.time() - start_time | |
| success = exit_code == 0 | |
| logger.info(f"Tests {'passed' if success else 'failed'} in {duration:.2f}s") | |
| return TestResult( | |
| success=success, | |
| output=output, | |
| exit_code=exit_code, | |
| duration=duration, | |
| ) | |
| finally: | |
| if dockerfile_path.exists(): | |
| dockerfile_path.unlink() | |
| try: | |
| self.client.images.remove("reporepair-test:latest", force=True) | |
| except: | |
| pass | |
| except Exception as e: | |
| duration = time.time() - start_time | |
| logger.error(f"Test execution failed: {e}") | |
| return TestResult( | |
| success=False, | |
| output=f"Error: {str(e)}", | |
| exit_code=1, | |
| duration=duration, | |
| ) | |
| def _detect_test_command(self, repo_path: Path) -> Optional[str]: | |
| """ | |
| Auto-detect test command based on project files. | |
| Args: | |
| repo_path: Path to repository | |
| Returns: | |
| Test command or None | |
| """ | |
| if (repo_path / "pytest.ini").exists() or (repo_path / "setup.py").exists(): | |
| return "pytest" | |
| if (repo_path / "package.json").exists(): | |
| import json | |
| try: | |
| package_json = json.loads((repo_path / "package.json").read_text()) | |
| if "test" in package_json.get("scripts", {}): | |
| return "npm test" | |
| except: | |
| pass | |
| if (repo_path / "Makefile").exists(): | |
| makefile_content = (repo_path / "Makefile").read_text() | |
| if "test:" in makefile_content: | |
| return "make test" | |
| if (repo_path / "pom.xml").exists(): | |
| return "mvn test" | |
| if (repo_path / "go.mod").exists(): | |
| return "go test ./..." | |
| if (repo_path / "Cargo.toml").exists(): | |
| return "cargo test" | |
| return None | |
| def _generate_dockerfile(self, repo_path: Path) -> str: | |
| """ | |
| Generate appropriate Dockerfile based on project type. | |
| Args: | |
| repo_path: Path to repository | |
| Returns: | |
| Dockerfile content | |
| """ | |
| if (repo_path / "requirements.txt").exists(): | |
| return """FROM python:3.10-slim | |
| WORKDIR /app | |
| COPY . /app | |
| RUN pip install --no-cache-dir -r requirements.txt | |
| CMD ["pytest"] | |
| """ | |
| if (repo_path / "package.json").exists(): | |
| return """FROM node:18-slim | |
| WORKDIR /app | |
| COPY package*.json ./ | |
| RUN npm ci | |
| COPY . . | |
| CMD ["npm", "test"] | |
| """ | |
| if (repo_path / "go.mod").exists(): | |
| return """FROM golang:1.21-alpine | |
| WORKDIR /app | |
| COPY go.* ./ | |
| RUN go mod download | |
| COPY . . | |
| CMD ["go", "test", "./..."] | |
| """ | |
| if (repo_path / "Cargo.toml").exists(): | |
| return """FROM rust:1.75-slim | |
| WORKDIR /app | |
| COPY . /app | |
| RUN cargo build --release | |
| CMD ["cargo", "test"] | |
| """ | |
| return """FROM alpine:latest | |
| WORKDIR /app | |
| COPY . /app | |
| CMD ["echo", "No tests found"] | |
| """ | |
| def is_docker_available(self) -> bool: | |
| """Check if Docker is available.""" | |
| try: | |
| self.client.ping() | |
| return True | |
| except: | |
| return False | |