diff --git a/astrbot/core/zip_updator.py b/astrbot/core/zip_updator.py index 44335a56af..e70173fa56 100644 --- a/astrbot/core/zip_updator.py +++ b/astrbot/core/zip_updator.py @@ -234,6 +234,16 @@ def compare_version(self, v1: str, v2: str) -> int: """Semver 版本比较""" return VersionComparator.compare_version(v1, v2) + def _is_prerelease_version(self, version: str) -> bool: + """Check if a version string is a prerelease version.""" + return bool( + re.search( + r"[\-_.]?(alpha|beta|rc|dev)[\-_.]?\d*$", + version, + re.IGNORECASE, + ) + ) + async def check_update( self, url: str, @@ -241,6 +251,7 @@ async def check_update( consider_prerelease: bool = True, ) -> ReleaseInfo | None: update_data = await self.fetch_release_info(url) + current_is_prerelease = self._is_prerelease_version(current_version) sel_release_data = None if consider_prerelease: @@ -249,11 +260,7 @@ async def check_update( else: for data in update_data: # 跳过带有 alpha、beta 等预发布标签的版本 - if re.search( - r"[\-_.]?(alpha|beta|rc|dev)[\-_.]?\d*$", - data["tag_name"], - re.IGNORECASE, - ): + if self._is_prerelease_version(data["tag_name"]): continue tag_name = data["tag_name"] sel_release_data = data @@ -263,6 +270,10 @@ async def check_update( logger.error("未找到合适的发布版本") return None + if current_is_prerelease and not self._is_prerelease_version(tag_name): + if self.compare_version(current_version, tag_name) >= 0: + return None + if self.compare_version(current_version, tag_name) >= 0: return None return ReleaseInfo( diff --git a/tests/test_updator_socks.py b/tests/test_updator_socks.py index 4cb30430a9..59bc031280 100644 --- a/tests/test_updator_socks.py +++ b/tests/test_updator_socks.py @@ -418,6 +418,41 @@ async def fake_download_from_repo_url( assert marker_path.read_text(encoding="utf-8") == "VALUE = 'old'\n" +@pytest.mark.asyncio +async def test_check_update_skips_stable_when_current_prerelease_is_newer( + monkeypatch: pytest.MonkeyPatch, +) -> None: + updator = RepoZipUpdator() + + async def fake_fetch_release_info(url: str, latest: bool = True): # noqa: ARG001 + return [ + { + "version": "AstrBot v4.26.0-beta.1", + "published_at": "2026-06-20T00:00:00Z", + "body": "beta", + "tag_name": "v4.26.0-beta.1", + "zipball_url": "https://github.example/beta.zip", + }, + { + "version": "AstrBot v4.25.6", + "published_at": "2026-06-19T00:00:00Z", + "body": "stable", + "tag_name": "v4.25.6", + "zipball_url": "https://github.example/stable.zip", + }, + ] + + monkeypatch.setattr(updator, "fetch_release_info", fake_fetch_release_info) + + result = await updator.check_update( + "https://example.invalid/releases", + current_version="v4.26.0-dev", + consider_prerelease=False, + ) + + assert result is None + + @pytest.mark.asyncio async def test_astrbot_updator_prefers_hosted_core_package( monkeypatch: pytest.MonkeyPatch,