refactor(installer): extract run_command helper to cut cognitive complexity
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
8f226dce99
commit
aa92ed30b3
@ -10,6 +10,7 @@ Follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · versioned wi
|
||||
- **`npm run qa`**: central QA command (`scripts/qa.mjs`) with timed stages (lint, python/node unit tests, Playwright, security scans, bundle freshness) and an opt-in SonarQube stage (`--sonar`, token from the environment only). See `architecture/qa.md`.
|
||||
|
||||
### Changed
|
||||
- `guided_installer.py`: command execution extracted into a testable `run_command` helper (lowers cognitive complexity, python:S3776); no behaviour change.
|
||||
- **Voice list rows no longer show the "Source unknown" badge** (it crowded the play buttons). The badge stays in the voice inspector, next to the Source & consent panel.
|
||||
|
||||
### Fixed
|
||||
|
||||
@ -46,6 +46,21 @@ MODEL_EXPLANATIONS = {
|
||||
}
|
||||
|
||||
|
||||
def run_command(cmd: list[str], log) -> bool:
|
||||
"""Run one command, log its output; return False if it failed (non-zero exit or exception)."""
|
||||
try:
|
||||
proc = subprocess.run(cmd, cwd=ROOT, text=True, capture_output=True)
|
||||
except Exception as exc:
|
||||
log(f"Failed: {exc}")
|
||||
return False
|
||||
for out in (proc.stdout, proc.stderr):
|
||||
if out:
|
||||
log(out)
|
||||
if proc.returncode:
|
||||
log(f"Command exited with {proc.returncode}")
|
||||
return not proc.returncode
|
||||
|
||||
|
||||
class GuidedInstaller(tk.Tk):
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
@ -192,17 +207,7 @@ class GuidedInstaller(tk.Tk):
|
||||
self.log_line(f"\n== {title} ==")
|
||||
for cmd in commands:
|
||||
self.log_line("$ " + " ".join(cmd))
|
||||
try:
|
||||
proc = subprocess.run(cmd, cwd=ROOT, text=True, capture_output=True)
|
||||
if proc.stdout:
|
||||
self.log_line(proc.stdout)
|
||||
if proc.stderr:
|
||||
self.log_line(proc.stderr)
|
||||
if proc.returncode:
|
||||
self.log_line(f"Command exited with {proc.returncode}")
|
||||
break
|
||||
except Exception as exc:
|
||||
self.log_line(f"Failed: {exc}")
|
||||
if not run_command(cmd, self.log_line):
|
||||
break
|
||||
if cleanup_dir:
|
||||
shutil.rmtree(cleanup_dir, ignore_errors=True)
|
||||
|
||||
31
tests/test_guided_installer.py
Normal file
31
tests/test_guided_installer.py
Normal file
@ -0,0 +1,31 @@
|
||||
import sys
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
||||
try:
|
||||
from guided_installer import run_command
|
||||
except ImportError: # no tkinter on this host
|
||||
run_command = None
|
||||
|
||||
|
||||
@unittest.skipIf(run_command is None, "tkinter unavailable")
|
||||
class RunCommandTest(unittest.TestCase):
|
||||
def test_success_logs_output(self):
|
||||
out = []
|
||||
self.assertTrue(run_command([sys.executable, "-c", "print('hi')"], out.append))
|
||||
self.assertIn("hi", "".join(out))
|
||||
|
||||
def test_nonzero_exit(self):
|
||||
out = []
|
||||
self.assertFalse(run_command([sys.executable, "-c", "raise SystemExit(3)"], out.append))
|
||||
self.assertIn("exited with 3", out[-1])
|
||||
|
||||
def test_missing_binary(self):
|
||||
out = []
|
||||
self.assertFalse(run_command(["/nonexistent-binary-xyz"], out.append))
|
||||
self.assertTrue(out[0].startswith("Failed:"))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Loading…
Reference in New Issue
Block a user