fix: warn when an explicitly configured WebUI dist is stale (#9518)

* fix: warn when an explicitly configured WebUI dist is stale

resolve_dashboard_dist() returns an explicitly configured --webui-dir
immediately, without checking the version it declares. Every other branch in
the same function verifies compatibility and warns on a mismatch, so this is
the one way to end up serving assets that do not match the running core with
no diagnostic at all.

That is the case a packaged distribution hits: when the launcher updates the
backend but keeps its bundled WebUI directory, the dashboard silently stays on
the previous release. Features added by the new core are simply missing from
the UI and nothing explains why.

Behaviour is unchanged -- the directory is still served, since refusing to
serve it would be worse than serving an old one. It is just no longer silent.

* refactor: tidy the explicit WebUI dist resolution

Build the Path once instead of twice, and hold the version read for the
warning in a local so the log call only carries values.
This commit is contained in:
KazusaUwU
2026-08-08 09:28:07 +08:00
committed by GitHub
parent f534c4fd5f
commit 516c22ede9
2 changed files with 84 additions and 2 deletions
+14 -2
View File
@@ -141,8 +141,20 @@ def resolve_dashboard_dist(webui_dir: str | Path | None = None) -> Path | None:
Explicit, managed, bundled, or stale fallback dist in priority order;
None when an existing managed dist is incomplete.
"""
if webui_dir and Path(webui_dir).exists():
return Path(webui_dir).absolute()
explicit_dist = Path(webui_dir).absolute() if webui_dir else None
if explicit_dist is not None and explicit_dist.exists():
if not _is_dist_compatible(explicit_dist, VERSION):
explicit_version = _read_dashboard_version(explicit_dist) or "unknown"
logger.warning(
"Serving the explicitly configured WebUI directory even though it "
"does not declare a version matching core: %s, expected v%s (%s). "
"Some dashboard features may not work until matching assets are "
"available.",
explicit_version,
VERSION,
explicit_dist,
)
return explicit_dist
user_dist = Path(get_astrbot_data_path()) / "dist"
bundled_dist = _get_bundled_dist_path()
@@ -0,0 +1,70 @@
"""Tests for resolve_dashboard_dist() when an explicit WebUI directory is used."""
import logging
import pytest
from astrbot.core.config.default import VERSION
from astrbot.core.dashboard_assets import resolve_dashboard_dist
WARNING_FRAGMENT = "does not declare a version matching core"
def _make_dist(root, version: str | None) -> str:
assets = root / "assets"
assets.mkdir(parents=True)
(root / "index.html").write_text("<html></html>", encoding="utf-8")
if version is not None:
(assets / "version").write_text(version, encoding="utf-8")
return str(root)
class TestExplicitWebuiDir:
def test_matching_version_is_served_quietly(self, tmp_path, caplog):
"""The happy path must not add startup noise."""
dist = _make_dist(tmp_path / "webui", f"v{VERSION}")
with caplog.at_level(logging.WARNING):
resolved = resolve_dashboard_dist(dist)
assert resolved is not None
assert str(resolved) == str(tmp_path / "webui")
assert WARNING_FRAGMENT not in caplog.text
def test_mismatched_version_warns_but_is_still_served(self, tmp_path, caplog):
"""A stale packaged WebUI must not be swapped in silently."""
dist = _make_dist(tmp_path / "webui", "v0.0.1")
with caplog.at_level(logging.WARNING):
resolved = resolve_dashboard_dist(dist)
assert resolved is not None # behaviour unchanged: still served
assert WARNING_FRAGMENT in caplog.text
assert "v0.0.1" in caplog.text
assert VERSION in caplog.text
def test_missing_version_marker_warns_as_unknown(self, tmp_path, caplog):
"""Assets without a version marker cannot be verified, so say so."""
dist = _make_dist(tmp_path / "webui", None)
with caplog.at_level(logging.WARNING):
resolved = resolve_dashboard_dist(dist)
assert resolved is not None
assert WARNING_FRAGMENT in caplog.text
assert "unknown" in caplog.text
def test_nonexistent_dir_falls_through(self, tmp_path, caplog):
"""A path that does not exist must not be reported as a stale dist."""
with caplog.at_level(logging.WARNING):
resolve_dashboard_dist(str(tmp_path / "does-not-exist"))
assert WARNING_FRAGMENT not in caplog.text
@pytest.mark.parametrize("empty", ["", None])
def test_no_explicit_dir_falls_through(self, empty, caplog):
"""Without --webui-dir the managed/bundled resolution path is used."""
with caplog.at_level(logging.WARNING):
resolve_dashboard_dist(empty)
assert WARNING_FRAGMENT not in caplog.text