from __future__ import annotations import asyncio import logging from .compose_manager import ComposeManager from .models import CommandResult logger = logging.getLogger(__name__) class RegistryLoginManager: def __init__( self, *, enabled: bool, server: str, username: str, password: str, timeout_seconds: int, compose_manager: ComposeManager, ) -> None: self.enabled = enabled self.server = server self.username = username self.password = password self.timeout_seconds = timeout_seconds self.compose_manager = compose_manager async def login_if_needed(self) -> CommandResult: if not self.enabled: return CommandResult(success=True, stdout="registry login skipped", returncode=0) command = [ "docker", "login", self.server, "-u", self.username, "-p", self.password, ] logger.info("开始执行私有仓库登录: %s", self.server) result = await self._run_command(command) if result.success: logger.info("私有仓库登录成功: %s", self.server) else: logger.error("私有仓库登录失败: %s", result.stderr or result.stdout) return result async def _run_command(self, command: list[str]) -> CommandResult: process = await asyncio.create_subprocess_exec( *command, cwd=str(self.compose_manager.working_dir), stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, ) try: stdout, stderr = await asyncio.wait_for(process.communicate(), timeout=self.timeout_seconds) except asyncio.TimeoutError: process.kill() await process.wait() return CommandResult(success=False, stderr=f"registry login timeout after {self.timeout_seconds}s", returncode=-1) return CommandResult( success=process.returncode == 0, stdout=stdout.decode("utf-8", errors="ignore"), stderr=stderr.decode("utf-8", errors="ignore"), returncode=process.returncode or 0, )