From d72b36b8f18e3ebe12ed06facedece32efa40c9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Forment?= Date: Tue, 17 Mar 2026 12:49:01 +0100 Subject: [PATCH] Feat: build script UI/UX --- scripts/build.py | 90 +++++++++++++++++++++--------------------------- 1 file changed, 39 insertions(+), 51 deletions(-) diff --git a/scripts/build.py b/scripts/build.py index 4b92866..63c4001 100755 --- a/scripts/build.py +++ b/scripts/build.py @@ -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