Initial commit
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
"""Interactive component selection (matches deployment/scripts/lib.sh)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
import deploy.ui as ui
|
||||
|
||||
COMPONENT_OPTIONS: list[tuple[str, str, bool]] = [
|
||||
("backend", "Backend (API, DB, LiveKit)", True),
|
||||
("frontend", "Web frontend", True),
|
||||
("caddy", "Caddy reverse proxy (TLS)", False),
|
||||
("updater", "Auto-update service", False),
|
||||
]
|
||||
|
||||
ALLOWED = {name for name, _, _ in COMPONENT_OPTIONS}
|
||||
|
||||
|
||||
def parse_components_csv(raw: str) -> list[str]:
|
||||
out: list[str] = []
|
||||
for part in raw.split(","):
|
||||
c = part.strip().lower()
|
||||
if not c:
|
||||
continue
|
||||
if c not in ALLOWED:
|
||||
sys.stderr.write(
|
||||
f"Unknown component: {c} (allowed: {','.join(sorted(ALLOWED))})\n"
|
||||
)
|
||||
raise SystemExit(1)
|
||||
if c not in out:
|
||||
out.append(c)
|
||||
if not out:
|
||||
sys.stderr.write("Select at least one component.\n")
|
||||
raise SystemExit(1)
|
||||
return out
|
||||
|
||||
|
||||
def _select_whiptail() -> list[str]:
|
||||
args = [
|
||||
"whiptail",
|
||||
"--title",
|
||||
"FromChat components",
|
||||
"--checklist",
|
||||
"Select components (Space toggles, Enter confirms)",
|
||||
"18",
|
||||
"72",
|
||||
str(len(COMPONENT_OPTIONS)),
|
||||
]
|
||||
for name, label, default_on in COMPONENT_OPTIONS:
|
||||
args.extend([name, label, "ON" if default_on else "OFF"])
|
||||
result = subprocess.run(args, capture_output=True, text=True)
|
||||
if result.returncode != 0:
|
||||
ui.error("Component selection cancelled.")
|
||||
raise SystemExit(1)
|
||||
selected: list[str] = []
|
||||
for token in result.stdout.split():
|
||||
name = token.strip('"')
|
||||
if name in ALLOWED:
|
||||
selected.append(name)
|
||||
if not selected:
|
||||
ui.error("Select at least one component.")
|
||||
raise SystemExit(1)
|
||||
return selected
|
||||
|
||||
|
||||
def _select_text() -> list[str]:
|
||||
ui.warning("whiptail not found; using text menu.")
|
||||
selected: list[str] = []
|
||||
for name, label, default_on in COMPONENT_OPTIONS:
|
||||
hint = "Y/n" if default_on else "y/N"
|
||||
answer = input(f" Include {label}? [{hint}]: ").strip()
|
||||
if not answer:
|
||||
if default_on:
|
||||
selected.append(name)
|
||||
continue
|
||||
if answer.lower().startswith("y"):
|
||||
selected.append(name)
|
||||
if not selected:
|
||||
ui.error("Select at least one component.")
|
||||
raise SystemExit(1)
|
||||
return selected
|
||||
|
||||
|
||||
def select_components_interactive() -> list[str]:
|
||||
ui.step("Select components")
|
||||
if shutil.which("whiptail"):
|
||||
return _select_whiptail()
|
||||
return _select_text()
|
||||
|
||||
|
||||
def compose_components(selected: list[str]) -> list[str]:
|
||||
"""Components that go into the merged stack compose (excludes updater)."""
|
||||
return [c for c in selected if c != "updater"]
|
||||
Reference in New Issue
Block a user