feat(n7web): enhance control workflows and status feedback

This commit is contained in:
Miayui
2026-09-16 12:52:07 +08:00
parent 84a3cd73bb
commit 0e4328f548
45 changed files with 7317 additions and 1001 deletions
+50
View File
@@ -7,6 +7,7 @@ import argparse
import json
import os
import shlex
import shutil
import subprocess
import sys
import time
@@ -21,6 +22,7 @@ PYTHON_SOURCE_PATHS = (
REPOSITORY_ROOT / "src",
REPOSITORY_ROOT / "domains" / "seat" / "src",
)
WEB_CONSOLE_ROOT = REPOSITORY_ROOT / "apps" / "web-console"
@dataclass(frozen=True)
@@ -90,6 +92,11 @@ def _parser() -> argparse.ArgumentParser:
action="store_true",
help="print the resolved commands without starting processes",
)
parser.add_argument(
"--skip-web-build",
action="store_true",
help="reuse the existing Vite dist output instead of rebuilding it",
)
return parser
@@ -176,6 +183,37 @@ def _display_command(command: Sequence[str]) -> str:
return shlex.join(command)
def _resolve_npm() -> Path:
executable = shutil.which("npm.cmd" if os.name == "nt" else "npm")
if not executable:
raise ValueError(
"npm is required to build the Vue 3 + Vite web console; "
"install Node.js or pass --skip-web-build to reuse an existing dist"
)
return Path(executable).resolve()
def _web_build_commands(npm: Path) -> list[list[str]]:
commands: list[list[str]] = []
vite_binary = WEB_CONSOLE_ROOT / "node_modules" / ".bin" / (
"vite.cmd" if os.name == "nt" else "vite"
)
if not vite_binary.is_file():
commands.append([str(npm), "ci"])
commands.append([str(npm), "run", "build"])
return commands
def _build_web_console(npm: Path) -> None:
for command in _web_build_commands(npm):
print(f"Web build: {_display_command(command)}", flush=True)
completed = subprocess.run(command, cwd=WEB_CONSOLE_ROOT, check=False)
if completed.returncode != 0:
raise RuntimeError(
f"Vue 3 + Vite web build failed with exit code {completed.returncode}"
)
def _catalog_is_ready(path: Path) -> bool:
try:
document = json.loads(path.read_text(encoding="utf-8"))
@@ -257,6 +295,7 @@ def run(argv: Sequence[str] | None = None) -> int:
try:
python = _resolve_python(args.python)
catalog = _resolve_path(args.catalog)
npm = None if args.skip_web_build else _resolve_npm()
if args.catalog_ready_timeout <= 0:
raise ValueError("catalog-ready-timeout must be greater than zero")
except ValueError as exc:
@@ -269,9 +308,20 @@ def run(argv: Sequence[str] | None = None) -> int:
print(f"Catalog: {catalog}", flush=True)
print(f"Main: {_display_command(main_command)}", flush=True)
print(f"Cards: {_display_command(scene_command)}", flush=True)
if npm is not None:
for command in _web_build_commands(npm):
print(f"Web build: {_display_command(command)}", flush=True)
if args.dry_run:
return 0
if npm is not None:
try:
_build_web_console(npm)
except RuntimeError as exc:
print(f"startup failed: {exc}", file=sys.stderr, flush=True)
return 1
children: list[ChildProcess] = []
interrupted = False
try: