import docker from docker.errors import DockerException, ImageNotFound import logging from pathlib import Path logger = logging.getLogger(__name__) class ContainerBuilder: """Builds and manages the development environment Docker image""" IMAGE_NAME = "sandbox-devenv" IMAGE_TAG = "latest" def __init__(self): try: self.client = docker.from_env() logger.info("Container builder initialized") except DockerException as e: logger.error(f"Failed to initialize Docker client: {e}") raise RuntimeError("Docker is not available") from e def image_exists(self) -> bool: """Check if devenv image exists""" try: self.client.images.get(f"{self.IMAGE_NAME}:{self.IMAGE_TAG}") return True except ImageNotFound: return False def build_devenv_image(self) -> bool: """ Build the development environment image. Returns: True if successful, False otherwise """ try: dockerfile_path = Path(__file__).parent / "images" / "devenv.Dockerfile" if not dockerfile_path.exists(): logger.error(f"Dockerfile not found: {dockerfile_path}") return False logger.info("Building development environment image (this may take several minutes)...") # Build image image, build_logs = self.client.images.build( path=str(dockerfile_path.parent), dockerfile=str(dockerfile_path.name), tag=f"{self.IMAGE_NAME}:{self.IMAGE_TAG}", rm=True, # Remove intermediate containers pull=True, # Pull base image decode=True # Decode build logs ) # Print build progress for log in build_logs: if 'stream' in log: print(log['stream'], end='') elif 'error' in log: logger.error(f"Build error: {log['error']}") return False logger.info(f"Successfully built {self.IMAGE_NAME}:{self.IMAGE_TAG}") return True except Exception as e: logger.error(f"Failed to build image: {e}", exc_info=True) return False def ensure_devenv_image(self) -> bool: """ Ensure devenv image exists, build if necessary. Returns: True if image is available, False otherwise """ if self.image_exists(): logger.info(f"Image {self.IMAGE_NAME}:{self.IMAGE_TAG} already exists") return True logger.info(f"Image {self.IMAGE_NAME}:{self.IMAGE_TAG} not found, building...") return self.build_devenv_image()