Files
DECNET/decnet/lifecycle/events.py
anti c0ad380020 feat(lifecycle): runner + strategies + bus topic
Add decnet.lifecycle package: pure orchestration layer that the
master API will invoke via asyncio.create_task to drive DeckyLifecycle
rows through pending -> running -> succeeded | failed without
holding an HTTP request open.

Strategy classes per (operation, transport):
- LocalDeployStrategy: master-resident, runs engine.deployer.deploy
  in a thread.
- SwarmDeployStrategy: shards by host_uuid, dispatches via
  AgentClient.deploy; worker drives terminal via heartbeat.
- LocalMutateStrategy: write_compose + compose up.
- SwarmMutateStrategy: AgentClient.mutate; worker drives terminal.

decnet.bus.topics gains decky_lifecycle(name) -> decky.<name>.lifecycle
plus DECKY_LIFECYCLE constant. Payload documented in the wiki
(separate commit). publish_safely keeps bus best-effort.

Nothing is wired to call this yet -- next commits convert worker
/deploy /mutate to 202, then heartbeat delta wiring, then master API.
2026-05-22 16:25:33 -04:00

44 lines
1.2 KiB
Python

"""Bus emit helper for DeckyLifecycle transitions.
DB is the source of truth (wizard polls ``GET /deckies/lifecycle?ids=``).
The bus is best-effort live notification — publish failures are logged
and swallowed via ``publish_safely``, never propagated.
"""
from __future__ import annotations
from typing import Optional
from decnet.bus import topics as _topics
from decnet.bus.base import BaseBus
from decnet.bus.publish import publish_safely
async def emit_lifecycle(
bus: BaseBus | None,
*,
lifecycle_id: str,
decky_name: str,
operation: str,
status: str,
error: Optional[str] = None,
) -> None:
"""Publish ``decky.<name>.lifecycle`` with the current transition.
Payload keys: ``lifecycle_id``, ``operation``, ``status`` and
optionally ``error``. Documented in
``wiki-checkout/Service-Bus.md``.
"""
payload: dict = {
"lifecycle_id": lifecycle_id,
"operation": operation,
"status": status,
}
if error is not None:
payload["error"] = error
await publish_safely(
bus,
_topics.decky_lifecycle(decky_name),
payload,
event_type=_topics.DECKY_LIFECYCLE,
)