diff --git a/src/repo_registry/repo_ingestion/git.py b/src/repo_registry/repo_ingestion/git.py
index c65f243..b327515 100644
--- a/src/repo_registry/repo_ingestion/git.py
+++ b/src/repo_registry/repo_ingestion/git.py
@@ -63,14 +63,20 @@ class GitIngestionService:
def _run_git(self, args: list[str], *, cwd: Path | None) -> None:
if shutil.which("git") is None:
raise RuntimeError("git executable was not found")
- result = subprocess.run(
- ["git", *args],
- cwd=cwd,
- check=False,
- capture_output=True,
- text=True,
- timeout=120,
- )
+ command = ["git", *args]
+ try:
+ result = subprocess.run(
+ command,
+ cwd=cwd,
+ check=False,
+ capture_output=True,
+ text=True,
+ timeout=120,
+ )
+ except subprocess.TimeoutExpired as exc:
+ raise RuntimeError(
+ f"git {' '.join(args)} timed out after {exc.timeout} seconds"
+ ) from exc
if result.returncode != 0:
message = result.stderr.strip() or result.stdout.strip()
raise RuntimeError(f"git {' '.join(args)} failed: {message}")
diff --git a/src/repo_registry/web_ui/views.py b/src/repo_registry/web_ui/views.py
index 328d992..b8bbc7f 100644
--- a/src/repo_registry/web_ui/views.py
+++ b/src/repo_registry/web_ui/views.py
@@ -34,6 +34,8 @@ def page(title: str, body: str) -> HTMLResponse:
--accent: #0f766e;
--accent-dark: #115e59;
--warn: #9a3412;
+ --danger: #b42318;
+ --danger-bg: #fff4f2;
}}
* {{ box-sizing: border-box; }}
body {{
@@ -64,6 +66,17 @@ def page(title: str, body: str) -> HTMLResponse:
border-radius: 8px;
padding: 16px;
}}
+ .notice {{
+ border: 1px solid var(--line);
+ border-radius: 8px;
+ padding: 10px 12px;
+ margin-bottom: 12px;
+ }}
+ .notice.error {{
+ border-color: #f3b8ae;
+ background: var(--danger-bg);
+ color: var(--danger);
+ }}
.stack {{ display: grid; gap: 12px; }}
.muted {{ color: var(--muted); }}
.pill {{
@@ -111,6 +124,9 @@ def page(title: str, body: str) -> HTMLResponse:
.tree li {{ margin: 6px 0; }}
.source {{ color: var(--muted); font-family: ui-monospace, SFMono-Regular, Consolas, monospace; font-size: 12px; }}
.actions {{ display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }}
+ [data-pending] {{ display: none; color: var(--muted); }}
+ form.is-submitting [data-pending] {{ display: inline; }}
+ form.is-submitting button[type="submit"] {{ opacity: .7; cursor: wait; }}
@media (max-width: 780px) {{
header {{ padding: 12px 16px; }}
main {{ padding: 16px; }}
@@ -132,14 +148,28 @@ def page(title: str, body: str) -> HTMLResponse:
{body}
+