Files
mirror/app/services/github.py
T
2026-06-03 17:33:19 +03:30

108 lines
3.3 KiB
Python

from __future__ import annotations
import configparser
import logging
from io import StringIO
from pathlib import Path
from app.models import Mirror
from app.services.host import host_path, run_on_host
logger = logging.getLogger(__name__)
MARKER = "mirror-manager"
INSTEAD_SECTION = f'url "https://github.com/" managed by {MARKER}'
def _gitconfig_paths() -> list[Path]:
paths = []
for rel in ("root/.gitconfig", "home/.gitconfig"):
p = host_path(rel)
if p not in paths:
paths.append(p)
local = Path.home() / ".gitconfig"
if local not in paths:
paths.append(local)
system = host_path("etc/gitconfig")
if system not in paths:
paths.append(system)
return paths
def read_current_github() -> dict:
configs = {}
for path in _gitconfig_paths():
if path.exists():
configs[str(path)] = path.read_text(encoding="utf-8")
return configs
def _parse_gitconfig(content: str) -> configparser.ConfigParser:
parser = configparser.ConfigParser()
parser.read_string(content if content.strip() else "[core]\n")
return parser
def _serialize_gitconfig(parser: configparser.ConfigParser) -> str:
buf = StringIO()
parser.write(buf)
return buf.getvalue()
def _remove_mirror_sections(parser: configparser.ConfigParser) -> None:
to_remove = []
for section in parser.sections():
if not section.startswith('url "'):
continue
instead_of = parser.get(section, "insteadOf", fallback="")
if instead_of in ("https://github.com/", "git@github.com:"):
to_remove.append(section)
for section in to_remove:
parser.remove_section(section)
def apply_github(mirror: Mirror) -> dict:
meta = mirror.get_meta()
instead_prefix = meta.get("instead_prefix") or mirror.url
if not instead_prefix:
return {"success": False, "error": "prefix میرور GitHub تعریف نشده"}
instead_prefix = instead_prefix.rstrip("/") + "/"
section_name = f'url "{instead_prefix}"'
applied_paths = []
for path in _gitconfig_paths():
content = path.read_text(encoding="utf-8") if path.exists() else ""
parser = _parse_gitconfig(content)
_remove_mirror_sections(parser)
if not parser.has_section(section_name):
parser.add_section(section_name)
parser.set(section_name, "insteadOf", "https://github.com/")
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(_serialize_gitconfig(parser), encoding="utf-8")
applied_paths.append(str(path))
test = run_on_host(
["git", "ls-remote", "https://github.com/octocat/Hello-World.git", "HEAD"],
timeout=60,
)
return {
"success": test.returncode == 0,
"message": f"GitHub mirror اعمال شد: {mirror.name}",
"paths": applied_paths,
"test_output": (test.stdout or test.stderr)[:300] if test.returncode != 0 else "git ls-remote موفق",
"error": test.stderr[:300] if test.returncode != 0 else None,
}
def remove_github_config() -> None:
for path in _gitconfig_paths():
if not path.exists():
continue
parser = _parse_gitconfig(path.read_text(encoding="utf-8"))
_remove_mirror_sections(parser)
path.write_text(_serialize_gitconfig(parser), encoding="utf-8")