75 lines
2.3 KiB
Python
Executable File
75 lines
2.3 KiB
Python
Executable File
"""Action runner — legacy entry point for single-action execution.
|
|
|
|
F-05 remediation:
|
|
The original implementation treated ``action["cmd"]`` as a callable
|
|
and invoked it directly, which is undefined behavior for normal
|
|
shell-command actions. This module now delegates to
|
|
:func:`backend.executor.runtime_router.execute_action`, which is the
|
|
same path the pipeline engine uses — so a target routed here gets
|
|
the same real subprocess + runtime-selection logic as a target
|
|
routed through the engine.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
from typing import Any, Mapping, Optional
|
|
|
|
from backend.events.bus import EventBus
|
|
from backend.executor.runtime_router import execute_action
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
def run_action(
|
|
action: Mapping[str, Any],
|
|
node: Mapping[str, Any],
|
|
bus: EventBus,
|
|
) -> int:
|
|
"""Execute one action on the chosen node and emit bus events.
|
|
|
|
Emits ``task_update(running)`` before dispatch and
|
|
``task_update(done|failed)`` after. Returns the subprocess exit
|
|
code (0 = success).
|
|
"""
|
|
action_name = action.get("name", "<unnamed>")
|
|
node_name = node.get("name", "localhost") if isinstance(node, Mapping) else "localhost"
|
|
|
|
bus.emit(
|
|
"task_update",
|
|
node=node_name,
|
|
action=action_name,
|
|
state="running",
|
|
)
|
|
|
|
# Normalise the action dict so runtime_router can consume it.
|
|
# Accept either ``cmd`` (legacy) or ``command`` (runtime_router
|
|
# convention). runtime_router looks at action["command"].
|
|
normalised = dict(action)
|
|
if "command" not in normalised and "cmd" in normalised:
|
|
normalised["command"] = normalised["cmd"]
|
|
|
|
workspace: Optional[str] = normalised.get("dir") or normalised.get("cwd") or "/tmp"
|
|
|
|
try:
|
|
rc = execute_action(normalised, workspace, dict(node) if isinstance(node, Mapping) else {"name": node_name})
|
|
state = "done" if rc == 0 else "failed"
|
|
except Exception as e:
|
|
log.exception("action %s crashed", action_name)
|
|
bus.emit(
|
|
"failure",
|
|
node=node_name,
|
|
action=action_name,
|
|
state="failed",
|
|
reason=f"{type(e).__name__}: {e}",
|
|
)
|
|
return 1
|
|
|
|
bus.emit(
|
|
"task_update",
|
|
node=node_name,
|
|
action=action_name,
|
|
state=state,
|
|
)
|
|
return 0 if state == "done" else 1
|