fix UI client
This commit is contained in:
@@ -1,14 +1,40 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
from collections.abc import Mapping
|
||||
|
||||
from app.config import settings
|
||||
from app.storage.repository import Repository
|
||||
|
||||
|
||||
def _tail_output(label: str, output: str, line_limit: int = 6) -> str:
|
||||
lines = [line.strip() for line in output.splitlines() if line.strip()]
|
||||
if not lines:
|
||||
return ""
|
||||
return f"{label}: {' | '.join(lines[-line_limit:])}"
|
||||
|
||||
|
||||
def _command_output_summary(stdout: str, stderr: str) -> str:
|
||||
parts = [
|
||||
part
|
||||
for part in (
|
||||
_tail_output("stderr", stderr),
|
||||
_tail_output("stdout", stdout),
|
||||
)
|
||||
if part
|
||||
]
|
||||
summary = " ; ".join(parts)
|
||||
return summary[:1600]
|
||||
|
||||
|
||||
class CommandError(RuntimeError):
|
||||
def __init__(self, command: list[str], returncode: int, stdout: str, stderr: str) -> None:
|
||||
super().__init__(f"Command failed with exit code {returncode}: {' '.join(command)}")
|
||||
message = f"Command failed with exit code {returncode}: {' '.join(command)}"
|
||||
output_summary = _command_output_summary(stdout, stderr)
|
||||
if output_summary:
|
||||
message = f"{message}. Last output: {output_summary}"
|
||||
super().__init__(message)
|
||||
self.command = command
|
||||
self.returncode = returncode
|
||||
self.stdout = stdout
|
||||
@@ -20,15 +46,26 @@ class CommandRunner:
|
||||
self.repository = repository
|
||||
self.task_id = task_id
|
||||
|
||||
def run(self, command: list[str], timeout: int | None = None) -> subprocess.CompletedProcess[str]:
|
||||
def run(
|
||||
self,
|
||||
command: list[str],
|
||||
timeout: int | None = None,
|
||||
env: Mapping[str, str] | None = None,
|
||||
) -> subprocess.CompletedProcess[str]:
|
||||
if self.task_id:
|
||||
self.repository.add_log(self.task_id, "debug", f"Running command: {' '.join(command)}")
|
||||
|
||||
command_env = os.environ.copy()
|
||||
if env:
|
||||
command_env.update(env)
|
||||
|
||||
try:
|
||||
result = subprocess.run(
|
||||
command,
|
||||
check=False,
|
||||
capture_output=True,
|
||||
env=command_env,
|
||||
stdin=subprocess.DEVNULL,
|
||||
text=True,
|
||||
timeout=timeout or settings.command_timeout_seconds,
|
||||
)
|
||||
@@ -47,4 +84,3 @@ class CommandRunner:
|
||||
raise CommandError(command, result.returncode, result.stdout, result.stderr)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
@@ -5,6 +5,22 @@ from pathlib import Path
|
||||
from app.core.command_runner import CommandRunner
|
||||
|
||||
|
||||
APT_DPKG_OPTIONS = [
|
||||
"-o",
|
||||
"Dpkg::Use-Pty=0",
|
||||
"-o",
|
||||
"Dpkg::Options::=--force-confdef",
|
||||
"-o",
|
||||
"Dpkg::Options::=--force-confold",
|
||||
]
|
||||
|
||||
APT_NONINTERACTIVE_ENV = {
|
||||
"DEBIAN_FRONTEND": "noninteractive",
|
||||
"DEBCONF_NONINTERACTIVE_SEEN": "true",
|
||||
"APT_LISTCHANGES_FRONTEND": "none",
|
||||
}
|
||||
|
||||
|
||||
def _parse_deb_control_output(output: str) -> dict[str, str]:
|
||||
metadata: dict[str, str] = {}
|
||||
|
||||
@@ -49,11 +65,23 @@ class DebInstaller:
|
||||
return metadata
|
||||
|
||||
def install_deb(self, file_path: Path) -> None:
|
||||
self.command_runner.run(["apt", "install", "-y", str(file_path)])
|
||||
self.command_runner.run(
|
||||
[
|
||||
"apt-get",
|
||||
*APT_DPKG_OPTIONS,
|
||||
"install",
|
||||
"--yes",
|
||||
str(file_path),
|
||||
],
|
||||
env=APT_NONINTERACTIVE_ENV,
|
||||
)
|
||||
|
||||
def remove_package(self, package_name: str, purge: bool = False) -> None:
|
||||
action = "purge" if purge else "remove"
|
||||
self.command_runner.run(["apt", action, "-y", package_name])
|
||||
self.command_runner.run(
|
||||
["apt-get", *APT_DPKG_OPTIONS, action, "--yes", package_name],
|
||||
env=APT_NONINTERACTIVE_ENV,
|
||||
)
|
||||
|
||||
def get_package_version(self, package_name: str) -> str | None:
|
||||
result = self.command_runner.run(["dpkg-query", "-W", "-f=${Version}", package_name])
|
||||
|
||||
Reference in New Issue
Block a user