Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
21 changes: 16 additions & 5 deletions astrbot/core/zip_updator.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,13 +234,24 @@ 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,
current_version: str,
consider_prerelease: bool = True,
) -> ReleaseInfo | None:
update_data = await self.fetch_release_info(url)
current_is_prerelease = self._is_prerelease_version(current_version)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

If the redundant block on lines 273-275 is removed, the current_is_prerelease variable becomes unused. Removing it prevents potential linter warnings (such as Ruff's F841).


sel_release_data = None
if consider_prerelease:
Expand All @@ -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
Expand All @@ -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:
Comment on lines +273 to +274

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

question: The prerelease-specific guard appears redundant with the general version comparison below.

If VersionComparator uses semver semantics, a prerelease like 1.0.0-beta already compares as lower than 1.0.0, so this branch doesn’t change the result of the final compare_version(...) >= 0 check. If you’re trying to support a different behavior (e.g. preferring a newer prerelease over the latest stable), it may be clearer to encode that explicitly in the comparison logic or in a separate branch that actually differs from the final condition, rather than duplicating it here.

return None

Comment on lines +273 to +276

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

This conditional block is completely redundant and can be safely removed.

Why is it redundant?

  1. Identical Inner Condition: The inner condition self.compare_version(current_version, tag_name) >= 0 is exactly the same as the subsequent check on line 277.
  2. Logic Flow:
    • If self.compare_version(current_version, tag_name) >= 0 is True, the function will return None anyway (either here or on line 278).
    • If it is False, neither block will trigger, and it won't return None.
    • Therefore, the outer check current_is_prerelease and not self._is_prerelease_version(tag_name) has no effect on the outcome.

Version Comparison Behavior

In VersionComparator.compare_version, the numeric parts are compared first. For example, with current_version = "v4.26.0-dev" and tag_name = "v4.25.6", the numeric parts are [4, 26, 0] and [4, 25, 6]. Since 26 > 25, it immediately returns 1 (greater than) without comparing prerelease tags. Thus, the standard check on line 277 already correctly returns None and skips the update.

Removing this block simplifies the logic and improves maintainability. Please also remove the unused current_is_prerelease variable definition on line 254 to avoid linter warnings.

if self.compare_version(current_version, tag_name) >= 0:
return None
return ReleaseInfo(
Expand Down
35 changes: 35 additions & 0 deletions tests/test_updator_socks.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Loading