mirror of
https://github.com/zhenxun-org/zhenxun_bot.git
synced 2026-10-18 02:30:08 +08:00
✨ 支持git更新(github与aliyun codeup),插件商店支持aliyun codeup (#1999)
* ✨ feat(env): 支持git更新 * ✨ feat(aliyun): 更新阿里云URL构建逻辑,支持组织名称并优化令牌解码处理 * ✨ feat(config): 修改错误提示信息,更新基础配置文件名称为.env.example * ⚡ 插件商店支持aliyun * ✨ feat(store): 优化插件数据获取逻辑,合并插件列表和额外插件列表 * 🐛 修复非git仓库的初始化更新 * ✨ feat(update): 增强更新提示信息,添加非git源的变更文件说明 * 🎨 代码格式化 * ✨ webui与resources支持git更新 * ✨ feat(update): 更新webui路径处理逻辑 * Fix/test_runwork (#2001) * fix(test): 修复测试工作流 - 修改自动更新模块中的导入路径 - 更新插件商店模块中的插件信息获取逻辑 - 优化插件添加、更新和移除流程 - 统一插件相关错误信息的格式 - 调整测试用例以适应新的插件管理逻辑 * test(builtin_plugins): 重构插件商店相关测试 - 移除 jsd 相关测试用例,只保留 gh(GitHub)的测试 - 删除了 test_plugin_store.py 文件,清理了插件商店的测试 - 更新了 test_search_plugin.py 中的插件版本号 - 调整了 test_update_plugin.py 中的已加载插件版本 - 移除了 StoreManager 类中的 is_external 变量 - 更新了 RepoFileManager 类中的文件获取逻辑,优先使用 GitHub * ✨ feat(submodule): 添加子模块管理功能,支持子模块的初始化、更新和信息获取 * ✨ feat(update): 移除资源管理器,重构更新逻辑,支持通过ZhenxunRepoManager进行资源和Web UI的更新 * test(auto_update): 修改更新检测消息格式 (#2003) - 移除了不必要的版本号后缀(如 "-e6f17c4") - 统一了版本更新消息的格式,删除了冗余信息 * 🐛 修复web zip更新路径问题 * ⚡ 文件获取优化使用ali * Fix/test (#2008) * test: 修复bot测试 - 在 test_check_update.py 中跳过两个测试函数 - 移除 test_check.py 中的 mocked_api 参数和相关调用 - 删除 test_add_plugin.py 中的多个测试函数 - 移除 test_remove_plugin.py 中的 mocked_api 参数和相关调用 - 删除 test_search_plugin.py 中的多个测试函数 - 移除 test_update_all_plugin.py 和 test_update_plugin.py 中的 mocked_api 参数和相关调用 * 🚨 auto fix by pre-commit hooks --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * 修复res zip更新路径问题 * 🐛 修复zhenxun更新zip占用问题 * ✨ feat(update): 优化资源更新逻辑,调整更新路径和消息处理 --------- Co-authored-by: molanp <104612722+molanp@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
pre-commit-ci[bot]
molanp
parent
7c153721f0
commit
7719be9866
@@ -1,87 +0,0 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import zipfile
|
||||
|
||||
from zhenxun.configs.path_config import FONT_PATH
|
||||
from zhenxun.services.log import logger
|
||||
from zhenxun.utils.github_utils import GithubUtils
|
||||
from zhenxun.utils.http_utils import AsyncHttpx
|
||||
|
||||
CMD_STRING = "ResourceManager"
|
||||
|
||||
|
||||
class DownloadResourceException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class ResourceManager:
|
||||
GITHUB_URL = "https://github.com/zhenxun-org/zhenxun-bot-resources/tree/main"
|
||||
|
||||
RESOURCE_PATH = Path() / "resources"
|
||||
|
||||
TMP_PATH = Path() / "_resource_tmp"
|
||||
|
||||
ZIP_FILE = TMP_PATH / "resources.zip"
|
||||
|
||||
UNZIP_PATH = None
|
||||
|
||||
@classmethod
|
||||
async def init_resources(cls, force: bool = False):
|
||||
if (FONT_PATH.exists() and os.listdir(FONT_PATH)) and not force:
|
||||
return
|
||||
if cls.TMP_PATH.exists():
|
||||
logger.debug(
|
||||
"resources临时文件夹已存在,移除resources临时文件夹", CMD_STRING
|
||||
)
|
||||
shutil.rmtree(cls.TMP_PATH)
|
||||
cls.TMP_PATH.mkdir(parents=True, exist_ok=True)
|
||||
try:
|
||||
await cls.__download_resources()
|
||||
cls.file_handle()
|
||||
except Exception as e:
|
||||
logger.error("获取resources资源包失败", CMD_STRING, e=e)
|
||||
if cls.TMP_PATH.exists():
|
||||
logger.debug("移除resources临时文件夹", CMD_STRING)
|
||||
shutil.rmtree(cls.TMP_PATH)
|
||||
|
||||
@classmethod
|
||||
def file_handle(cls):
|
||||
if not cls.UNZIP_PATH:
|
||||
return
|
||||
cls.__recursive_folder(cls.UNZIP_PATH, "resources")
|
||||
|
||||
@classmethod
|
||||
def __recursive_folder(cls, dir: Path, parent_path: str):
|
||||
for file in dir.iterdir():
|
||||
if file.is_dir():
|
||||
cls.__recursive_folder(file, f"{parent_path}/{file.name}")
|
||||
else:
|
||||
res_file = Path(parent_path) / file.name
|
||||
if res_file.exists():
|
||||
res_file.unlink()
|
||||
res_file.parent.mkdir(parents=True, exist_ok=True)
|
||||
file.rename(res_file)
|
||||
|
||||
@classmethod
|
||||
async def __download_resources(cls):
|
||||
"""获取resources文件夹"""
|
||||
repo_info = GithubUtils.parse_github_url(cls.GITHUB_URL)
|
||||
url = await repo_info.get_archive_download_urls()
|
||||
logger.debug("开始下载resources资源包...", CMD_STRING)
|
||||
if not await AsyncHttpx.download_file(url, cls.ZIP_FILE, stream=True):
|
||||
logger.error(
|
||||
"下载resources资源包失败,请尝试重启重新下载或前往 "
|
||||
"https://github.com/zhenxun-org/zhenxun-bot-resources 手动下载..."
|
||||
)
|
||||
raise DownloadResourceException("下载resources资源包失败...")
|
||||
logger.debug("下载resources资源文件压缩包完成...", CMD_STRING)
|
||||
tf = zipfile.ZipFile(cls.ZIP_FILE)
|
||||
tf.extractall(cls.TMP_PATH)
|
||||
logger.debug("解压文件压缩包完成...", CMD_STRING)
|
||||
download_file_path = cls.TMP_PATH / next(
|
||||
x for x in os.listdir(cls.TMP_PATH) if (cls.TMP_PATH / x).is_dir()
|
||||
)
|
||||
cls.UNZIP_PATH = download_file_path / "resources"
|
||||
if tf:
|
||||
tf.close()
|
||||
@@ -1,3 +1,4 @@
|
||||
import asyncio
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
from subprocess import CalledProcessError
|
||||
@@ -36,7 +37,7 @@ class VirtualEnvPackageManager:
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def install(cls, package: list[str] | str):
|
||||
async def install(cls, package: list[str] | str):
|
||||
"""安装依赖包
|
||||
|
||||
参数:
|
||||
@@ -49,7 +50,8 @@ class VirtualEnvPackageManager:
|
||||
command.append("install")
|
||||
command.append(" ".join(package))
|
||||
logger.info(f"执行虚拟环境安装包指令: {command}", LOG_COMMAND)
|
||||
result = subprocess.run(
|
||||
result = await asyncio.to_thread(
|
||||
subprocess.run,
|
||||
command,
|
||||
check=True,
|
||||
capture_output=True,
|
||||
@@ -65,7 +67,7 @@ class VirtualEnvPackageManager:
|
||||
return e.stderr
|
||||
|
||||
@classmethod
|
||||
def uninstall(cls, package: list[str] | str):
|
||||
async def uninstall(cls, package: list[str] | str):
|
||||
"""卸载依赖包
|
||||
|
||||
参数:
|
||||
@@ -79,7 +81,8 @@ class VirtualEnvPackageManager:
|
||||
command.append("-y")
|
||||
command.append(" ".join(package))
|
||||
logger.info(f"执行虚拟环境卸载包指令: {command}", LOG_COMMAND)
|
||||
result = subprocess.run(
|
||||
result = await asyncio.to_thread(
|
||||
subprocess.run,
|
||||
command,
|
||||
check=True,
|
||||
capture_output=True,
|
||||
@@ -95,7 +98,7 @@ class VirtualEnvPackageManager:
|
||||
return e.stderr
|
||||
|
||||
@classmethod
|
||||
def update(cls, package: list[str] | str):
|
||||
async def update(cls, package: list[str] | str):
|
||||
"""更新依赖包
|
||||
|
||||
参数:
|
||||
@@ -109,7 +112,8 @@ class VirtualEnvPackageManager:
|
||||
command.append("--upgrade")
|
||||
command.append(" ".join(package))
|
||||
logger.info(f"执行虚拟环境更新包指令: {command}", LOG_COMMAND)
|
||||
result = subprocess.run(
|
||||
result = await asyncio.to_thread(
|
||||
subprocess.run,
|
||||
command,
|
||||
check=True,
|
||||
capture_output=True,
|
||||
@@ -122,7 +126,7 @@ class VirtualEnvPackageManager:
|
||||
return e.stderr
|
||||
|
||||
@classmethod
|
||||
def install_requirement(cls, requirement_file: Path):
|
||||
async def install_requirement(cls, requirement_file: Path):
|
||||
"""安装依赖文件
|
||||
|
||||
参数:
|
||||
@@ -139,7 +143,8 @@ class VirtualEnvPackageManager:
|
||||
command.append("-r")
|
||||
command.append(str(requirement_file.absolute()))
|
||||
logger.info(f"执行虚拟环境安装依赖文件指令: {command}", LOG_COMMAND)
|
||||
result = subprocess.run(
|
||||
result = await asyncio.to_thread(
|
||||
subprocess.run,
|
||||
command,
|
||||
check=True,
|
||||
capture_output=True,
|
||||
@@ -158,13 +163,14 @@ class VirtualEnvPackageManager:
|
||||
return e.stderr
|
||||
|
||||
@classmethod
|
||||
def list(cls) -> str:
|
||||
async def list(cls) -> str:
|
||||
"""列出已安装的依赖包"""
|
||||
try:
|
||||
command = cls.__get_command()
|
||||
command.append("list")
|
||||
logger.info(f"执行虚拟环境列出包指令: {command}", LOG_COMMAND)
|
||||
result = subprocess.run(
|
||||
result = await asyncio.to_thread(
|
||||
subprocess.run,
|
||||
command,
|
||||
check=True,
|
||||
capture_output=True,
|
||||
|
||||
@@ -0,0 +1,556 @@
|
||||
"""
|
||||
真寻仓库管理器
|
||||
负责真寻主仓库的更新、版本检查、文件处理等功能
|
||||
"""
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
from typing import ClassVar, Literal
|
||||
import zipfile
|
||||
|
||||
import aiofiles
|
||||
|
||||
from zhenxun.configs.path_config import DATA_PATH, TEMP_PATH
|
||||
from zhenxun.services.log import logger
|
||||
from zhenxun.utils.github_utils import GithubUtils
|
||||
from zhenxun.utils.http_utils import AsyncHttpx
|
||||
from zhenxun.utils.manager.virtual_env_package_manager import VirtualEnvPackageManager
|
||||
from zhenxun.utils.repo_utils import AliyunRepoManager, GithubRepoManager
|
||||
from zhenxun.utils.repo_utils.models import RepoUpdateResult
|
||||
from zhenxun.utils.repo_utils.utils import check_git
|
||||
|
||||
LOG_COMMAND = "ZhenxunRepoManager"
|
||||
|
||||
|
||||
class ZhenxunUpdateException(Exception):
|
||||
"""资源下载异常"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class ZhenxunRepoConfig:
|
||||
"""真寻仓库配置"""
|
||||
|
||||
# Zhenxun Bot 相关配置
|
||||
ZHENXUN_BOT_GIT = "https://github.com/zhenxun-org/zhenxun_bot.git"
|
||||
ZHENXUN_BOT_GITHUB_URL = "https://github.com/HibiKier/zhenxun_bot/tree/main"
|
||||
ZHENXUN_BOT_DOWNLOAD_FILE_STRING = "zhenxun_bot.zip"
|
||||
ZHENXUN_BOT_DOWNLOAD_FILE = TEMP_PATH / ZHENXUN_BOT_DOWNLOAD_FILE_STRING
|
||||
ZHENXUN_BOT_UNZIP_PATH = TEMP_PATH / "zhenxun_bot"
|
||||
ZHENXUN_BOT_CODE_PATH = Path() / "zhenxun"
|
||||
ZHENXUN_BOT_RELEASES_API_URL = (
|
||||
"https://api.github.com/repos/HibiKier/zhenxun_bot/releases/latest"
|
||||
)
|
||||
ZHENXUN_BOT_BACKUP_PATH = Path() / "backup"
|
||||
# 需要替换的文件夹
|
||||
ZHENXUN_BOT_UPDATE_FOLDERS: ClassVar[list[str]] = [
|
||||
"zhenxun/builtin_plugins",
|
||||
"zhenxun/services",
|
||||
"zhenxun/utils",
|
||||
"zhenxun/models",
|
||||
"zhenxun/configs",
|
||||
]
|
||||
ZHENXUN_BOT_VERSION_FILE_STRING = "__version__"
|
||||
ZHENXUN_BOT_VERSION_FILE = Path() / ZHENXUN_BOT_VERSION_FILE_STRING
|
||||
|
||||
# 备份杂项
|
||||
BACKUP_FILES: ClassVar[list[str]] = [
|
||||
"pyproject.toml",
|
||||
"poetry.lock",
|
||||
"requirements.txt",
|
||||
".env.dev",
|
||||
".env.example",
|
||||
]
|
||||
|
||||
# WEB UI 相关配置
|
||||
WEBUI_GIT = "https://github.com/HibiKier/zhenxun_bot_webui.git"
|
||||
WEBUI_DIST_GITHUB_URL = "https://github.com/HibiKier/zhenxun_bot_webui/tree/dist"
|
||||
WEBUI_DOWNLOAD_FILE_STRING = "webui_assets.zip"
|
||||
WEBUI_DOWNLOAD_FILE = TEMP_PATH / WEBUI_DOWNLOAD_FILE_STRING
|
||||
WEBUI_UNZIP_PATH = TEMP_PATH / "web_ui"
|
||||
WEBUI_PATH = DATA_PATH / "web_ui" / "public"
|
||||
WEBUI_BACKUP_PATH = DATA_PATH / "web_ui" / "backup_public"
|
||||
|
||||
# 资源管理相关配置
|
||||
RESOURCE_GIT = "https://github.com/zhenxun-org/zhenxun-bot-resources.git"
|
||||
RESOURCE_GITHUB_URL = (
|
||||
"https://github.com/zhenxun-org/zhenxun-bot-resources/tree/main"
|
||||
)
|
||||
RESOURCE_ZIP_FILE_STRING = "resources.zip"
|
||||
RESOURCE_ZIP_FILE = TEMP_PATH / RESOURCE_ZIP_FILE_STRING
|
||||
RESOURCE_UNZIP_PATH = TEMP_PATH / "resources"
|
||||
RESOURCE_PATH = Path() / "resources"
|
||||
|
||||
REQUIREMENTS_FILE_STRING = "requirements.txt"
|
||||
REQUIREMENTS_FILE = Path() / REQUIREMENTS_FILE_STRING
|
||||
|
||||
PYPROJECT_FILE_STRING = "pyproject.toml"
|
||||
PYPROJECT_FILE = Path() / PYPROJECT_FILE_STRING
|
||||
|
||||
PYPROJECT_LOCK_FILE_STRING = "poetry.lock"
|
||||
PYPROJECT_LOCK_FILE = Path() / PYPROJECT_LOCK_FILE_STRING
|
||||
|
||||
|
||||
class ZhenxunRepoManagerClass:
|
||||
"""真寻仓库管理器"""
|
||||
|
||||
def __init__(self):
|
||||
self.config = ZhenxunRepoConfig()
|
||||
|
||||
def __clear_folder(self, folder_path: Path):
|
||||
"""
|
||||
清空文件夹
|
||||
|
||||
参数:
|
||||
folder_path: 文件夹路径
|
||||
"""
|
||||
if not folder_path.exists():
|
||||
return
|
||||
for filename in os.listdir(folder_path):
|
||||
file_path = folder_path / filename
|
||||
try:
|
||||
if file_path.is_file():
|
||||
os.unlink(file_path)
|
||||
elif file_path.is_dir() and not filename.startswith("."):
|
||||
shutil.rmtree(file_path)
|
||||
except Exception as e:
|
||||
logger.warning(f"无法删除 {file_path}", LOG_COMMAND, e=e)
|
||||
|
||||
def __copy_files(self, src_path: Path, dest_path: Path, incremental: bool = False):
|
||||
"""
|
||||
复制文件或文件夹
|
||||
|
||||
参数:
|
||||
src_path: 源文件或文件夹路径
|
||||
dest_path: 目标文件或文件夹路径
|
||||
incremental: 是否增量复制
|
||||
"""
|
||||
if src_path.is_file():
|
||||
shutil.copy(src_path, dest_path)
|
||||
logger.debug(f"复制文件 {src_path} -> {dest_path}", LOG_COMMAND)
|
||||
elif src_path.is_dir():
|
||||
for filename in os.listdir(src_path):
|
||||
file_path = src_path / filename
|
||||
dest_file = dest_path / filename
|
||||
dest_file.parent.mkdir(exist_ok=True, parents=True)
|
||||
if file_path.is_file():
|
||||
if dest_file.exists():
|
||||
dest_file.unlink()
|
||||
shutil.copy(file_path, dest_file)
|
||||
logger.debug(f"复制文件 {file_path} -> {dest_file}", LOG_COMMAND)
|
||||
elif file_path.is_dir():
|
||||
if incremental:
|
||||
self.__copy_files(file_path, dest_file, incremental=True)
|
||||
else:
|
||||
if dest_file.exists():
|
||||
shutil.rmtree(dest_file, True)
|
||||
shutil.copytree(file_path, dest_file)
|
||||
logger.debug(
|
||||
f"复制文件夹 {file_path} -> {dest_file}",
|
||||
LOG_COMMAND,
|
||||
)
|
||||
|
||||
# ==================== Zhenxun Bot 相关方法 ====================
|
||||
|
||||
async def zhenxun_get_version_from_repo(self) -> str:
|
||||
"""从指定分支获取版本号
|
||||
|
||||
|
||||
返回:
|
||||
str: 版本号
|
||||
"""
|
||||
repo_info = GithubUtils.parse_github_url(self.config.ZHENXUN_BOT_GITHUB_URL)
|
||||
version_url = await repo_info.get_raw_download_urls(
|
||||
path=self.config.ZHENXUN_BOT_VERSION_FILE_STRING
|
||||
)
|
||||
try:
|
||||
res = await AsyncHttpx.get(version_url)
|
||||
if res.status_code == 200:
|
||||
return res.text.strip()
|
||||
except Exception as e:
|
||||
logger.error(f"获取 {repo_info.branch} 分支版本失败", LOG_COMMAND, e=e)
|
||||
return "未知版本"
|
||||
|
||||
async def zhenxun_write_version_file(self, version: str):
|
||||
"""写入版本文件"""
|
||||
async with aiofiles.open(
|
||||
self.config.ZHENXUN_BOT_VERSION_FILE, "w", encoding="utf8"
|
||||
) as f:
|
||||
await f.write(f"__version__: {version}")
|
||||
|
||||
def __backup_zhenxun(self):
|
||||
"""备份真寻文件"""
|
||||
for filename in os.listdir(self.config.ZHENXUN_BOT_CODE_PATH):
|
||||
file_path = self.config.ZHENXUN_BOT_CODE_PATH / filename
|
||||
if file_path.exists():
|
||||
self.__copy_files(
|
||||
file_path,
|
||||
self.config.ZHENXUN_BOT_BACKUP_PATH / filename,
|
||||
True,
|
||||
)
|
||||
for filename in self.config.BACKUP_FILES:
|
||||
file_path = Path() / filename
|
||||
if file_path.exists():
|
||||
self.__copy_files(
|
||||
file_path,
|
||||
self.config.ZHENXUN_BOT_BACKUP_PATH / filename,
|
||||
)
|
||||
|
||||
async def zhenxun_get_latest_releases_data(self) -> dict:
|
||||
"""获取真寻releases最新版本信息
|
||||
|
||||
返回:
|
||||
dict: 最新版本数据
|
||||
"""
|
||||
try:
|
||||
res = await AsyncHttpx.get(self.config.ZHENXUN_BOT_RELEASES_API_URL)
|
||||
if res.status_code == 200:
|
||||
return res.json()
|
||||
except Exception as e:
|
||||
logger.error("检查更新真寻获取版本失败", LOG_COMMAND, e=e)
|
||||
return {}
|
||||
|
||||
async def zhenxun_download_zip(self, ver_type: Literal["main", "release"]) -> str:
|
||||
"""下载真寻最新版文件
|
||||
|
||||
参数:
|
||||
ver_type: 版本类型,main 为最新版,release 为最新release版
|
||||
|
||||
返回:
|
||||
str: 版本号
|
||||
"""
|
||||
repo_info = GithubUtils.parse_github_url(self.config.ZHENXUN_BOT_GITHUB_URL)
|
||||
if ver_type == "main":
|
||||
download_url = await repo_info.get_archive_download_urls()
|
||||
new_version = await self.zhenxun_get_version_from_repo()
|
||||
else:
|
||||
release_data = await self.zhenxun_get_latest_releases_data()
|
||||
logger.debug(f"获取真寻RELEASES最新版本信息: {release_data}", LOG_COMMAND)
|
||||
if not release_data:
|
||||
raise ZhenxunUpdateException("获取真寻RELEASES最新版本失败...")
|
||||
new_version = release_data.get("name", "")
|
||||
download_url = await repo_info.get_release_source_download_urls_tgz(
|
||||
new_version
|
||||
)
|
||||
if not download_url:
|
||||
raise ZhenxunUpdateException("获取真寻最新版文件下载链接失败...")
|
||||
if self.config.ZHENXUN_BOT_DOWNLOAD_FILE.exists():
|
||||
self.config.ZHENXUN_BOT_DOWNLOAD_FILE.unlink()
|
||||
if await AsyncHttpx.download_file(
|
||||
download_url, self.config.ZHENXUN_BOT_DOWNLOAD_FILE, stream=True
|
||||
):
|
||||
logger.debug("下载真寻最新版文件完成...", LOG_COMMAND)
|
||||
else:
|
||||
raise ZhenxunUpdateException("下载真寻最新版文件失败...")
|
||||
return new_version
|
||||
|
||||
async def zhenxun_unzip(self):
|
||||
"""解压真寻最新版文件"""
|
||||
if not self.config.ZHENXUN_BOT_DOWNLOAD_FILE.exists():
|
||||
raise FileNotFoundError("真寻最新版文件不存在")
|
||||
if self.config.ZHENXUN_BOT_UNZIP_PATH.exists():
|
||||
shutil.rmtree(self.config.ZHENXUN_BOT_UNZIP_PATH)
|
||||
tf = None
|
||||
try:
|
||||
tf = zipfile.ZipFile(self.config.ZHENXUN_BOT_DOWNLOAD_FILE)
|
||||
tf.extractall(self.config.ZHENXUN_BOT_UNZIP_PATH)
|
||||
logger.debug("解压Zhenxun Bot文件压缩包完成!", LOG_COMMAND)
|
||||
self.__backup_zhenxun()
|
||||
for filename in self.config.BACKUP_FILES:
|
||||
self.__copy_files(
|
||||
self.config.ZHENXUN_BOT_UNZIP_PATH / filename,
|
||||
Path() / filename,
|
||||
)
|
||||
logger.debug("备份真寻更新文件完成!", LOG_COMMAND)
|
||||
unzip_dir = next(self.config.ZHENXUN_BOT_UNZIP_PATH.iterdir())
|
||||
for folder in self.config.ZHENXUN_BOT_UPDATE_FOLDERS:
|
||||
self.__copy_files(unzip_dir / folder, Path() / folder)
|
||||
logger.debug("移动真寻更新文件完成!", LOG_COMMAND)
|
||||
if self.config.ZHENXUN_BOT_UNZIP_PATH.exists():
|
||||
shutil.rmtree(self.config.ZHENXUN_BOT_UNZIP_PATH)
|
||||
except Exception as e:
|
||||
logger.error("解压真寻最新版文件失败...", LOG_COMMAND, e=e)
|
||||
raise
|
||||
finally:
|
||||
if tf:
|
||||
tf.close()
|
||||
|
||||
async def zhenxun_zip_update(self, ver_type: Literal["main", "release"]) -> str:
|
||||
"""使用zip更新真寻
|
||||
|
||||
参数:
|
||||
ver_type: 版本类型,main 为最新版,release 为最新release版
|
||||
|
||||
返回:
|
||||
str: 版本号
|
||||
"""
|
||||
new_version = await self.zhenxun_download_zip(ver_type)
|
||||
await self.zhenxun_unzip()
|
||||
await self.zhenxun_write_version_file(new_version)
|
||||
return new_version
|
||||
|
||||
async def zhenxun_git_update(
|
||||
self, source: Literal["git", "ali"], branch: str = "main", force: bool = False
|
||||
) -> RepoUpdateResult:
|
||||
"""使用git或阿里云更新真寻
|
||||
|
||||
参数:
|
||||
source: 更新源,git 为 git 更新,ali 为阿里云更新
|
||||
branch: 分支名称
|
||||
force: 是否强制更新
|
||||
"""
|
||||
if source == "git":
|
||||
return await GithubRepoManager.update_via_git(
|
||||
self.config.ZHENXUN_BOT_GIT,
|
||||
Path(),
|
||||
branch=branch,
|
||||
force=force,
|
||||
)
|
||||
else:
|
||||
return await AliyunRepoManager.update_via_git(
|
||||
self.config.ZHENXUN_BOT_GIT,
|
||||
Path(),
|
||||
branch=branch,
|
||||
force=force,
|
||||
)
|
||||
|
||||
async def zhenxun_update(
|
||||
self,
|
||||
source: Literal["git", "ali"] = "ali",
|
||||
branch: str = "main",
|
||||
force: bool = False,
|
||||
ver_type: Literal["main", "release"] = "main",
|
||||
):
|
||||
"""更新真寻
|
||||
|
||||
参数:
|
||||
source: 更新源,git 为 git 更新,ali 为阿里云更新
|
||||
branch: 分支名称
|
||||
force: 是否强制更新
|
||||
ver_type: 版本类型,main 为最新版,release 为最新release版
|
||||
"""
|
||||
if await check_git():
|
||||
await self.zhenxun_git_update(source, branch, force)
|
||||
logger.debug("使用git更新真寻!", LOG_COMMAND)
|
||||
else:
|
||||
await self.zhenxun_zip_update(ver_type)
|
||||
logger.debug("使用zip更新真寻!", LOG_COMMAND)
|
||||
|
||||
async def install_requirements(self):
|
||||
"""安装真寻依赖"""
|
||||
await VirtualEnvPackageManager.install_requirement(
|
||||
self.config.REQUIREMENTS_FILE
|
||||
)
|
||||
|
||||
# ==================== 资源管理相关方法 ====================
|
||||
|
||||
def check_resources_exists(self) -> bool:
|
||||
"""检查资源文件是否存在
|
||||
|
||||
返回:
|
||||
bool: 是否存在
|
||||
"""
|
||||
if self.config.RESOURCE_PATH.exists():
|
||||
font_path = self.config.RESOURCE_PATH / "font"
|
||||
if font_path.exists() and os.listdir(font_path):
|
||||
return True
|
||||
return False
|
||||
|
||||
async def resources_download_zip(self):
|
||||
"""下载资源文件"""
|
||||
download_url = await GithubUtils.parse_github_url(
|
||||
self.config.RESOURCE_GITHUB_URL
|
||||
).get_archive_download_urls()
|
||||
logger.debug("开始下载resources资源包...", LOG_COMMAND)
|
||||
if await AsyncHttpx.download_file(
|
||||
download_url, self.config.RESOURCE_ZIP_FILE, stream=True
|
||||
):
|
||||
logger.debug("下载resources资源文件压缩包成功!", LOG_COMMAND)
|
||||
else:
|
||||
raise ZhenxunUpdateException("下载resources资源包失败...")
|
||||
|
||||
async def resources_unzip(self):
|
||||
"""解压资源文件"""
|
||||
if not self.config.RESOURCE_ZIP_FILE.exists():
|
||||
raise FileNotFoundError("资源文件压缩包不存在")
|
||||
if self.config.RESOURCE_UNZIP_PATH.exists():
|
||||
shutil.rmtree(self.config.RESOURCE_UNZIP_PATH)
|
||||
tf = None
|
||||
try:
|
||||
tf = zipfile.ZipFile(self.config.RESOURCE_ZIP_FILE)
|
||||
tf.extractall(self.config.RESOURCE_UNZIP_PATH)
|
||||
logger.debug("解压文件压缩包完成...", LOG_COMMAND)
|
||||
unzip_dir = next(self.config.RESOURCE_UNZIP_PATH.iterdir())
|
||||
self.__copy_files(unzip_dir, self.config.RESOURCE_PATH, True)
|
||||
logger.debug("复制资源文件完成!", LOG_COMMAND)
|
||||
shutil.rmtree(self.config.RESOURCE_UNZIP_PATH, ignore_errors=True)
|
||||
except Exception as e:
|
||||
logger.error("解压资源文件失败...", LOG_COMMAND, e=e)
|
||||
raise
|
||||
finally:
|
||||
if tf:
|
||||
tf.close()
|
||||
|
||||
async def resources_zip_update(self):
|
||||
"""使用zip更新资源文件"""
|
||||
await self.resources_download_zip()
|
||||
await self.resources_unzip()
|
||||
|
||||
async def resources_git_update(
|
||||
self, source: Literal["git", "ali"], branch: str = "main", force: bool = False
|
||||
) -> RepoUpdateResult:
|
||||
"""使用git或阿里云更新资源文件
|
||||
|
||||
参数:
|
||||
source: 更新源,git 为 git 更新,ali 为阿里云更新
|
||||
branch: 分支名称
|
||||
force: 是否强制更新
|
||||
"""
|
||||
if source == "git":
|
||||
return await GithubRepoManager.update_via_git(
|
||||
self.config.RESOURCE_GIT,
|
||||
self.config.RESOURCE_PATH,
|
||||
branch=branch,
|
||||
force=force,
|
||||
)
|
||||
else:
|
||||
return await AliyunRepoManager.update_via_git(
|
||||
self.config.RESOURCE_GIT,
|
||||
self.config.RESOURCE_PATH,
|
||||
branch=branch,
|
||||
force=force,
|
||||
)
|
||||
|
||||
async def resources_update(
|
||||
self,
|
||||
source: Literal["git", "ali"] = "ali",
|
||||
branch: str = "main",
|
||||
force: bool = False,
|
||||
):
|
||||
"""更新资源文件
|
||||
|
||||
参数:
|
||||
source: 更新源,git 为 git 更新,ali 为阿里云更新
|
||||
branch: 分支名称
|
||||
force: 是否强制更新
|
||||
"""
|
||||
if await check_git():
|
||||
await self.resources_git_update(source, branch, force)
|
||||
logger.debug("使用git更新资源文件!", LOG_COMMAND)
|
||||
else:
|
||||
await self.resources_zip_update()
|
||||
logger.debug("使用zip更新资源文件!", LOG_COMMAND)
|
||||
|
||||
# ==================== Web UI 管理相关方法 ====================
|
||||
|
||||
def check_webui_exists(self) -> bool:
|
||||
"""检查 Web UI 资源是否存在"""
|
||||
return bool(
|
||||
self.config.WEBUI_PATH.exists() and os.listdir(self.config.WEBUI_PATH)
|
||||
)
|
||||
|
||||
async def webui_download_zip(self):
|
||||
"""下载 WEBUI_ASSETS 资源"""
|
||||
download_url = await GithubUtils.parse_github_url(
|
||||
self.config.WEBUI_DIST_GITHUB_URL
|
||||
).get_archive_download_urls()
|
||||
logger.info("开始下载 WEBUI_ASSETS 资源...", LOG_COMMAND)
|
||||
if await AsyncHttpx.download_file(
|
||||
download_url, self.config.WEBUI_DOWNLOAD_FILE, follow_redirects=True
|
||||
):
|
||||
logger.info("下载 WEBUI_ASSETS 成功!", LOG_COMMAND)
|
||||
else:
|
||||
raise ZhenxunUpdateException("下载 WEBUI_ASSETS 失败", LOG_COMMAND)
|
||||
|
||||
def __backup_webui(self):
|
||||
"""备份 WEBUI_ASSERT 资源"""
|
||||
if self.config.WEBUI_PATH.exists():
|
||||
if self.config.WEBUI_BACKUP_PATH.exists():
|
||||
logger.debug(
|
||||
f"删除旧的备份webui文件夹 {self.config.WEBUI_BACKUP_PATH}",
|
||||
LOG_COMMAND,
|
||||
)
|
||||
shutil.rmtree(self.config.WEBUI_BACKUP_PATH)
|
||||
shutil.copytree(self.config.WEBUI_PATH, self.config.WEBUI_BACKUP_PATH)
|
||||
|
||||
async def webui_unzip(self):
|
||||
"""解压 WEBUI_ASSETS 资源
|
||||
|
||||
返回:
|
||||
str: 更新结果
|
||||
"""
|
||||
if not self.config.WEBUI_DOWNLOAD_FILE.exists():
|
||||
raise FileNotFoundError("webui文件压缩包不存在")
|
||||
tf = None
|
||||
try:
|
||||
self.__backup_webui()
|
||||
self.__clear_folder(self.config.WEBUI_PATH)
|
||||
tf = zipfile.ZipFile(self.config.WEBUI_DOWNLOAD_FILE)
|
||||
tf.extractall(self.config.WEBUI_UNZIP_PATH)
|
||||
logger.debug("Web UI 解压文件压缩包完成...", LOG_COMMAND)
|
||||
unzip_dir = next(self.config.WEBUI_UNZIP_PATH.iterdir())
|
||||
self.__copy_files(unzip_dir, self.config.WEBUI_PATH)
|
||||
logger.debug("Web UI 复制 WEBUI_ASSETS 成功!", LOG_COMMAND)
|
||||
shutil.rmtree(self.config.WEBUI_UNZIP_PATH, ignore_errors=True)
|
||||
except Exception as e:
|
||||
if self.config.WEBUI_BACKUP_PATH.exists():
|
||||
self.__copy_files(self.config.WEBUI_BACKUP_PATH, self.config.WEBUI_PATH)
|
||||
logger.debug("恢复备份 WEBUI_ASSETS 成功!", LOG_COMMAND)
|
||||
shutil.rmtree(self.config.WEBUI_BACKUP_PATH, ignore_errors=True)
|
||||
logger.error("Web UI 更新失败", LOG_COMMAND, e=e)
|
||||
raise
|
||||
finally:
|
||||
if tf:
|
||||
tf.close()
|
||||
|
||||
async def webui_zip_update(self):
|
||||
"""使用zip更新 Web UI"""
|
||||
await self.webui_download_zip()
|
||||
await self.webui_unzip()
|
||||
|
||||
async def webui_git_update(
|
||||
self, source: Literal["git", "ali"], branch: str = "dist", force: bool = False
|
||||
) -> RepoUpdateResult:
|
||||
"""使用git或阿里云更新 Web UI
|
||||
|
||||
参数:
|
||||
source: 更新源,git 为 git 更新,ali 为阿里云更新
|
||||
branch: 分支名称
|
||||
force: 是否强制更新
|
||||
"""
|
||||
if source == "git":
|
||||
return await GithubRepoManager.update_via_git(
|
||||
self.config.WEBUI_GIT,
|
||||
self.config.WEBUI_PATH,
|
||||
branch=branch,
|
||||
force=force,
|
||||
)
|
||||
else:
|
||||
return await AliyunRepoManager.update_via_git(
|
||||
self.config.WEBUI_GIT,
|
||||
self.config.WEBUI_PATH,
|
||||
branch=branch,
|
||||
force=force,
|
||||
)
|
||||
|
||||
async def webui_update(
|
||||
self,
|
||||
source: Literal["git", "ali"] = "ali",
|
||||
branch: str = "dist",
|
||||
force: bool = False,
|
||||
):
|
||||
"""更新 Web UI
|
||||
|
||||
参数:
|
||||
source: 更新源,git 为 git 更新,ali 为阿里云更新
|
||||
"""
|
||||
if await check_git():
|
||||
await self.webui_git_update(source, branch, force)
|
||||
logger.debug("使用git更新Web UI!", LOG_COMMAND)
|
||||
else:
|
||||
await self.webui_zip_update()
|
||||
logger.debug("使用zip更新Web UI!", LOG_COMMAND)
|
||||
|
||||
|
||||
ZhenxunRepoManager = ZhenxunRepoManagerClass()
|
||||
Reference in New Issue
Block a user