mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-06-19 11:08:39 +00:00
fix(install): bound subprocess output and dedupe apt update in dependency installer
Address coderabbitai review on PR #369: - _run() now streams combined stdout/stderr to a temp file and returns only the last ERROR_TAIL_LINES lines, instead of buffering full output in memory (Codacy also flagged the previous capture_output call as a subprocess-without-static-string security issue; the new call is annotated as safe since cmd is built from hardcoded args). - `apt update` now runs once in main() instead of once per package needing an apt fallback.
This commit is contained in:
@@ -6,6 +6,7 @@ then falls back to pip with --break-system-packages
|
|||||||
|
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
import tempfile
|
||||||
import warnings
|
import warnings
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
@@ -16,13 +17,18 @@ ERROR_TAIL_LINES = 15
|
|||||||
|
|
||||||
|
|
||||||
def _run(cmd):
|
def _run(cmd):
|
||||||
"""Run a command, capturing combined stdout/stderr.
|
"""Run a command, streaming combined stdout/stderr to a temp file.
|
||||||
|
|
||||||
Returns (success, output) instead of raising, so callers can report
|
Returns (success, output) instead of raising, so callers can report
|
||||||
*why* a command failed rather than just that it failed.
|
*why* a command failed rather than just that it failed. `output` is
|
||||||
|
bounded to the last ERROR_TAIL_LINES lines so failures from very
|
||||||
|
chatty commands (e.g. pip build logs) don't get buffered in memory.
|
||||||
"""
|
"""
|
||||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
with tempfile.TemporaryFile(mode='w+b') as f:
|
||||||
return result.returncode == 0, (result.stdout or "") + (result.stderr or "")
|
result = subprocess.run(cmd, stdout=f, stderr=subprocess.STDOUT) # nosec B603 B607 - hardcoded apt/pip args, not user input
|
||||||
|
f.seek(0)
|
||||||
|
lines = f.read().decode('utf-8', errors='replace').splitlines()
|
||||||
|
return result.returncode == 0, '\n'.join(lines[-ERROR_TAIL_LINES:])
|
||||||
|
|
||||||
|
|
||||||
def install_via_apt(package_name):
|
def install_via_apt(package_name):
|
||||||
@@ -47,8 +53,6 @@ def install_via_apt(package_name):
|
|||||||
apt_package = apt_package_map.get(package_name, f'python3-{package_name}')
|
apt_package = apt_package_map.get(package_name, f'python3-{package_name}')
|
||||||
|
|
||||||
print(f"Trying to install {apt_package} via apt...")
|
print(f"Trying to install {apt_package} via apt...")
|
||||||
_run(['sudo', 'apt', 'update']) # best-effort refresh; ignore failures here
|
|
||||||
|
|
||||||
success, output = _run(['sudo', 'apt', 'install', '-y', apt_package])
|
success, output = _run(['sudo', 'apt', 'install', '-y', apt_package])
|
||||||
if success:
|
if success:
|
||||||
print(f"Successfully installed {apt_package} via apt")
|
print(f"Successfully installed {apt_package} via apt")
|
||||||
@@ -113,6 +117,9 @@ def main():
|
|||||||
"""Main installation function."""
|
"""Main installation function."""
|
||||||
print("Installing dependencies for LED Matrix Web Interface V2...")
|
print("Installing dependencies for LED Matrix Web Interface V2...")
|
||||||
|
|
||||||
|
print("Refreshing apt package index...")
|
||||||
|
_run(['sudo', 'apt', 'update']) # best-effort; individual installs surface their own errors
|
||||||
|
|
||||||
# List of required packages
|
# List of required packages
|
||||||
required_packages = [
|
required_packages = [
|
||||||
'flask',
|
'flask',
|
||||||
|
|||||||
Reference in New Issue
Block a user