fix: add process tracking and cancellation support to command execution

This commit is contained in:
Даниил Грабарь
2026-05-22 13:23:38 +10:00
parent 14337aa4e7
commit 4202421d5b
6 changed files with 224 additions and 73 deletions
+49 -15
View File
@@ -53,6 +53,7 @@ def _image_exists() -> bool:
def _build_docs_image(
write: Write,
on_progress: Optional[Callable[[float], None]] = None,
register_proc: Optional[Callable] = None,
) -> bool:
write("[cyan][*][/cyan] Building documentation image (runs once)...")
# DOCKER_BUILDKIT=0 gives us "Step X/Y" lines that we can parse for progress.
@@ -62,6 +63,8 @@ def _build_docs_image(
["docker", "build", "-t", _IMAGE_NAME, str(_DOC_DIR)],
stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, env=env,
)
if register_proc:
register_proc(proc)
for line in proc.stdout:
s = line.rstrip()
if s:
@@ -72,6 +75,8 @@ def _build_docs_image(
step, total = int(m.group(1)), int(m.group(2))
on_progress(step / total * 100)
proc.wait()
if proc.returncode in (-9, -15):
return False
if proc.returncode == 0:
write("[green][ok][/green] Documentation image ready")
return True
@@ -86,25 +91,34 @@ def _task_up(screen: LogScreen, port: str) -> None:
if _is_running():
screen.write(f"[green]Docs already running at:[/green] http://localhost:{port}")
screen.write(" Stop with: [bold]cobot doc-setup down[/bold]")
screen.finish(True)
if not screen.is_stopped():
screen.finish(True)
return
if not _DOC_DIR.exists():
screen.write(f"[red]Doc directory not found:[/red] {_DOC_DIR}")
screen.finish(False)
if not screen.is_stopped():
screen.finish(False)
return
if not _image_exists():
screen.set_progress(0, "Building documentation image...")
if not _build_docs_image(
ok = _build_docs_image(
screen.write,
on_progress=lambda p: screen.set_progress(p * 0.85, "Building documentation image..."),
):
register_proc=screen.set_proc,
)
if screen.is_stopped():
return
if not ok:
screen.finish(False)
return
else:
screen.write("[dim]Documentation image already built, skipping.[/dim]")
if screen.is_stopped():
return
screen.set_progress(88, "Starting MkDocs server...")
screen.write("\n[cyan][*][/cyan] Starting MkDocs server...")
result = _docker(
@@ -116,6 +130,8 @@ def _task_up(screen: LogScreen, port: str) -> None:
_IMAGE_NAME, "serve", "--dev-addr=0.0.0.0:8000",
capture=True,
)
if screen.is_stopped():
return
if result.returncode != 0:
screen.write(f"[red]Failed to start container.[/red]\n{result.stderr}")
screen.finish(False)
@@ -125,11 +141,13 @@ def _task_up(screen: LogScreen, port: str) -> None:
screen.write(f"\n[green]Docs running at:[/green] http://localhost:{port}")
screen.write(" Edit files in [bold]doc/lwc-doc/docs/[/bold] — reloads automatically.")
screen.write(" Stop with: [bold]cobot doc-setup down[/bold]")
screen.finish(True)
if not screen.is_stopped():
screen.finish(True)
except Exception as exc:
screen.write(f"[red]Error:[/red] {exc}")
screen.finish(False)
if not screen.is_stopped():
screen.write(f"[red]Error:[/red] {exc}")
screen.finish(False)
# Stop the running docs container.
@@ -138,17 +156,21 @@ def _task_down(screen: LogScreen) -> None:
try:
if not _is_running():
screen.write("[yellow]Docs container is not running.[/yellow]")
screen.finish(True)
if not screen.is_stopped():
screen.finish(True)
return
screen.set_progress(30, "Stopping container...")
screen.write("[cyan][*][/cyan] Stopping documentation server...")
_docker("stop", _CONTAINER_NAME)
if screen.is_stopped():
return
screen.set_progress(100, "Done")
screen.write("[green][ok][/green] Container stopped.")
screen.finish(True)
except Exception as exc:
screen.write(f"[red]Error:[/red] {exc}")
screen.finish(False)
if not screen.is_stopped():
screen.write(f"[red]Error:[/red] {exc}")
screen.finish(False)
# Stop the container, remove the old image, rebuild it, and start a new container.
@@ -159,19 +181,27 @@ def _task_rebuild(screen: LogScreen, port: str) -> None:
screen.set_progress(5, "Stopping container...")
screen.write("[cyan][*][/cyan] Stopping existing container...")
_docker("stop", _CONTAINER_NAME)
if screen.is_stopped():
return
screen.write("[green][ok][/green] Stopped.")
if _image_exists():
screen.set_progress(15, "Removing old image...")
screen.write("[cyan][*][/cyan] Removing old image...")
_docker("rmi", "-f", _IMAGE_NAME)
if screen.is_stopped():
return
screen.write("[green][ok][/green] Image removed.")
screen.set_progress(20, "Building documentation image...")
if not _build_docs_image(
ok = _build_docs_image(
screen.write,
on_progress=lambda p: screen.set_progress(20 + p * 0.68, "Building documentation image..."),
):
register_proc=screen.set_proc,
)
if screen.is_stopped():
return
if not ok:
screen.finish(False)
return
@@ -184,6 +214,8 @@ def _task_rebuild(screen: LogScreen, port: str) -> None:
_IMAGE_NAME, "serve", "--dev-addr=0.0.0.0:8000",
capture=True,
)
if screen.is_stopped():
return
if result.returncode != 0:
screen.write(f"[red]Failed to start container.[/red]\n{result.stderr}")
screen.finish(False)
@@ -192,11 +224,13 @@ def _task_rebuild(screen: LogScreen, port: str) -> None:
screen.set_progress(100, "Server running")
screen.write(f"\n[green]Docs running at:[/green] http://localhost:{port}")
screen.write(" Stop with: [bold]cobot doc-setup down[/bold]")
screen.finish(True)
if not screen.is_stopped():
screen.finish(True)
except Exception as exc:
screen.write(f"[red]Error:[/red] {exc}")
screen.finish(False)
if not screen.is_stopped():
screen.write(f"[red]Error:[/red] {exc}")
screen.finish(False)
# One app handles all three actions (up/down/rebuild) by branching in on_mount.