Skip to content

fix: prefer bundled dashboard over stale data dist#8172

Merged
RC-CHN merged 2 commits into
AstrBotDevs:masterfrom
he-yufeng:fix/dashboard-stale-dist
May 14, 2026
Merged

fix: prefer bundled dashboard over stale data dist#8172
RC-CHN merged 2 commits into
AstrBotDevs:masterfrom
he-yufeng:fix/dashboard-stale-dist

Conversation

@he-yufeng
Copy link
Copy Markdown
Contributor

@he-yufeng he-yufeng commented May 13, 2026

Fixes #8170.

Summary

  • Detect stale data/dist dashboard assets by comparing their version with the current core version.
  • Prefer the bundled dashboard dist when data/dist is older, instead of serving stale login UI after an upgrade.
  • Keep explicit --webui-dir unchanged.

To verify

  • uv run pytest tests\test_main.py tests\test_dashboard.py::test_dashboard_uses_bundled_dist_when_data_dist_is_stale -q --basetemp .tmp\pytest -p no:cacheprovider
  • uv run ruff check main.py astrbot\core\utils\io.py astrbot\dashboard\server.py tests\test_main.py tests\test_dashboard.py
  • uv run ruff format --check main.py astrbot\core\utils\io.py astrbot\dashboard\server.py tests\test_main.py tests\test_dashboard.py
  • uv run python -m py_compile main.py astrbot\core\utils\io.py astrbot\dashboard\server.py tests\test_main.py tests\test_dashboard.py
  • git diff --check

Summary by Sourcery

Ensure the dashboard uses bundled assets when user data dist is stale relative to the core version.

Bug Fixes:

  • Prevent serving outdated WebUI assets by preferring the bundled dashboard dist when the user data dist version is older than the core version.

Enhancements:

  • Introduce utilities to detect dashboard dist versions and decide when to fall back to bundled assets.
  • Update dashboard server initialization to respect version comparisons while still honoring explicit webui_dir configuration.

Tests:

  • Add unit and integration tests verifying that stale data/dist does not override bundled dashboard assets and that the bundled dist path is used appropriately.

@auto-assign auto-assign Bot requested review from LIghtJUNction and Raven95676 May 13, 2026 07:07
@dosubot dosubot Bot added size:M This PR changes 30-99 lines, ignoring generated files. area:webui The bug / feature is about webui(dashboard) of astrbot. labels May 13, 2026
Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 2 issues, and left some high level feedback:

  • The logic for selecting between data/dist and the bundled dashboard dist is now duplicated between check_dashboard_files in main.py and AstrBotDashboard.__init__; consider centralizing this selection in a single helper to keep behavior consistent over time.
  • In AstrBotDashboard.__init__, when should_use_bundled_dashboard_dist causes a fallback to the bundled dist, you only log in the elif bundled_dist.exists() branch; consider adding a log message that explicitly notes when a stale data/dist is being ignored to make version-related behavior easier to trace.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The logic for selecting between `data/dist` and the bundled dashboard dist is now duplicated between `check_dashboard_files` in `main.py` and `AstrBotDashboard.__init__`; consider centralizing this selection in a single helper to keep behavior consistent over time.
- In `AstrBotDashboard.__init__`, when `should_use_bundled_dashboard_dist` causes a fallback to the bundled dist, you only log in the `elif bundled_dist.exists()` branch; consider adding a log message that explicitly notes when a stale `data/dist` is being ignored to make version-related behavior easier to trace.

## Individual Comments

### Comment 1
<location path="astrbot/core/utils/io.py" line_range="260-267" />
<code_context>
+    return Path(get_astrbot_path()) / "astrbot" / "dashboard" / "dist"
+
+
+def should_use_bundled_dashboard_dist(
+    user_dist: str | Path, current_version: str
+) -> bool:
+    user_version = _read_dashboard_dist_version(user_dist)
+    bundled_dist = get_bundled_dashboard_dist_path()
+    if user_version is None or not bundled_dist.exists():
+        return False
+    return VersionComparator.compare_version(current_version, user_version) > 0
+
+
</code_context>
<issue_to_address>
**issue (bug_risk):** Handle malformed or prefixed version strings more defensively in VersionComparator usage.

`user_version` may include a leading `v` (e.g., `v1.2.3`) or otherwise differ from the clean semantic version that `VersionComparator` expects. This can lead to incorrect comparisons or parsing errors, potentially breaking dashboard startup.

Normalize both versions before comparison (e.g., strip a leading `v`) and wrap the compare call in a `try/except` for `ValueError`/`TypeError`, defaulting to `False` (keep the user dist) on failure to make this more robust to malformed version files.
</issue_to_address>

### Comment 2
<location path="tests/test_main.py" line_range="205-214" />
<code_context>
+    mock_download.assert_not_called()
+
+
 @pytest.mark.asyncio
 async def test_check_dashboard_files_with_webui_dir_arg(monkeypatch):
     """Tests that providing a valid webui_dir skips all checks."""
</code_context>
<issue_to_address>
**issue (testing):** Use an AsyncMock (or async stub) when patching `get_dashboard_version`, since it is awaited in `check_dashboard_files`.

