refactor: BottleImages dataclass, prelaunch_checks, build_or_load_images
lint / lint (push) Failing after 2m2s
test / unit (pull_request) Successful in 1m0s
test / integration (pull_request) Successful in 15s
test / coverage (pull_request) Successful in 1m8s

Addresses review comments 3098, 3099, 3100 on PR #336:

- Add BottleImages(agent, sidecar) dataclass to backend/__init__.py.
  Docker/macOS backends use str image refs; smolmachines uses Path
  artifacts. Replaces the singular `image` variable from the canonical
  pattern in comment 3100.

- Replace _image_stale_checks/skip_stale with public prelaunch_checks().
  CLI now calls backend.prelaunch_checks(plan) before backend.launch(plan);
  if StaleImageError is raised and the operator confirms, launch proceeds
  without re-checking. Removes the while-True/skip_stale retry loop.

- Add abstract _build_or_load_images(plan) -> BottleImages to
  BottleBackend. launch() calls it then passes images to _launch_impl.
  Each backend implements both methods.

- Fix comment 3098 (macos-container): _build_images is removed.
  build_or_load_images() has separate fresh/cached code paths — the
  cached path never calls a build helper.

- Update _start_bundle (smolmachines) to accept sidecar_artifact: Path
  directly. Sidecar artifact resolution moves to _sidecar_from_path(),
  called by build_or_load_images alongside _agent_from_path().
This commit is contained in:
2026-07-10 20:25:24 +00:00
parent 62365d527a
commit 99872e39cd
14 changed files with 243 additions and 228 deletions
+28 -16
View File
@@ -49,6 +49,7 @@ from ...bottle_state import (
git_gate_state_dir,
read_committed_image,
)
from .. import BottleImages
from . import loopback_alias as _loopback
from . import port_forward as _forward
from . import sidecar_bundle as _bundle
@@ -76,13 +77,32 @@ _GIT_HTTP_PORT = 9420
_SUPERVISE_PORT = SUPERVISE_PORT
def build_or_load_images(plan: SmolmachinesBottlePlan) -> BottleImages:
"""Return pre-built or freshly built smolmachine artifact paths."""
return BottleImages(
agent=_agent_from_path(plan),
sidecar=_sidecar_from_path(plan),
)
def _sidecar_from_path(plan: SmolmachinesBottlePlan) -> Path:
"""Return the sidecar bundle artifact path, building it if needed."""
if _image_policy(plan) == "cached":
return _cached_smolmachine(_bundle.SIDECAR_BUNDLE_IMAGE, label="sidecar")
return _ensure_smolmachine(
_bundle.SIDECAR_BUNDLE_IMAGE,
dockerfile=_bundle.SIDECAR_BUNDLE_DOCKERFILE,
)
@contextmanager
def launch(
plan: SmolmachinesBottlePlan,
images: BottleImages,
*,
provision: Callable[[SmolmachinesBottlePlan, "SmolmachinesBottle"], str | None],
) -> Generator[SmolmachinesBottle, None, None]:
"""Build + run the bottle and yield a handle; tear everything
"""Run the bottle from pre-built images and yield a handle; tear everything
down on exit. Errors during bringup unwind any partial state
via the ExitStack."""
stack = ExitStack()
@@ -90,11 +110,9 @@ def launch(
loopback_ip, network = _allocate_resources(plan, stack)
plan = _mint_certs(plan)
proxy_host = loopback_ip
plan = _start_bundle(plan, network, proxy_host, stack)
plan = _start_bundle(plan, network, proxy_host, Path(images.sidecar), stack)
agent_from_path = _agent_from_path(plan)
_launch_vm(plan, agent_from_path, proxy_host, stack)
_launch_vm(plan, Path(images.agent), proxy_host, stack)
_init_vm(plan)
bottle = SmolmachinesBottle(
@@ -171,24 +189,18 @@ def _start_bundle(
plan: SmolmachinesBottlePlan,
network: str,
proxy_host: str,
sidecar_artifact: Path,
stack: ExitStack,
) -> SmolmachinesBottlePlan:
"""Build the BundleLaunchSpec, start the sidecar VM, wrap its raw
smolVM-published loopback ports with per-bottle forwarders, stamp
agent URLs from those forwarder ports, and register teardown."""
"""Build the BundleLaunchSpec, start the sidecar VM from the pre-resolved
artifact, wrap its raw smolVM-published loopback ports with per-bottle
forwarders, stamp agent URLs from those forwarder ports, and register teardown."""
plan = _provision_git_gate_keys(plan)
bundle_spec = _bundle_launch_spec(plan, network, proxy_host)
token_env = _resolve_token_env(plan, dict(os.environ))
if _image_policy(plan) == "cached":
artifact = _cached_smolmachine(bundle_spec.image, label="sidecar")
else:
artifact = _ensure_smolmachine(
bundle_spec.image,
dockerfile=_bundle.SIDECAR_BUNDLE_DOCKERFILE,
)
launch = _bundle.start_bundle_vm(
bundle_spec,
from_path=artifact,
from_path=sidecar_artifact,
host_env={**os.environ, **token_env},
)
stack.callback(_bundle.stop_bundle_vm, plan.slug)