File size: 4,533 Bytes
b6db694 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | """
网络工具
处理Git克隆、文件下载等
"""
import os
import tempfile
import logging
from pathlib import Path
from typing import Optional, Tuple, Callable
from urllib.parse import urlparse
import subprocess
logger = logging.getLogger(__name__)
class NetworkUtils:
"""网络工具类"""
@staticmethod
def clone_git_repository(repo_url: str, target_dir: Path, branch: str = "main") -> Tuple[bool, str]:
"""克隆Git仓库"""
try:
# 验证Git URL
if not NetworkUtils._is_valid_git_url(repo_url):
return False, "无效的Git仓库URL"
# 确保目标目录不存在或为空
if target_dir.exists():
# 如果目录存在且非空,先删除
if any(target_dir.iterdir()):
import shutil
shutil.rmtree(target_dir)
# 执行Git克隆命令
cmd = ["git", "clone", "--branch", branch, "--depth", "1", repo_url, str(target_dir)]
result = subprocess.run(
cmd,
capture_output=True,
text=True,
timeout=300 # 5分钟超时
)
if result.returncode == 0:
logger.info(f"成功克隆Git仓库: {repo_url} -> {target_dir}")
return True, "克隆成功"
else:
error_msg = result.stderr.strip()
logger.error(f"克隆Git仓库失败: {error_msg}")
return False, f"克隆失败: {error_msg}"
except subprocess.TimeoutExpired:
logger.error("克隆Git仓库超时")
return False, "克隆超时,请检查网络连接"
except Exception as e:
logger.error(f"克隆Git仓库异常: {e}")
return False, f"克隆异常: {str(e)}"
@staticmethod
def _is_valid_git_url(url: str) -> bool:
"""验证Git URL格式"""
try:
parsed = urlparse(url)
if parsed.scheme in ('http', 'https', 'git'):
return True
# 支持SSH格式的URL
if url.startswith('git@') and '.git' in url:
return True
return False
except Exception:
return False
@staticmethod
def download_file(url: str, target_path: Path) -> Tuple[bool, str]:
"""下载文件"""
try:
import requests
response = requests.get(url, stream=True, timeout=30)
response.raise_for_status()
with open(target_path, 'wb') as f:
for chunk in response.iter_content(chunk_size=8192):
f.write(chunk)
logger.info(f"成功下载文件: {url} -> {target_path}")
return True, "下载成功"
except requests.RequestException as e:
logger.error(f"下载文件失败: {e}")
return False, f"下载失败: {str(e)}"
except Exception as e:
logger.error(f"下载文件异常: {e}")
return False, f"下载异常: {str(e)}"
@staticmethod
def get_git_info(repo_url: str) -> Tuple[bool, dict]:
"""获取Git仓库信息"""
try:
with tempfile.TemporaryDirectory() as temp_dir:
temp_path = Path(temp_dir)
# 克隆仓库(浅克隆以加快速度)
success, message = NetworkUtils.clone_git_repository(repo_url, temp_path)
if not success:
return False, {"error": message}
# 检查插件结构
from app.utils.file_utils import file_utils
valid, msg, metadata = file_utils.validate_plugin_structure(temp_path)
if valid:
return True, {
"name": metadata.get("name"),
"version": metadata.get("version"),
"description": metadata.get("description"),
"author": metadata.get("author")
}
else:
return False, {"error": msg}
except Exception as e:
logger.error(f"获取Git信息失败: {e}")
return False, {"error": str(e)}
# 创建全局实例
network_utils = NetworkUtils() |