Since `get_dashboard_version()` is awaited in `check_dashboard_files`, the patched object here must be awaitable. A regular `Mock` with `return_value="v0.0.1"` will raise when awaited. Please use an `AsyncMock` (e.g. `mock.AsyncMock(return_value="v0.0.1")`) or an `async def` stub so the test accurately reflects the async behavior and remains stable.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread astrbot/core/utils/io.py Outdated
Comment thread tests/test_main.py
Comment on lines +205 to +214
@pytest.mark.asyncio
async def test_check_dashboard_files_uses_bundled_dist_when_data_dist_is_stale(
tmp_path,
):
"""Tests that a stale data/dist does not override bundled dashboard assets."""
data_dir = tmp_path / "data"
data_dist = data_dir / "dist"
bundled_dist = tmp_path / "bundled-dist"
data_dist.mkdir(parents=True)
bundled_dist.mkdir()
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.

issue (testing): Use an AsyncMock (or async stub) when patching get_dashboard_version, since it is awaited in check_dashboard_files.

Since get_dashboard_version() is awaited in check_dashboard_files, the patched object here must be awaitable. A regular Mock with return_value="v0.0.1" will raise when awaited. Please use an AsyncMock (e.g. mock.AsyncMock(return_value="v0.0.1")) or an async def stub so the test accurately reflects the async behavior and remains stable.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request implements a version-based fallback mechanism for the dashboard WebUI, ensuring that bundled assets are used if the user-provided assets are outdated. This is achieved through new version comparison utilities and updates to the server and main startup logic. Review feedback suggests improving the robustness of the version check when metadata is missing and streamlining the version retrieval logic to avoid redundant I/O.

Comment thread astrbot/core/utils/io.py Outdated
Comment on lines +263 to +267
user_version = _read_dashboard_dist_version(user_dist)
bundled_dist = get_bundled_dashboard_dist_path()
if user_version is None or not bundled_dist.exists():
return False
return VersionComparator.compare_version(current_version, user_version) > 0
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

The current implementation returns False if the user dashboard version cannot be read (user_version is None). This results in the application preferring a potentially broken or unversioned dashboard in data/dist over a valid bundled one, which contradicts the goal of avoiding stale or broken UI after an upgrade.

It is safer to prefer the bundled dashboard if the user-installed one is missing its version file, provided the bundled one exists. Additionally, checking for the existence of the bundled distribution before reading the user version avoids unnecessary file I/O when no fallback is available.

Suggested change
user_version = _read_dashboard_dist_version(user_dist)
bundled_dist = get_bundled_dashboard_dist_path()
if user_version is None or not bundled_dist.exists():
return False
return VersionComparator.compare_version(current_version, user_version) > 0
bundled_dist = get_bundled_dashboard_dist_path()
if not bundled_dist.exists():
return False
user_version = _read_dashboard_dist_version(user_dist)
if user_version is None:
return True
return VersionComparator.compare_version(current_version, user_version) > 0

Comment thread astrbot/core/utils/io.py
Comment on lines +276 to 287
if should_use_bundled_dashboard_dist(dist_dir, VERSION):
bundled_version = _read_dashboard_dist_version(
get_bundled_dashboard_dist_path()
)
if bundled_version is not None:
return bundled_version
return _read_dashboard_dist_version(dist_dir)

bundled = get_bundled_dashboard_dist_path()
if bundled.exists():
return _read_dashboard_dist_version(bundled)
return None
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

The logic in get_dashboard_version can be simplified and made more efficient. The current implementation makes redundant calls to _read_dashboard_dist_version and get_bundled_dashboard_dist_path. Since should_use_bundled_dashboard_dist already performs the necessary checks, the return paths can be streamlined to avoid re-reading the same files.

Suggested change
if should_use_bundled_dashboard_dist(dist_dir, VERSION):
bundled_version = _read_dashboard_dist_version(
get_bundled_dashboard_dist_path()
)
if bundled_version is not None:
return bundled_version
return _read_dashboard_dist_version(dist_dir)
bundled = get_bundled_dashboard_dist_path()
if bundled.exists():
return _read_dashboard_dist_version(bundled)
return None
if should_use_bundled_dashboard_dist(dist_dir, VERSION):
return _read_dashboard_dist_version(get_bundled_dashboard_dist_path())
return _read_dashboard_dist_version(dist_dir)
bundled = get_bundled_dashboard_dist_path()
if bundled.exists():
return _read_dashboard_dist_version(bundled)
return None

@he-yufeng
Copy link
Copy Markdown
Contributor Author

Addressed the review feedback in 8f3389c: dashboard dist version comparison now normalizes a leading v, rejects malformed version files conservatively, and the check_dashboard_files tests now patch the awaited get_dashboard_version with AsyncMock. Validation passed: focused tests for the stale/malformed dashboard dist cases, py_compile on astrbot/core/utils/io.py and tests/test_main.py, and ruff check on the touched files.

@dosubot dosubot Bot added the lgtm This PR has been approved by a maintainer label May 14, 2026
@RC-CHN RC-CHN merged commit 3290d75 into AstrBotDevs:master May 14, 2026
21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:webui The bug / feature is about webui(dashboard) of astrbot. lgtm This PR has been approved by a maintainer size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]重大bug,更新4.24.4后无法进入系统,疑似密码系统出bug了

2 participants