refactor: organize API routers and services into subpackages

Service organization (19 files moved into 6 subpackages):
- services/instance/ — event_bus, health_monitor, lifecycle_hooks
- services/config/ — config_profile_resolver
- services/git/ — clone, git_operations, git_service
- services/build/ — docker_build, manifest_compiler
- services/terminal/ — terminal_manager, terminal_session
- services/shared/ — correlation, file_service, notification_service,
  permission_fixer, readiness_probe, ssh_keys, tunnel, workspace_manager

API router organization (16 files moved into 6 subpackages):
- api/tool/ — tool_instances, tool_types, tool_definitions,
  tool_types_validation, sessions (extracted from tool_instances)
- api/config/ — config_profiles, user_config
- api/workspace/ — workspaces, workspace_files, workspace_git,
  workspace_instances
- api/user/ — users, auth, ssh_keys
- api/project/ — projects, git_repositories
- api/system/ — health, events, notifications, dashboard, terminal,
  instance_proxy

Updated main.py imports and all __init__.py re-exports.
Sessions router extracted from tool_instances.py into api/tool/sessions.py.

Quality gates: py_compile passed, ruff passed.
This commit is contained in:
2026-06-04 12:24:14 +02:00
parent 8816ee02ce
commit 37ccaa4fdc
57 changed files with 315 additions and 163 deletions
+97
View File
@@ -0,0 +1,97 @@
"""Clone service for repository cloning and dirty state checking."""
import logging
import os
import subprocess
from pathlib import Path
logger = logging.getLogger(__name__)
def clone_repository(
remote_url: str,
ssh_key_path: str | None,
instance_dir: str,
branch: str = "main",
) -> str:
"""Clone a git repository into the instance directory.
Args:
remote_url: Git remote URL (SSH or HTTPS)
ssh_key_path: Path to SSH private key for authentication (optional)
instance_dir: Path to instance directory
branch: Branch to clone (default: main)
Returns:
Path to the cloned repository
"""
clone_path = Path(instance_dir) / "repo-clone"
clone_path.mkdir(parents=True, exist_ok=True)
env = os.environ.copy()
if ssh_key_path:
# Use SSH key for cloning
env["GIT_SSH_COMMAND"] = f"ssh -i {ssh_key_path} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
cmd = [
"git",
"clone",
"--branch", branch,
"--single-branch",
remote_url,
str(clone_path),
]
logger.debug("Cloning repository %s (branch: %s) into %s", remote_url, branch, clone_path)
result = subprocess.run(
cmd,
capture_output=True,
text=True,
env=env,
timeout=300,
)
if result.returncode != 0:
logger.error("Git clone failed: %s", result.stderr)
raise RuntimeError(f"Failed to clone repository: {result.stderr}")
logger.debug("Successfully cloned repository into %s", clone_path)
return str(clone_path)
def check_dirty_state(clone_path: str) -> tuple[bool, list[str]]:
"""Check for uncommitted changes in a cloned repository.
Args:
clone_path: Path to the cloned repository
Returns:
Tuple of (is_dirty, list_of_changed_files)
"""
result = subprocess.run(
["git", "-C", clone_path, "status", "--short"],
capture_output=True,
text=True,
)
if result.returncode != 0:
logger.warning("Failed to check git status: %s", result.stderr)
return False, []
changed_files = [line.strip() for line in result.stdout.split("\n") if line.strip()]
is_dirty = len(changed_files) > 0
return is_dirty, changed_files
def remove_clone_directory(instance_dir: str) -> None:
"""Remove the cloned repository from the instance directory.
Args:
instance_dir: Path to instance directory
"""
clone_path = Path(instance_dir) / "repo-clone"
if clone_path.exists():
import shutil
shutil.rmtree(clone_path)
logger.debug("Removed clone directory: %s", clone_path)