Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🔨 提取GitHub相关操作 #1609

Merged
merged 2 commits into from
Sep 8, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions tests/builtin_plugins/auto_update/test_check_update.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,20 @@ def init_mocked_api(mocked_api: MockRouter) -> None:
url="https://api.github.com/repos/HibiKier/zhenxun_bot/releases/latest",
name="release_latest",
).respond(json=get_response_json("release_latest.json"))

mocked_api.head(
url="https://raw.githubusercontent.com/",
name="head_raw",
).respond(text="")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

建议(测试): 为与GitHub相关的URL添加新的模拟响应

这些新的模拟响应针对raw.githubusercontent.com、github.com和codeload.github.com,改进了与GitHub相关操作的测试覆盖率。考虑添加断言以验证在测试期间这些端点是否按预期被调用。

Original comment in English

suggestion (testing): New mock responses added for GitHub-related URLs

These new mock responses for raw.githubusercontent.com, github.com, and codeload.github.com improve the test coverage for GitHub-related operations. Consider adding assertions to verify that these endpoints are called as expected during the tests.

mocked_api.head(
url="https://github.com/",
name="head_github",
).respond(text="")
mocked_api.head(
url="https://codeload.github.com/",
name="head_codeload",
).respond(text="")

mocked_api.get(
url="https://raw.githubusercontent.com/HibiKier/zhenxun_bot/dev/__version__",
name="dev_branch_version",
Expand Down Expand Up @@ -75,13 +89,13 @@ def init_mocked_api(mocked_api: MockRouter) -> None:
content=tar_buffer.getvalue(),
)
mocked_api.get(
url="https://ghproxy.cc/https://github.com/HibiKier/zhenxun_bot/archive/refs/heads/dev.zip",
url="https://github.com/HibiKier/zhenxun_bot/archive/refs/heads/dev.zip",
name="dev_download_url",
).respond(
content=zip_bytes.getvalue(),
)
mocked_api.get(
url="https://ghproxy.cc/https://github.com/HibiKier/zhenxun_bot/archive/refs/heads/main.zip",
url="https://github.com/HibiKier/zhenxun_bot/archive/refs/heads/main.zip",
name="main_download_url",
).respond(
content=zip_bytes.getvalue(),
Expand Down Expand Up @@ -306,7 +320,6 @@ async def test_check_update_release(
)
ctx.should_finished(_matcher)
assert mocked_api["release_latest"].called
assert mocked_api["release_download_url"].called
assert mocked_api["release_download_url_redirect"].called

assert (mock_backup_path / PYPROJECT_FILE_STRING).exists()
Expand Down
29 changes: 13 additions & 16 deletions zhenxun/builtin_plugins/auto_update/_data_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
from zhenxun.services.log import logger
from zhenxun.utils.http_utils import AsyncHttpx
from zhenxun.utils.platform import PlatformUtils
from zhenxun.utils.github_utils.models import RepoInfo
from zhenxun.utils.github_utils import parse_github_url

from .config import (
DEV_URL,
MAIN_URL,
TMP_PATH,
BASE_PATH,
BACKUP_PATH,
Expand All @@ -25,6 +25,7 @@
BASE_PATH_STRING,
DOWNLOAD_GZ_FILE,
DOWNLOAD_ZIP_FILE,
DEFAULT_GITHUB_URL,
PYPROJECT_LOCK_FILE,
REQ_TXT_FILE_STRING,
PYPROJECT_FILE_STRING,
Expand Down Expand Up @@ -169,23 +170,19 @@ async def update(cls, bot: Bot, user_id: str, version_type: str) -> str | None:
cur_version = cls.__get_version()
url = None
new_version = None
if version_type == "dev":
url = DEV_URL
new_version = await cls.__get_version_from_branch("dev")
if new_version:
new_version = new_version.split(":")[-1].strip()
elif version_type == "main":
url = MAIN_URL
new_version = await cls.__get_version_from_branch("main")
repo_info = parse_github_url(DEFAULT_GITHUB_URL)
if version_type in {"dev", "main"}:
repo_info.branch = version_type
new_version = await cls.__get_version_from_repo(repo_info)
if new_version:
new_version = new_version.split(":")[-1].strip()
url = await repo_info.get_archive_download_url()
elif version_type == "release":
data = await cls.__get_latest_data()
if not data:
return "获取更新版本失败..."
url = data.get("tarball_url")
new_version = data.get("name")
url = (await AsyncHttpx.get(url)).headers.get("Location") # type: ignore
new_version = data.get("name", "")
url = await repo_info.get_release_source_download_url_tgz(new_version)
if not url:
return "获取版本下载链接失败..."
if TMP_PATH.exists():
Expand Down Expand Up @@ -247,7 +244,7 @@ async def __get_latest_data(cls) -> dict:
return {}

@classmethod
async def __get_version_from_branch(cls, branch: str) -> str:
async def __get_version_from_repo(cls, repo_info: RepoInfo) -> str:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

建议: 改进__get_version_from_repo方法中的错误处理

考虑重试请求或以不同方式处理特定类型的异常。这可以使更新过程更加稳健。例如,您可能希望在网络错误时重试,但在身份验证错误时不重试。

    async def __get_version_from_repo(cls, repo_info: RepoInfo) -> str:
        max_retries = 3
        retry_delay = 1

        for attempt in range(max_retries):
            try:
                return await cls.__fetch_version(repo_info)
            except aiohttp.ClientError as e:
                if attempt == max_retries - 1:
                    raise
                await asyncio.sleep(retry_delay)
Original comment in English

suggestion: Improve error handling in __get_version_from_repo method

Consider retrying the request or handling specific types of exceptions differently. This could make the update process more robust. For example, you might want to retry on network errors but not on authentication errors.

    async def __get_version_from_repo(cls, repo_info: RepoInfo) -> str:
        max_retries = 3
        retry_delay = 1

        for attempt in range(max_retries):
            try:
                return await cls.__fetch_version(repo_info)
            except aiohttp.ClientError as e:
                if attempt == max_retries - 1:
                    raise
                await asyncio.sleep(retry_delay)

"""从指定分支获取版本号

参数:
Expand All @@ -256,11 +253,11 @@ async def __get_version_from_branch(cls, branch: str) -> str:
返回:
str: 版本号
"""
version_url = f"https://raw.githubusercontent.com/HibiKier/zhenxun_bot/{branch}/__version__"
version_url = await repo_info.get_raw_download_url(path="__version__")
try:
res = await AsyncHttpx.get(version_url)
if res.status_code == 200:
return res.text.strip()
except Exception as e:
logger.error(f"获取 {branch} 分支版本失败", e=e)
logger.error(f"获取 {repo_info.branch} 分支版本失败", e=e)
return "未知版本"
9 changes: 5 additions & 4 deletions zhenxun/builtin_plugins/auto_update/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@

from zhenxun.configs.path_config import TEMP_PATH

DEV_URL = "https://ghproxy.cc/https://github.com/HibiKier/zhenxun_bot/archive/refs/heads/dev.zip"
MAIN_URL = "https://ghproxy.cc/https://github.com/HibiKier/zhenxun_bot/archive/refs/heads/main.zip"
DEFAULT_GITHUB_URL = "https://github.com/HibiKier/zhenxun_bot/tree/main"
RELEASE_URL = "https://api.github.com/repos/HibiKier/zhenxun_bot/releases/latest"

VERSION_FILE_STRING = "__version__"
Expand All @@ -23,8 +22,10 @@

BACKUP_PATH = Path() / "backup"

DOWNLOAD_GZ_FILE = TMP_PATH / "download_latest_file.tar.gz"
DOWNLOAD_ZIP_FILE = TMP_PATH / "download_latest_file.zip"
DOWNLOAD_GZ_FILE_STRING = "download_latest_file.tar.gz"
DOWNLOAD_ZIP_FILE_STRING = "download_latest_file.zip"
DOWNLOAD_GZ_FILE = TMP_PATH / DOWNLOAD_GZ_FILE_STRING
DOWNLOAD_ZIP_FILE = TMP_PATH / DOWNLOAD_ZIP_FILE_STRING

REPLACE_FOLDERS = [
"builtin_plugins",
Expand Down
19 changes: 0 additions & 19 deletions zhenxun/builtin_plugins/plugin_store/config.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import re
from pathlib import Path

BASE_PATH = Path() / "zhenxun"
Expand All @@ -10,21 +9,3 @@

EXTRA_GITHUB_URL = "https://github.com/zhenxun-org/zhenxun_bot_plugins_index/tree/index"
"""插件库索引github仓库地址"""

GITHUB_REPO_URL_PATTERN = re.compile(
r"^https://github.com/(?P<owner>[^/]+)/(?P<repo>[^/]+)(/tree/(?P<branch>[^/]+))?$"
)
"""github仓库地址正则"""

JSD_PACKAGE_API_FORMAT = (
"https://data.jsdelivr.com/v1/packages/gh/{owner}/{repo}@{branch}"
)
"""jsdelivr包地址格式"""

GIT_API_TREES_FORMAT = (
"https://api.github.com/repos/{owner}/{repo}/git/trees/{branch}?recursive=1"
)
"""git api trees地址格式"""

CACHED_API_TTL = 300
"""缓存api ttl"""
25 changes: 10 additions & 15 deletions zhenxun/builtin_plugins/plugin_store/data_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,11 @@
from zhenxun.services.log import logger
from zhenxun.utils.http_utils import AsyncHttpx
from zhenxun.models.plugin_info import PluginInfo
from zhenxun.utils.github_utils import parse_github_url
from zhenxun.utils.github_utils.models import BaseAPI, PackageApi
from zhenxun.builtin_plugins.plugin_store.models import StorePluginInfo
from zhenxun.utils.image_utils import RowStyle, BuildImage, ImageTemplate
from zhenxun.builtin_plugins.auto_update.config import REQ_TXT_FILE_STRING
from zhenxun.builtin_plugins.plugin_store.models import (
BaseAPI,
RepoInfo,
PackageApi,
StorePluginInfo,
)

from .config import BASE_PATH, EXTRA_GITHUB_URL, DEFAULT_GITHUB_URL

Expand Down Expand Up @@ -81,12 +78,12 @@ async def __get_data(cls) -> dict[str, StorePluginInfo]:
返回:
dict: 插件信息数据
"""
default_github_url = await RepoInfo.parse_github_url(
default_github_url = await parse_github_url(
DEFAULT_GITHUB_URL
).get_download_url_with_path("plugins.json")
extra_github_url = await RepoInfo.parse_github_url(
).get_raw_download_url("plugins.json")
extra_github_url = await parse_github_url(
EXTRA_GITHUB_URL
).get_download_url_with_path("plugins.json")
).get_raw_download_url("plugins.json")
res = await AsyncHttpx.get(default_github_url)
res2 = await AsyncHttpx.get(extra_github_url)

Expand Down Expand Up @@ -214,7 +211,7 @@ async def install_plugin_with_repo(
package_api: PackageApi
files: list[str]
package_info: BaseAPI
repo_info = RepoInfo.parse_github_url(github_url)
repo_info = parse_github_url(github_url)
logger.debug(f"成功获取仓库信息: {repo_info}", "插件管理")
for package_api in PackageApi:
try:
Expand All @@ -231,9 +228,7 @@ async def install_plugin_with_repo(
module_path=module_path.replace(".", "/") + ("" if is_dir else ".py"),
is_dir=is_dir,
)
download_urls = [
await repo_info.get_download_url_with_path(file) for file in files
]
download_urls = [await repo_info.get_raw_download_url(file) for file in files]
base_path = BASE_PATH / "plugins" if is_external else BASE_PATH
download_paths: list[Path | str] = [base_path / file for file in files]
logger.debug(f"插件下载路径: {download_paths}", "插件管理")
Expand All @@ -248,7 +243,7 @@ async def install_plugin_with_repo(
req_files.extend(package_info.get_files("requirement.txt", False))
logger.debug(f"获取插件依赖文件列表: {req_files}", "插件管理")
req_download_urls = [
await repo_info.get_download_url_with_path(file) for file in req_files
await repo_info.get_raw_download_url(file) for file in req_files
]
req_paths: list[Path | str] = [plugin_path / file for file in req_files]
logger.debug(f"插件依赖文件下载路径: {req_paths}", "插件管理")
Expand Down
Loading
Loading