ci: add GitHub Actions workflows with lint, typecheck and tests

This commit is contained in:
AFredefon
2026-03-11 01:13:35 +01:00
parent f2dca0a7e7
commit f8002254e5
17 changed files with 263 additions and 34 deletions

View File

@@ -9,12 +9,14 @@ hub management capabilities.
from __future__ import annotations
from collections import defaultdict
from pathlib import Path
from typing import TYPE_CHECKING, Any
from rich.text import Text
from textual.app import App, ComposeResult
from textual.binding import Binding
from textual.containers import Horizontal, Vertical, VerticalScroll
from textual.widgets import Button, DataTable, Footer, Header, Label
from textual.widgets import Button, DataTable, Footer, Header
from fuzzforge_cli.tui.helpers import (
check_agent_status,
@@ -24,11 +26,14 @@ from fuzzforge_cli.tui.helpers import (
load_hub_config,
)
if TYPE_CHECKING:
from fuzzforge_cli.commands.mcp import AIAgent
# Agent config entries stored alongside their linked status for row mapping
_AgentRow = tuple[str, "AIAgent", "Path", str, bool] # noqa: F821
_AgentRow = tuple[str, "AIAgent", Path, str, bool]
class FuzzForgeApp(App):
class FuzzForgeApp(App[None]):
"""FuzzForge AI terminal user interface."""
TITLE = "FuzzForge AI"
@@ -236,7 +241,7 @@ class FuzzForgeApp(App):
return
# Group servers by source hub
groups: dict[str, list[dict]] = defaultdict(list)
groups: dict[str, list[dict[str, Any]]] = defaultdict(list)
for server in servers:
source = server.get("source_hub", "manual")
groups[source].append(server)
@@ -245,7 +250,7 @@ class FuzzForgeApp(App):
ready_count = 0
total = len(hub_servers)
statuses: list[tuple[dict, bool, str]] = []
statuses: list[tuple[dict[str, Any], bool, str]] = []
for server in hub_servers:
enabled = server.get("enabled", True)
if not enabled:

View File

@@ -108,7 +108,7 @@ def check_hub_image(image: str) -> tuple[bool, str]:
try:
result = subprocess.run(
["docker", "image", "inspect", image],
capture_output=True,
check=False, capture_output=True,
text=True,
timeout=5,
)
@@ -132,7 +132,8 @@ def load_hub_config(fuzzforge_root: Path) -> dict[str, Any]:
if not config_path.exists():
return {}
try:
return json.loads(config_path.read_text())
data: dict[str, Any] = json.loads(config_path.read_text())
return data
except json.JSONDecodeError:
return {}
@@ -264,7 +265,8 @@ def load_hubs_registry() -> dict[str, Any]:
if not path.exists():
return {"hubs": []}
try:
return json.loads(path.read_text())
data: dict[str, Any] = json.loads(path.read_text())
return data
except (json.JSONDecodeError, OSError):
return {"hubs": []}
@@ -422,8 +424,7 @@ def clone_hub(
"""
if name is None:
name = git_url.rstrip("/").split("/")[-1]
if name.endswith(".git"):
name = name[:-4]
name = name.removesuffix(".git")
if dest is None:
dest = get_default_hubs_dir() / name
@@ -433,7 +434,7 @@ def clone_hub(
try:
result = subprocess.run(
["git", "-C", str(dest), "pull"],
capture_output=True,
check=False, capture_output=True,
text=True,
timeout=120,
)
@@ -451,7 +452,7 @@ def clone_hub(
try:
result = subprocess.run(
["git", "clone", git_url, str(dest)],
capture_output=True,
check=False, capture_output=True,
text=True,
timeout=300,
)

View File

@@ -81,6 +81,7 @@ class HubManagerScreen(ModalScreen[str | None]):
is_default = hub.get("is_default", False)
hub_path = Path(path)
count: str | Text
if hub_path.is_dir():
servers = scan_hub_for_servers(hub_path)
count = str(len(servers))
@@ -88,10 +89,11 @@ class HubManagerScreen(ModalScreen[str | None]):
count = Text("dir missing", style="yellow")
source = git_url or "local"
name_cell: str | Text
if is_default:
name_cell = Text(f"{name}", style="bold")
else:
name_cell = Text(name)
name_cell = name
table.add_row(name_cell, path, count, source)