fix: TUI should not pull contianers on start, fixed image detection logic bug
This commit is contained in:
parent
f4576868a6
commit
59f45a2db7
1 changed files with 98 additions and 18 deletions
|
|
@ -505,36 +505,116 @@ class ContainerManager:
|
||||||
digests[image] = stdout.strip().splitlines()[0]
|
digests[image] = stdout.strip().splitlines()[0]
|
||||||
return digests
|
return digests
|
||||||
|
|
||||||
def _parse_compose_images(self) -> list[str]:
|
def _extract_images_from_compose_config(self, text: str, tried_json: bool) -> set[str]:
|
||||||
"""Best-effort parse of image names from compose files without YAML dependency."""
|
"""
|
||||||
|
Try JSON first (if we asked for it or it looks like JSON), then YAML if available.
|
||||||
|
Returns a set of image names.
|
||||||
|
"""
|
||||||
images: set[str] = set()
|
images: set[str] = set()
|
||||||
for compose in [self.compose_file, self.cpu_compose_file]:
|
|
||||||
|
# Try JSON parse
|
||||||
|
if tried_json or (text.lstrip().startswith("{") and text.rstrip().endswith("}")):
|
||||||
try:
|
try:
|
||||||
if not compose.exists():
|
cfg = json.loads(text)
|
||||||
|
services = cfg.get("services", {})
|
||||||
|
for _, svc in services.items():
|
||||||
|
image = svc.get("image")
|
||||||
|
if image:
|
||||||
|
images.add(str(image))
|
||||||
|
if images:
|
||||||
|
return images
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Try YAML (if available) - import here to avoid hard dependency
|
||||||
|
try:
|
||||||
|
import yaml
|
||||||
|
cfg = yaml.safe_load(text) or {}
|
||||||
|
services = cfg.get("services", {})
|
||||||
|
if isinstance(services, dict):
|
||||||
|
for _, svc in services.items():
|
||||||
|
if isinstance(svc, dict):
|
||||||
|
image = svc.get("image")
|
||||||
|
if image:
|
||||||
|
images.add(str(image))
|
||||||
|
if images:
|
||||||
|
return images
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return images
|
||||||
|
|
||||||
|
async def _parse_compose_images(self) -> list[str]:
|
||||||
|
"""Get resolved image names from compose files using docker/podman compose, with robust fallbacks."""
|
||||||
|
images: set[str] = set()
|
||||||
|
|
||||||
|
compose_files = [self.compose_file, self.cpu_compose_file]
|
||||||
|
for compose_file in compose_files:
|
||||||
|
try:
|
||||||
|
if not compose_file or not compose_file.exists():
|
||||||
continue
|
continue
|
||||||
for line in compose.read_text().splitlines():
|
|
||||||
line = line.strip()
|
cpu_mode = (compose_file == self.cpu_compose_file)
|
||||||
if not line or line.startswith("#"):
|
|
||||||
|
# Try JSON format first
|
||||||
|
success, stdout, _ = await self._run_compose_command(
|
||||||
|
["config", "--format", "json"],
|
||||||
|
cpu_mode=cpu_mode
|
||||||
|
)
|
||||||
|
|
||||||
|
if success and stdout.strip():
|
||||||
|
from_cfg = self._extract_images_from_compose_config(stdout, tried_json=True)
|
||||||
|
if from_cfg:
|
||||||
|
images.update(from_cfg)
|
||||||
|
continue # this compose file succeeded; move to next file
|
||||||
|
|
||||||
|
# Fallback to YAML output (for older compose versions)
|
||||||
|
success, stdout, _ = await self._run_compose_command(
|
||||||
|
["config"],
|
||||||
|
cpu_mode=cpu_mode
|
||||||
|
)
|
||||||
|
|
||||||
|
if success and stdout.strip():
|
||||||
|
from_cfg = self._extract_images_from_compose_config(stdout, tried_json=False)
|
||||||
|
if from_cfg:
|
||||||
|
images.update(from_cfg)
|
||||||
continue
|
continue
|
||||||
if line.startswith("image:"):
|
|
||||||
# image: repo/name:tag
|
|
||||||
val = line.split(":", 1)[1].strip()
|
|
||||||
# Remove quotes if present
|
|
||||||
if (val.startswith('"') and val.endswith('"')) or (
|
|
||||||
val.startswith("'") and val.endswith("'")
|
|
||||||
):
|
|
||||||
val = val[1:-1]
|
|
||||||
images.add(val)
|
|
||||||
except Exception:
|
except Exception:
|
||||||
|
# Keep behavior resilient—just continue to next file
|
||||||
continue
|
continue
|
||||||
return list(images)
|
|
||||||
|
# Fallback: manual parsing if compose config didn't work
|
||||||
|
if not images:
|
||||||
|
for compose in compose_files:
|
||||||
|
try:
|
||||||
|
if not compose.exists():
|
||||||
|
continue
|
||||||
|
for line in compose.read_text().splitlines():
|
||||||
|
line = line.strip()
|
||||||
|
if not line or line.startswith("#"):
|
||||||
|
continue
|
||||||
|
if line.startswith("image:"):
|
||||||
|
# image: repo/name:tag
|
||||||
|
val = line.split(":", 1)[1].strip()
|
||||||
|
# Remove quotes if present
|
||||||
|
if (val.startswith('"') and val.endswith('"')) or (
|
||||||
|
val.startswith("'") and val.endswith("'")
|
||||||
|
):
|
||||||
|
val = val[1:-1]
|
||||||
|
if val:
|
||||||
|
images.add(val)
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
|
||||||
|
return sorted(images)
|
||||||
|
|
||||||
async def get_project_images_info(self) -> list[tuple[str, str]]:
|
async def get_project_images_info(self) -> list[tuple[str, str]]:
|
||||||
"""
|
"""
|
||||||
Return list of (image, digest_or_id) for images referenced by compose files.
|
Return list of (image, digest_or_id) for images referenced by compose files.
|
||||||
If an image isn't present locally, returns '-' for its digest.
|
If an image isn't present locally, returns '-' for its digest.
|
||||||
"""
|
"""
|
||||||
expected = self._parse_compose_images()
|
expected = await self._parse_compose_images()
|
||||||
results: list[tuple[str, str]] = []
|
results: list[tuple[str, str]] = []
|
||||||
for image in expected:
|
for image in expected:
|
||||||
digest = "-"
|
digest = "-"
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue