refactor: slim backend routers to ≤500 lines

- tool_instances.py: 2108 → 496 lines
- git_repositories.py: 1422 → 500 lines
- config_profiles.py: 474 → 300 lines (already committed)

Extract business logic into services:
- services/tool/instance_service.py
- services/git/operations.py
- services/config/crud_service.py

Quality gates: py_compile pass on all files
This commit is contained in:
Developer
2026-06-05 21:18:57 +00:00
parent 6efe524974
commit 9a17916dd2
5 changed files with 1395 additions and 2820 deletions
+44
View File
@@ -167,3 +167,47 @@ def init_working_repository(repo_path: str) -> None:
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"failed to set initial branch: {ref_result.stderr}",
)
def list_remote_branches(remote_url: str, ssh_key: SSHKey | None = None) -> tuple[list[str], str]:
"""List branches from a remote repository via ls-remote.
Returns:
Tuple of (branch_names, default_branch).
"""
ssh_result = prepare_ssh_env(ssh_key)
env, key_path = ssh_result if ssh_result else (None, None)
try:
result = subprocess.run(
["git", "ls-remote", "--heads", remote_url],
capture_output=True,
text=True,
timeout=30,
env={**os.environ, **env} if env else None,
)
if result.returncode != 0:
logger.warning("ls-remote returned %d: %s", result.returncode, result.stderr)
raise RuntimeError(f"ls-remote failed: {result.stderr}")
branches = []
default_branch = "main"
for line in result.stdout.strip().split("\n"):
if not line:
continue
parts = line.split("\t")
if len(parts) == 2:
ref = parts[1]
if ref.startswith("refs/heads/"):
branch_name = ref[len("refs/heads/"):]
branches.append(branch_name)
if branch_name in ("main", "master"):
default_branch = branch_name
return branches, default_branch
except subprocess.TimeoutExpired:
logger.warning("ls-remote timed out for %s", remote_url)
raise RuntimeError("ls-remote timed out")
except Exception as e:
logger.warning("ls-remote failed for %s: %s", remote_url, str(e))
raise RuntimeError(f"ls-remote failed: {e}")
finally:
if key_path and os.path.exists(key_path):
os.unlink(key_path)