refactor(image-cache): replace warn_if_stale with StaleImageError; add launch template
- image_cache: StaleImageError exception + check_stale/check_stale_path (raise instead of warn) - BottleBackend.launch: template method (skip_stale flag) that calls _image_stale_checks then _launch_impl - Each backend: _image_stale_checks delegates to a stale_checks() function in its launch module; _launch_impl replaces launch override - macos_container: adds image_created_at to util, cached-image support in _build_images, stale_checks - cli/start.py: catches StaleImageError, prompts interactively, retries with skip_stale=True; headless mode dies on it
This commit is contained in:
@@ -10,6 +10,7 @@ import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
import time
|
||||
from datetime import datetime, timezone
|
||||
from typing import Iterable
|
||||
|
||||
from ...log import die, info
|
||||
@@ -458,6 +459,39 @@ def image_id(ref: str) -> str:
|
||||
raise AssertionError("unreachable")
|
||||
|
||||
|
||||
def image_created_at(ref: str) -> datetime:
|
||||
"""Return the image creation timestamp as an aware UTC datetime.
|
||||
|
||||
Parses the `created` field from `container image inspect` JSON output."""
|
||||
result = subprocess.run(
|
||||
[_CONTAINER, "image", "inspect", ref],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
)
|
||||
if result.returncode != 0:
|
||||
die(
|
||||
f"container image inspect for {ref!r} failed: "
|
||||
f"{(result.stderr or '').strip() or '<no stderr>'}"
|
||||
)
|
||||
try:
|
||||
data = json.loads(result.stdout or "{}")
|
||||
except json.JSONDecodeError as exc:
|
||||
die(f"container image inspect for {ref!r} returned malformed JSON: {exc}")
|
||||
if isinstance(data, list) and data:
|
||||
data = data[0]
|
||||
if isinstance(data, dict):
|
||||
value = data.get("created") or data.get("Created")
|
||||
if isinstance(value, str) and value:
|
||||
try:
|
||||
ts = value.rstrip("Z")
|
||||
return datetime.fromisoformat(ts).replace(tzinfo=timezone.utc)
|
||||
except ValueError:
|
||||
pass
|
||||
die(f"container image inspect for {ref!r} did not include a creation timestamp")
|
||||
raise AssertionError("unreachable")
|
||||
|
||||
|
||||
def save(ref: str, output: str) -> None:
|
||||
subprocess.run([_CONTAINER, "image", "save", ref, "-o", output], check=True)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user