Feat: build script UI/UX

This commit is contained in:
2026-03-17 12:49:01 +01:00
parent 3d9d2ad759
commit d72b36b8f1

View File

@@ -22,8 +22,7 @@ from concurrent.futures import ThreadPoolExecutor
from dataclasses import dataclass, field
from pathlib import Path
from rich.console import Console
from rich.layout import Layout
from rich.console import Console, Group
from rich.live import Live
from rich.panel import Panel
from rich.progress_bar import ProgressBar
@@ -606,19 +605,12 @@ def _build_display(
completed: dict[str, PlatformResult],
start_times: dict[str, float],
log_max_lines: int,
) -> Layout:
layout = Layout()
status_height = len(platforms) + 4
layout.split_column(
Layout(name="status", size=status_height),
Layout(name="logs"),
)
table = Table(padding=(0, 1), expand=True)
table.add_column("Platform", style="cyan", min_width=28, no_wrap=True)
table.add_column("Phase", min_width=20, no_wrap=True)
table.add_column("Progress", min_width=22, no_wrap=True)
table.add_column("Time", justify="right", min_width=6, no_wrap=True)
) -> Group:
table = Table(padding=(0, 1), expand=True, show_edge=False)
table.add_column("Platform", style="cyan", no_wrap=True)
table.add_column("Phase", no_wrap=True)
table.add_column("Progress", no_wrap=True)
table.add_column("Time", justify="right", no_wrap=True)
with _progress_lock:
progress_snapshot = dict(_build_progress)
@@ -634,7 +626,7 @@ def _build_display(
phase = Text(f"OK ({n} artifacts)", style="green")
bar = ProgressBar(total=total, completed=total, width=20, complete_style="green")
else:
phase = Text(f"FAIL", style="red")
phase = Text("FAIL", style="red")
bar = ProgressBar(total=total, completed=total, width=20, complete_style="red")
elapsed = f"{r.elapsed:.0f}s"
elif alias in progress_snapshot:
@@ -649,23 +641,18 @@ def _build_display(
table.add_row(p.label, phase, bar, elapsed)
layout["status"].update(Panel(table, title="[bold blue]Build Progress[/]", border_style="blue"))
with _progress_lock:
recent = _build_logs[-log_max_lines:]
if recent:
lines: list[str] = []
for alias, line in recent:
short = alias.split("-")[0][:3]
lines.append(f"[dim]{short}[/] {line.rstrip()}")
log_text = "\n".join(lines)
lines.append(f"[dim]{alias}[/] {line.rstrip()}")
log_text = Text("\n") + Text.from_markup("\n".join(lines))
else:
log_text = "[dim]waiting for output...[/]"
log_text = Text("\nwaiting for output...", style="dim")
layout["logs"].update(Panel(log_text, title="[bold]Build Output[/]", border_style="dim"))
return layout
return Group(table, log_text)
def run_builds(
@@ -685,30 +672,23 @@ def run_builds(
term_height = console.size.height
log_max_lines = max(term_height - len(platforms) - 10, 5)
def make_display() -> Layout:
def make_display() -> Group:
return _build_display(platforms, config, completed, start_times, log_max_lines)
with Live(make_display(), console=console, refresh_per_second=4) as live:
if len(platforms) == 1:
p = platforms[0]
r = build_platform(root, p, config)
completed[p.alias] = r
results.append(r)
live.update(make_display())
else:
with ThreadPoolExecutor(max_workers=len(platforms)) as pool:
futures = {pool.submit(build_platform, root, p, config): p for p in platforms}
pending = set(futures.keys())
while pending:
done = {f for f in pending if f.done()}
for f in done:
r = f.result()
completed[r.platform.alias] = r
results.append(r)
pending.discard(f)
live.update(make_display())
if pending:
time.sleep(0.25)
with ThreadPoolExecutor(max_workers=len(platforms)) as pool:
futures = {pool.submit(build_platform, root, p, config): p for p in platforms}
pending = set(futures.keys())
while pending:
done = {f for f in pending if f.done()}
for f in done:
r = f.result()
completed[r.platform.alias] = r
results.append(r)
pending.discard(f)
live.update(make_display())
if pending:
time.sleep(0.25)
for r in results:
_print_platform_log(r, verbose)
@@ -716,13 +696,21 @@ def run_builds(
return results
_FAILURE_LOG_TAIL = 50
def _print_platform_log(r: PlatformResult, verbose: bool = False) -> None:
if r.success and not verbose:
return
style = "green" if r.success else "red"
status = "OK" if r.success else "FAIL"
lines = r.log_lines or []
if not r.success and not verbose and len(lines) > _FAILURE_LOG_TAIL:
lines = [f" ... ({len(r.log_lines) - _FAILURE_LOG_TAIL} lines omitted, use --verbose for full output)"] + lines[-_FAILURE_LOG_TAIL:]
console.print(Panel(
"\n".join(r.log_lines) if r.log_lines else "[dim]no output[/]",
"\n".join(lines) if lines else "[dim]no output[/]",
title=f"{r.platform.label} [{status}] {r.elapsed:.1f}s",
border_style=style,
))
@@ -748,14 +736,14 @@ def _build_presets(platforms: list[Platform]) -> list[tuple[str, list[Platform],
native = _native_platforms(platforms)
if native:
label = ", ".join(p.label for p in native)
presets.append((f"This machine ({label})", native, BuildConfig()))
presets.append((f"This machine ({label})", native, BuildConfig()))
macos = _platforms_by_os(platforms, "macos")
if len(macos) > 1:
presets.append(("macOS all (arm64 + x86_64)", macos, BuildConfig()))
presets.append(("macOS all (arm64 + x86_64)", macos, BuildConfig()))
presets.append(("Full release (all platforms, all targets)", list(platforms), BuildConfig()))
presets.append(("CLI only (all platforms, no desktop/plugins)", list(platforms), BuildConfig(desktop=False, plugins=False)))
presets.append(("Full release (all platforms, all targets)", list(platforms), BuildConfig()))
presets.append(("CLI only (all platforms, no desktop/plugins)", list(platforms), BuildConfig(desktop=False, plugins=False)))
return presets