109 lines
3.4 KiB
Python
109 lines
3.4 KiB
Python
"""Local Docker daemon, Docker Desktop, and buildx setup."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import subprocess
|
|
import sys
|
|
import time
|
|
|
|
import deploy.ui as ui
|
|
|
|
|
|
BUILDER_NAME = "fromchat-builder"
|
|
|
|
|
|
def ensure_daemon() -> None:
|
|
if _daemon_ok():
|
|
return
|
|
ui.warning("Docker daemon is not running")
|
|
if not _start_desktop():
|
|
ui.error("Failed to start Docker Desktop. Please start it manually and try again.")
|
|
sys.exit(1)
|
|
|
|
|
|
def _daemon_ok() -> bool:
|
|
return subprocess.run(["docker", "info"], capture_output=True).returncode == 0
|
|
|
|
|
|
def _start_desktop() -> bool:
|
|
ui.substep("Starting Docker Desktop...")
|
|
if subprocess.run(["docker", "desktop", "start"], capture_output=True).returncode != 0:
|
|
return False
|
|
ui.substep("Waiting for Docker to start...", end="")
|
|
sys.stdout.flush()
|
|
max_wait = 60
|
|
waited = 0
|
|
while waited < max_wait:
|
|
if _daemon_ok():
|
|
print()
|
|
return True
|
|
time.sleep(2)
|
|
waited += 2
|
|
print(".", end="", flush=True)
|
|
print()
|
|
return False
|
|
|
|
|
|
def ensure_buildx(use_compose_build: bool) -> None:
|
|
if use_compose_build:
|
|
return
|
|
if subprocess.run(["docker", "buildx", "version"], capture_output=True).returncode != 0:
|
|
ui.error("Docker buildx not available. Install Docker Desktop.")
|
|
sys.exit(1)
|
|
_setup_builder()
|
|
|
|
|
|
def _setup_builder() -> None:
|
|
ui.step("Setting up buildx builder")
|
|
name = BUILDER_NAME
|
|
exists = subprocess.run(["docker", "buildx", "inspect", name], capture_output=True).returncode == 0
|
|
if exists:
|
|
if subprocess.run(["docker", "buildx", "use", name], capture_output=True).returncode != 0:
|
|
ui.substep("Recreating builder...")
|
|
subprocess.run(["docker", "buildx", "rm", name], capture_output=True)
|
|
exists = False
|
|
elif subprocess.run(["docker", "buildx", "inspect", name], capture_output=True).returncode != 0:
|
|
ui.substep("Recreating builder (inspection failed)...")
|
|
subprocess.run(["docker", "buildx", "rm", name], capture_output=True)
|
|
exists = False
|
|
if not exists:
|
|
ui.substep("Creating builder with persistent cache...")
|
|
subprocess.run(
|
|
[
|
|
"docker",
|
|
"buildx",
|
|
"create",
|
|
"--name",
|
|
name,
|
|
"--driver",
|
|
"docker-container",
|
|
"--driver-opt",
|
|
"image=moby/buildkit:latest",
|
|
"--use",
|
|
"--bootstrap",
|
|
],
|
|
capture_output=True,
|
|
)
|
|
subprocess.run(["docker", "buildx", "use", name], capture_output=True)
|
|
|
|
|
|
def pull_images(images: list[str], *, platform: str | None = None) -> None:
|
|
"""Pull third-party images on this machine (then transfer via pussh)."""
|
|
from deploy.util import dedupe_preserve
|
|
|
|
unique = dedupe_preserve([img for img in images if img.strip()])
|
|
if not unique:
|
|
return
|
|
ui.step(f"Pulling {len(unique)} remote image(s) locally")
|
|
for image in unique:
|
|
ui.substep(f"Pulling {image}...")
|
|
cmd = ["docker", "pull"]
|
|
if platform:
|
|
cmd.extend(["--platform", platform])
|
|
cmd.append(image)
|
|
if subprocess.run(cmd).returncode != 0:
|
|
ui.error(f"Failed to pull {image}")
|
|
sys.exit(1)
|
|
ui.success("Remote images ready locally")
|
|
|