Source code for contree_sdk.docker.events

"""Typed build notifications and per-build event state."""

import time
from dataclasses import dataclass
from typing import Literal

from contree_client.models import InstanceResult


BuildEventType = Literal[
    "step_started", "operation_started", "stdout", "stderr", "cache_hit", "step_completed", "step_failed"
]


[docs] @dataclass(frozen=True) class BuildStep: """One directive execution; IDs start at zero within each build. index is the parsed Dockerfile position, or None for a synthetic step. parent_id identifies the enclosing directive for nested execution. """ id: int keyword: str index: int | None = None parent_id: int | None = None
[docs] @dataclass(frozen=True, kw_only=True) class BuildEvent: """A build notification with raw output bytes and the current operation identity. result is available after the operation finishes, including nonzero exits. Errors retain their original exception object. Output is never decoded or formatted. """ type: BuildEventType step: BuildStep operation_uuid: str | None = None image_before: str | None = None image_after: str | None = None duration: float = 0.0 data: bytes = b"" result: InstanceResult | None = None error: BaseException | None = None
@dataclass class BuildFrame: step: BuildStep started: float image_before: str | None operation_uuid: str | None = None result: InstanceResult | None = None @dataclass class BuildProgress: sequence: int = 0 current: BuildFrame | None = None def start(self, keyword: str, index: int | None, image: str | None) -> None: parent = self.current.step.id if self.current is not None else None self.current = BuildFrame(BuildStep(self.sequence, keyword, index, parent), time.monotonic(), image) self.sequence += 1 def event( self, event_type: BuildEventType, image: str | None, *, data: bytes = b"", error: BaseException | None = None ) -> BuildEvent | None: if self.current is None: return None frame = self.current return BuildEvent( type=event_type, step=frame.step, operation_uuid=frame.operation_uuid, image_before=frame.image_before, image_after=image, duration=time.monotonic() - frame.started, data=data, result=frame.result, error=error, )