增强阿里云和GitHub的文件管理功能,新增Git不可用异常处理,优化稀疏检出逻辑,提升代码可读性和稳定性。

This commit is contained in:
HibiKier 2025-08-28 11:05:19 +08:00
parent f3ff5a3404
commit b5417d1141
5 changed files with 83 additions and 44 deletions

View File

@ -276,6 +276,7 @@ class StoreManager:
else:
files = [RepoFileInfo(path=f"{replace_module_path}.py", is_dir=False)]
local_path = BASE_PATH / "plugins" if is_external else BASE_PATH
target_dir = BASE_PATH / "plugins" / plugin_name
files = [file for file in files if not file.is_dir]
download_files = [(file.path, local_path / file.path) for file in files]
await RepoFileManager.download_files(
@ -283,7 +284,7 @@ class StoreManager:
download_files,
repo_type=repo_type,
sparse_path=replace_module_path,
target_dir=local_path / plugin_name,
target_dir=target_dir,
)
requirement_paths = [

View File

@ -11,6 +11,7 @@ from aiocache import cached
from zhenxun.services.log import logger
from zhenxun.utils.github_utils.models import AliyunFileInfo
from zhenxun.utils.repo_utils.utils import prepare_aliyun_url
from .base_manager import BaseRepoManager
from .config import LOG_COMMAND, RepoConfig
@ -445,32 +446,6 @@ class AliyunCodeupManager(BaseRepoManager):
返回:
RepoUpdateResult: 更新结果
"""
# 定义预处理函数构建阿里云CodeUp的URL
def prepare_aliyun_url(repo_url: str) -> str:
import base64
repo_name = repo_url.split("/tree/")[0].split("/")[-1].replace(".git", "")
# 构建仓库URL
# 阿里云CodeUp的仓库URL格式通常为
# https://codeup.aliyun.com/{organization_id}/{organization_name}/{repo_name}.git
url = f"https://codeup.aliyun.com/{self.config.aliyun_codeup.organization_id}/{self.config.aliyun_codeup.organization_name}/{repo_name}.git"
# 添加访问令牌 - 使用base64解码后的令牌
if self.config.aliyun_codeup.rdc_access_token_encrypted:
try:
# 解码RDC访问令牌
token = base64.b64decode(
self.config.aliyun_codeup.rdc_access_token_encrypted.encode()
).decode()
# 阿里云CodeUp使用oauth2:token的格式进行身份验证
url = url.replace("https://", f"https://oauth2:{token}@")
logger.debug(f"使用RDC令牌构建阿里云URL: {url.split('@')[0]}@***")
except Exception as e:
logger.error(f"解码RDC令牌失败: {e}")
return url
# 调用基类的update_via_git方法
return await super().update_via_git(
repo_url=repo_url,

View File

@ -66,3 +66,10 @@ class ConfigError(RepoManagerError):
def __init__(self, message: str):
super().__init__(f"配置错误: {message}")
class GitUnavailableError(RepoManagerError):
"""Git不可用异常"""
def __init__(self, message: str = "Git命令不可用"):
super().__init__(f"Git不可用: {message}")

View File

@ -2,6 +2,7 @@
仓库文件管理器用于从GitHub和阿里云CodeUp获取指定文件内容
"""
import contextlib
from pathlib import Path
from typing import cast, overload
@ -15,9 +16,14 @@ from zhenxun.utils.http_utils import AsyncHttpx
from zhenxun.utils.utils import is_binary_file
from .config import LOG_COMMAND, RepoConfig
from .exceptions import FileNotFoundError, NetworkError, RepoManagerError
from .exceptions import (
FileNotFoundError,
GitUnavailableError,
NetworkError,
RepoManagerError,
)
from .models import FileDownloadResult, RepoFileInfo, RepoType
from .utils import sparse_checkout_clone
from .utils import prepare_aliyun_url, sparse_checkout_clone
class RepoFileManager:
@ -264,15 +270,15 @@ class RepoFileManager:
if repo_type is None:
# 尝试GitHub失败则尝试阿里云
try:
return await self._list_github_directory_files(
repo_url, directory_path, branch, recursive
return await self._list_aliyun_directory_files(
repo_name, directory_path, branch, recursive
)
except Exception as e:
logger.warning(
"获取GitHub目录文件失败尝试阿里云", LOG_COMMAND, e=e
"获取阿里云目录文件失败尝试GitHub", LOG_COMMAND, e=e
)
return await self._list_aliyun_directory_files(
repo_name, directory_path, branch, recursive
return await self._list_github_directory_files(
repo_url, directory_path, branch, recursive
)
if repo_type == RepoType.GITHUB:
return await self._list_github_directory_files(
@ -512,7 +518,7 @@ class RepoFileManager:
)
if (
any(is_binary_file(file_name) for file_name in file_path_mapping)
and repo_type == RepoType.ALIYUN
and repo_type != RepoType.GITHUB
and sparse_path
and target_dir
):
@ -597,7 +603,7 @@ class RepoFileManager:
) -> FileDownloadResult:
try:
await sparse_checkout_clone(
repo_url=repo_url,
repo_url=prepare_aliyun_url(repo_url),
branch=branch,
sparse_path=sparse_path,
target_dir=target_dir,
@ -606,14 +612,17 @@ class RepoFileManager:
if target_dir.exists():
for f in target_dir.rglob("*"):
if f.is_file():
try:
with contextlib.suppress(Exception):
total_size += f.stat().st_size
except Exception:
pass
result.success = True
result.file_size = total_size
logger.info(f"sparse-checkout 克隆成功: {target_dir}")
return result
except GitUnavailableError as e:
logger.error(f"Git不可用: {e}")
result.success = False
result.error_message = "Git不可用请尝试添加参数 -s git"
return result
except Exception as e:
logger.error(f"sparse-checkout 克隆失败: {e}")
result.success = False

View File

@ -3,12 +3,15 @@
"""
import asyncio
import base64
from pathlib import Path
import re
import shutil
from zhenxun.services.log import logger
from .config import LOG_COMMAND
from .config import LOG_COMMAND, RepoConfig
from .exceptions import GitUnavailableError
async def check_git() -> bool:
@ -152,7 +155,7 @@ async def sparse_checkout_clone(
target_dir.mkdir(parents=True, exist_ok=True)
if not await check_git():
raise RuntimeError("未检测到可用的 git 命令")
raise GitUnavailableError()
git_dir = target_dir / ".git"
if not git_dir.exists():
@ -172,15 +175,18 @@ async def sparse_checkout_clone(
# 兜底尝试添加
await run_git_command(f"remote add origin {repo_url}", target_dir)
# 启用稀疏检出(重复设置以确保幂等
# 启用稀疏检出(使用 --no-cone 模式以获得更精确的控制
await run_git_command("config core.sparseCheckout true", target_dir)
await run_git_command("sparse-checkout init --cone", target_dir)
await run_git_command("sparse-checkout init --no-cone", target_dir)
# 设置需要检出的路径(每次都覆盖配置)
if not sparse_path:
raise RuntimeError("sparse-checkout 路径不能为空")
# 使用 --no-cone 模式,直接指定要检出的具体路径
# 例如sparse_path="plugins/mahiro" -> 只检出 plugins/mahiro/ 下的内容
success, out, err = await run_git_command(
f"sparse-checkout set {sparse_path}", target_dir
f"sparse-checkout set {sparse_path}/", target_dir
)
if not success:
raise RuntimeError(f"配置稀疏路径失败: {err or out}")
@ -205,3 +211,44 @@ async def sparse_checkout_clone(
# 强制对齐工作区
await run_git_command(f"reset --hard origin/{branch}", target_dir)
await run_git_command("clean -xdf", target_dir)
dir_path = target_dir / Path(sparse_path)
for f in dir_path.iterdir():
shutil.move(f, target_dir / f.name)
dir_name = sparse_path.split("/")[0]
rm_path = target_dir / dir_name
if rm_path.exists():
shutil.rmtree(rm_path)
def prepare_aliyun_url(repo_url: str) -> str:
"""解析阿里云CodeUp的仓库URL
参数:
repo_url: 仓库URL
返回:
str: 解析后的仓库URL
"""
config = RepoConfig.get_instance()
repo_name = repo_url.split("/tree/")[0].split("/")[-1].replace(".git", "")
# 构建仓库URL
# 阿里云CodeUp的仓库URL格式通常为
# https://codeup.aliyun.com/{organization_id}/{organization_name}/{repo_name}.git
url = f"https://codeup.aliyun.com/{config.aliyun_codeup.organization_id}/{config.aliyun_codeup.organization_name}/{repo_name}.git"
# 添加访问令牌 - 使用base64解码后的令牌
if config.aliyun_codeup.rdc_access_token_encrypted:
try:
# 解码RDC访问令牌
token = base64.b64decode(
config.aliyun_codeup.rdc_access_token_encrypted.encode()
).decode()
# 阿里云CodeUp使用oauth2:token的格式进行身份验证
url = url.replace("https://", f"https://oauth2:{token}@")
logger.debug(f"使用RDC令牌构建阿里云URL: {url.split('@')[0]}@***")
except Exception as e:
logger.error(f"解码RDC令牌失败: {e}")
return url