Files
Netbox-Store/netbox_plugin/netbox_plugin_store/commands.py
T
MrBlake f36d6be511
CI / php-store (push) Waiting to run
CI / python-components (push) Waiting to run
feat: add NetBox plugin store
2026-08-24 20:51:25 +02:00

70 lines
2.2 KiB
Python

from __future__ import annotations
import os
import subprocess
from dataclasses import dataclass
from pathlib import Path
from typing import Sequence
from .redaction import redact_text
class CommandExecutionError(RuntimeError):
def __init__(self, message: str, *, output: str = ""):
super().__init__(message)
self.output = redact_text(output)
@dataclass(frozen=True, slots=True)
class CommandResult:
returncode: int
stdout: str
stderr: str
@property
def output(self) -> str:
return redact_text("\n".join(part for part in (self.stdout, self.stderr) if part))
class SubprocessRunner:
"""Injectable no-shell subprocess boundary."""
def run(
self,
argv: Sequence[str],
*,
timeout: int,
cwd: Path | None = None,
input_text: str | None = None,
) -> CommandResult:
if not argv or any(not isinstance(arg, str) or "\x00" in arg for arg in argv):
raise CommandExecutionError("Refusing to execute invalid argv.")
env = os.environ.copy()
env.update({"PIP_NO_INPUT": "1", "PYTHONUNBUFFERED": "1"})
try:
completed = subprocess.run(
list(argv),
cwd=str(cwd) if cwd else None,
env=env,
shell=False,
check=False,
text=True,
input=input_text,
stdin=subprocess.DEVNULL if input_text is None else None,
capture_output=True,
timeout=timeout,
)
except subprocess.TimeoutExpired as exc:
output = "\n".join(
str(value) for value in (getattr(exc, "stdout", ""), getattr(exc, "stderr", "")) if value
)
raise CommandExecutionError("Command timed out.", output=output) from exc
except OSError as exc:
raise CommandExecutionError(f"Unable to start command: {type(exc).__name__}") from exc
result = CommandResult(completed.returncode, completed.stdout or "", completed.stderr or "")
if completed.returncode != 0:
raise CommandExecutionError(
f"Command failed with exit code {completed.returncode}.", output=result.output
)
return result