From e94e2c1902d0570fadfdd65accba95b11692260d Mon Sep 17 00:00:00 2001 From: ruv Date: Sun, 15 Mar 2026 11:02:57 -0400 Subject: [PATCH] fix(ci): handle missing 'ip' command in QEMU swarm orchestrator The IDF container doesn't have iproute2 installed, so 'ip' binary is missing. Add shutil.which() check to can_tap guard and catch FileNotFoundError in _run_ip() for robustness. Co-Authored-By: claude-flow --- scripts/qemu_swarm.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/scripts/qemu_swarm.py b/scripts/qemu_swarm.py index 9cdc2883..2747eac1 100644 --- a/scripts/qemu_swarm.py +++ b/scripts/qemu_swarm.py @@ -326,7 +326,12 @@ class NetworkState: def _run_ip(args: List[str], check: bool = False) -> subprocess.CompletedProcess: - return subprocess.run(["ip"] + args, capture_output=True, text=True, check=check) + try: + return subprocess.run(["ip"] + args, capture_output=True, text=True, check=check) + except FileNotFoundError: + # 'ip' command not installed (e.g. minimal container image) + return subprocess.CompletedProcess(args=["ip"] + args, returncode=127, + stdout="", stderr="ip: command not found") def setup_network(cfg: SwarmConfig, net: NetworkState) -> Dict[int, List[str]]: @@ -338,8 +343,10 @@ def setup_network(cfg: SwarmConfig, net: NetworkState) -> Dict[int, List[str]]: node_net_args: Dict[int, List[str]] = {} n = len(cfg.nodes) - # Check if we can use TAP/bridge (requires root on Linux) - can_tap = IS_LINUX and hasattr(os, 'geteuid') and os.geteuid() == 0 + # Check if we can use TAP/bridge (requires root on Linux + ip command) + import shutil + can_tap = (IS_LINUX and hasattr(os, 'geteuid') and os.geteuid() == 0 + and shutil.which("ip") is not None) if not can_tap: if IS_LINUX: