From a079821976a4240f8009612ee34f1567409c04f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=86=B0=E8=8B=B7=E6=99=B6?= <2749332490@qq.com> Date: Mon, 17 Mar 2025 23:12:23 +0800 Subject: [PATCH 1/3] fix: fix SSL certificateverification error during GitHub plugin installation. - Create a custom SSL context using certifi for proper HTTPS certificate verification, meow - Add the ssl parameter to aiohttp requests to prevent download failure due to missing root certificates, meow - Improve error messages and enhance the overall plugin installation process, meow! --- pkg/plugin/installers/github.py | 55 ++++++++++----------------------- 1 file changed, 17 insertions(+), 38 deletions(-) diff --git a/pkg/plugin/installers/github.py b/pkg/plugin/installers/github.py index 45439579..039ff196 100644 --- a/pkg/plugin/installers/github.py +++ b/pkg/plugin/installers/github.py @@ -4,6 +4,8 @@ import re import os import shutil import zipfile +import ssl +import certifi import aiohttp import aiofiles @@ -21,44 +23,39 @@ class GitHubRepoInstaller(installer.PluginInstaller): def get_github_plugin_repo_label(self, repo_url: str) -> list[str]: """获取username, repo""" - - # 提取 username/repo , 正则表达式 repo = re.findall( r"(?:https?://github\.com/|git@github\.com:)([^/]+/[^/]+?)(?:\.git|/|$)", repo_url, ) - - if len(repo) > 0: # github + if len(repo) > 0: return repo[0].split("/") else: return None - + async def download_plugin_source_code(self, repo_url: str, target_path: str, task_context: taskmgr.TaskContext = taskmgr.TaskContext.placeholder()) -> str: """下载插件源码(全异步)""" - - # 提取 username/repo , 正则表达式 repo = self.get_github_plugin_repo_label(repo_url) - - target_path += repo[1] - if repo is None: raise errors.PluginInstallerError('仅支持GitHub仓库地址') + target_path += repo[1] self.ap.logger.debug("正在下载源码...") task_context.trace("下载源码...", "download-plugin-source-code") zipball_url = f"https://api.github.com/repos/{'/'.join(repo)}/zipball/HEAD" - zip_resp: bytes = None + + # 创建自定义SSL上下文,使用certifi提供的根证书 + ssl_context = ssl.create_default_context(cafile=certifi.where()) async with aiohttp.ClientSession(trust_env=True) as session: async with session.get( url=zipball_url, - timeout=aiohttp.ClientTimeout(total=300) + timeout=aiohttp.ClientTimeout(total=300), + ssl=ssl_context # 使用自定义SSL上下文来验证证书 ) as resp: if resp.status != 200: - raise errors.PluginInstallerError(f"下载源码失败: {resp.text}") - + raise errors.PluginInstallerError(f"下载源码失败: {await resp.text()}") zip_resp = await resp.read() if await aiofiles_os.path.exists("temp/" + target_path): @@ -80,15 +77,11 @@ class GitHubRepoInstaller(installer.PluginInstaller): await aiofiles_os.remove("temp/" + target_path + "/source.zip") import glob - unzip_dir = glob.glob("temp/" + target_path + "/*")[0] - await aioshutil.copytree(unzip_dir, target_path + "/") - await aioshutil.rmtree(unzip_dir) self.ap.logger.debug("源码下载完成。") - return repo[1] async def install_requirements(self, path: str): @@ -100,20 +93,14 @@ class GitHubRepoInstaller(installer.PluginInstaller): plugin_source: str, task_context: taskmgr.TaskContext = taskmgr.TaskContext.placeholder(), ): - """安装插件 - """ + """安装插件""" task_context.trace("下载插件源码...", "install-plugin") - repo_label = await self.download_plugin_source_code(plugin_source, "plugins/", task_context) - task_context.trace("安装插件依赖...", "install-plugin") - await self.install_requirements("plugins/" + repo_label) - task_context.trace("完成.", "install-plugin") - await self.ap.plugin_mgr.setting.record_installed_plugin_source( - "plugins/"+repo_label+'/', plugin_source + "plugins/" + repo_label + '/', plugin_source ) async def uninstall_plugin( @@ -121,10 +108,8 @@ class GitHubRepoInstaller(installer.PluginInstaller): plugin_name: str, task_context: taskmgr.TaskContext = taskmgr.TaskContext.placeholder(), ): - """卸载插件 - """ + """卸载插件""" plugin_container = self.ap.plugin_mgr.get_plugin_by_name(plugin_name) - if plugin_container is None: raise errors.PluginInstallerError('插件不存在或未成功加载') else: @@ -135,24 +120,18 @@ class GitHubRepoInstaller(installer.PluginInstaller): async def update_plugin( self, plugin_name: str, - plugin_source: str=None, + plugin_source: str = None, task_context: taskmgr.TaskContext = taskmgr.TaskContext.placeholder(), ): - """更新插件 - """ + """更新插件""" task_context.trace("更新插件...", "update-plugin") - plugin_container = self.ap.plugin_mgr.get_plugin_by_name(plugin_name) - if plugin_container is None: raise errors.PluginInstallerError('插件不存在或未成功加载') else: if plugin_container.plugin_source: plugin_source = plugin_container.plugin_source - task_context.trace("转交安装任务.", "update-plugin") - await self.install_plugin(plugin_source, task_context) - else: - raise errors.PluginInstallerError('插件无源码信息,无法更新') + raise errors.PluginInstallerError('插件无源码信息,无法更新') \ No newline at end of file From dd362780326c81384d3074f0620d3cca49521969 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=86=B0=E8=8B=B7=E6=99=B6?= <2749332490@qq.com> Date: Mon, 17 Mar 2025 23:16:51 +0800 Subject: [PATCH 2/3] fix: add certifi to requirement --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index a5fb776a..a867e055 100644 --- a/requirements.txt +++ b/requirements.txt @@ -32,6 +32,7 @@ gewechat-client dingtalk_stream dashscope python-telegram-bot +certifi # indirect taskgroup==0.0.0a4 \ No newline at end of file From 572182180cee6a8e1028aeed6f890234d9135887 Mon Sep 17 00:00:00 2001 From: Junyan Qin Date: Mon, 17 Mar 2025 23:53:29 +0800 Subject: [PATCH 3/3] deps: add `certifi` --- pkg/core/bootutils/deps.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/core/bootutils/deps.py b/pkg/core/bootutils/deps.py index 200b510d..805b50f7 100644 --- a/pkg/core/bootutils/deps.py +++ b/pkg/core/bootutils/deps.py @@ -33,6 +33,7 @@ required_deps = { "dingtalk_stream": "dingtalk_stream", "dashscope": "dashscope", "telegram": "python-telegram-bot", + "certifi": "certifi", }