mirror of
https://github.com/zhenxun-org/zhenxun_bot.git
synced 2025-12-15 14:22:55 +08:00
✨ 增强阿里云和GitHub的文件管理功能,新增Git不可用异常处理,优化稀疏检出逻辑,提升代码可读性和稳定性。
This commit is contained in:
parent
f3ff5a3404
commit
b5417d1141
@ -276,6 +276,7 @@ class StoreManager:
|
|||||||
else:
|
else:
|
||||||
files = [RepoFileInfo(path=f"{replace_module_path}.py", is_dir=False)]
|
files = [RepoFileInfo(path=f"{replace_module_path}.py", is_dir=False)]
|
||||||
local_path = BASE_PATH / "plugins" if is_external else BASE_PATH
|
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]
|
files = [file for file in files if not file.is_dir]
|
||||||
download_files = [(file.path, local_path / file.path) for file in files]
|
download_files = [(file.path, local_path / file.path) for file in files]
|
||||||
await RepoFileManager.download_files(
|
await RepoFileManager.download_files(
|
||||||
@ -283,7 +284,7 @@ class StoreManager:
|
|||||||
download_files,
|
download_files,
|
||||||
repo_type=repo_type,
|
repo_type=repo_type,
|
||||||
sparse_path=replace_module_path,
|
sparse_path=replace_module_path,
|
||||||
target_dir=local_path / plugin_name,
|
target_dir=target_dir,
|
||||||
)
|
)
|
||||||
|
|
||||||
requirement_paths = [
|
requirement_paths = [
|
||||||
|
|||||||
@ -11,6 +11,7 @@ from aiocache import cached
|
|||||||
|
|
||||||
from zhenxun.services.log import logger
|
from zhenxun.services.log import logger
|
||||||
from zhenxun.utils.github_utils.models import AliyunFileInfo
|
from zhenxun.utils.github_utils.models import AliyunFileInfo
|
||||||
|
from zhenxun.utils.repo_utils.utils import prepare_aliyun_url
|
||||||
|
|
||||||
from .base_manager import BaseRepoManager
|
from .base_manager import BaseRepoManager
|
||||||
from .config import LOG_COMMAND, RepoConfig
|
from .config import LOG_COMMAND, RepoConfig
|
||||||
@ -445,32 +446,6 @@ class AliyunCodeupManager(BaseRepoManager):
|
|||||||
返回:
|
返回:
|
||||||
RepoUpdateResult: 更新结果
|
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方法
|
# 调用基类的update_via_git方法
|
||||||
return await super().update_via_git(
|
return await super().update_via_git(
|
||||||
repo_url=repo_url,
|
repo_url=repo_url,
|
||||||
|
|||||||
@ -66,3 +66,10 @@ class ConfigError(RepoManagerError):
|
|||||||
|
|
||||||
def __init__(self, message: str):
|
def __init__(self, message: str):
|
||||||
super().__init__(f"配置错误: {message}")
|
super().__init__(f"配置错误: {message}")
|
||||||
|
|
||||||
|
|
||||||
|
class GitUnavailableError(RepoManagerError):
|
||||||
|
"""Git不可用异常"""
|
||||||
|
|
||||||
|
def __init__(self, message: str = "Git命令不可用"):
|
||||||
|
super().__init__(f"Git不可用: {message}")
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
仓库文件管理器,用于从GitHub和阿里云CodeUp获取指定文件内容
|
仓库文件管理器,用于从GitHub和阿里云CodeUp获取指定文件内容
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import contextlib
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import cast, overload
|
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 zhenxun.utils.utils import is_binary_file
|
||||||
|
|
||||||
from .config import LOG_COMMAND, RepoConfig
|
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 .models import FileDownloadResult, RepoFileInfo, RepoType
|
||||||
from .utils import sparse_checkout_clone
|
from .utils import prepare_aliyun_url, sparse_checkout_clone
|
||||||
|
|
||||||
|
|
||||||
class RepoFileManager:
|
class RepoFileManager:
|
||||||
@ -264,15 +270,15 @@ class RepoFileManager:
|
|||||||
if repo_type is None:
|
if repo_type is None:
|
||||||
# 尝试GitHub,失败则尝试阿里云
|
# 尝试GitHub,失败则尝试阿里云
|
||||||
try:
|
try:
|
||||||
return await self._list_github_directory_files(
|
return await self._list_aliyun_directory_files(
|
||||||
repo_url, directory_path, branch, recursive
|
repo_name, directory_path, branch, recursive
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
"获取GitHub目录文件失败,尝试阿里云", LOG_COMMAND, e=e
|
"获取阿里云目录文件失败,尝试GitHub", LOG_COMMAND, e=e
|
||||||
)
|
)
|
||||||
return await self._list_aliyun_directory_files(
|
return await self._list_github_directory_files(
|
||||||
repo_name, directory_path, branch, recursive
|
repo_url, directory_path, branch, recursive
|
||||||
)
|
)
|
||||||
if repo_type == RepoType.GITHUB:
|
if repo_type == RepoType.GITHUB:
|
||||||
return await self._list_github_directory_files(
|
return await self._list_github_directory_files(
|
||||||
@ -512,7 +518,7 @@ class RepoFileManager:
|
|||||||
)
|
)
|
||||||
if (
|
if (
|
||||||
any(is_binary_file(file_name) for file_name in file_path_mapping)
|
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 sparse_path
|
||||||
and target_dir
|
and target_dir
|
||||||
):
|
):
|
||||||
@ -597,7 +603,7 @@ class RepoFileManager:
|
|||||||
) -> FileDownloadResult:
|
) -> FileDownloadResult:
|
||||||
try:
|
try:
|
||||||
await sparse_checkout_clone(
|
await sparse_checkout_clone(
|
||||||
repo_url=repo_url,
|
repo_url=prepare_aliyun_url(repo_url),
|
||||||
branch=branch,
|
branch=branch,
|
||||||
sparse_path=sparse_path,
|
sparse_path=sparse_path,
|
||||||
target_dir=target_dir,
|
target_dir=target_dir,
|
||||||
@ -606,14 +612,17 @@ class RepoFileManager:
|
|||||||
if target_dir.exists():
|
if target_dir.exists():
|
||||||
for f in target_dir.rglob("*"):
|
for f in target_dir.rglob("*"):
|
||||||
if f.is_file():
|
if f.is_file():
|
||||||
try:
|
with contextlib.suppress(Exception):
|
||||||
total_size += f.stat().st_size
|
total_size += f.stat().st_size
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
result.success = True
|
result.success = True
|
||||||
result.file_size = total_size
|
result.file_size = total_size
|
||||||
logger.info(f"sparse-checkout 克隆成功: {target_dir}")
|
logger.info(f"sparse-checkout 克隆成功: {target_dir}")
|
||||||
return result
|
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:
|
except Exception as e:
|
||||||
logger.error(f"sparse-checkout 克隆失败: {e}")
|
logger.error(f"sparse-checkout 克隆失败: {e}")
|
||||||
result.success = False
|
result.success = False
|
||||||
|
|||||||
@ -3,12 +3,15 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import base64
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import re
|
import re
|
||||||
|
import shutil
|
||||||
|
|
||||||
from zhenxun.services.log import logger
|
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:
|
async def check_git() -> bool:
|
||||||
@ -152,7 +155,7 @@ async def sparse_checkout_clone(
|
|||||||
target_dir.mkdir(parents=True, exist_ok=True)
|
target_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
if not await check_git():
|
if not await check_git():
|
||||||
raise RuntimeError("未检测到可用的 git 命令")
|
raise GitUnavailableError()
|
||||||
|
|
||||||
git_dir = target_dir / ".git"
|
git_dir = target_dir / ".git"
|
||||||
if not git_dir.exists():
|
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)
|
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("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:
|
if not sparse_path:
|
||||||
raise RuntimeError("sparse-checkout 路径不能为空")
|
raise RuntimeError("sparse-checkout 路径不能为空")
|
||||||
|
|
||||||
|
# 使用 --no-cone 模式,直接指定要检出的具体路径
|
||||||
|
# 例如:sparse_path="plugins/mahiro" -> 只检出 plugins/mahiro/ 下的内容
|
||||||
success, out, err = await run_git_command(
|
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:
|
if not success:
|
||||||
raise RuntimeError(f"配置稀疏路径失败: {err or out}")
|
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(f"reset --hard origin/{branch}", target_dir)
|
||||||
await run_git_command("clean -xdf", 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
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user