"""Docker build service for building images from Dockerfiles.""" import logging import subprocess logger = logging.getLogger(__name__) def build_image(instance_dir: str, dockerfile: str, tag: str, build_context: dict | None = None) -> tuple[int, str, str]: """Build a Docker image from a Dockerfile. Args: instance_dir: Directory containing the Dockerfile dockerfile: Dockerfile content tag: Image tag to apply build_context: Optional build context files {path: content} Returns: Tuple of (returncode, stdout, stderr) """ import os from pathlib import Path # Write Dockerfile dockerfile_path = Path(instance_dir) / "Dockerfile" dockerfile_path.write_text(dockerfile) logger.info("Wrote Dockerfile to %s", dockerfile_path) # Write build context files if build_context: for file_path, content in build_context.items(): full_path = Path(instance_dir) / file_path # Security: ensure path doesn't escape instance_dir try: full_path.resolve().relative_to(Path(instance_dir).resolve()) except ValueError: logger.error("Build context file path escapes instance directory: %s", file_path) raise ValueError(f"Build context file path '{file_path}' escapes instance directory") full_path.parent.mkdir(parents=True, exist_ok=True) full_path.write_text(content) logger.info("Wrote build context file: %s", full_path) # Build image logger.info("Building Docker image with tag: %s", tag) cmd = [ "docker", "build", "-t", tag, "-f", str(dockerfile_path), instance_dir, ] try: result = subprocess.run( cmd, capture_output=True, text=True, timeout=300, # 5 minute timeout for builds ) logger.info("Docker build completed: returncode=%d", result.returncode) if result.returncode != 0: logger.error("Docker build failed: %s", result.stderr[:1000]) return result.returncode, result.stdout, result.stderr except subprocess.TimeoutExpired: logger.error("Docker build timed out after 300 seconds") return 1, "", "Build timed out after 300 seconds" except Exception as exc: logger.exception("Docker build failed: %s", exc) return 1, "", str(exc)