Skip to content

Commit 6de68a5

Browse files
committed
fix: guard empty updater archive roots
1 parent 909feea commit 6de68a5

3 files changed

Lines changed: 82 additions & 26 deletions

File tree

astrbot/core/star/updator.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from astrbot.core import logger
66
from astrbot.core.utils.astrbot_path import get_astrbot_plugin_path
77
from astrbot.core.utils.io import ensure_dir, on_error, remove_dir
8+
from astrbot.core.zip_updator import normalize_archive_root_dir
89

910
from ..star.star import StarMetadata
1011
from ..updator import RepoZipUpdator
@@ -75,9 +76,18 @@ def unzip_file(self, zip_path: str, target_dir: str) -> None:
7576
update_dir = ""
7677
logger.info(f"Extracting archive: {zip_path}")
7778
with zipfile.ZipFile(zip_path, "r") as z:
78-
update_dir = self._normalize_archive_root_dir(z.namelist()[0])
79+
update_dir = normalize_archive_root_dir(z.namelist()[0])
7980
z.extractall(target_dir)
8081

82+
if not update_dir:
83+
try:
84+
os.remove(zip_path)
85+
except BaseException:
86+
logger.warning(
87+
f"Failed to remove update files; you can manually delete {zip_path}",
88+
)
89+
return
90+
8191
update_root_path = os.path.normpath(os.path.join(target_dir, update_dir))
8292
files = os.listdir(update_root_path)
8393
for f in files:

astrbot/core/zip_updator.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@
1313
from astrbot.core.utils.version_comparator import VersionComparator
1414

1515

16+
def normalize_archive_root_dir(path: str) -> str:
17+
normalized = os.path.normpath(path)
18+
return "" if normalized == "." else normalized
19+
20+
1621
class ReleaseInfo:
1722
version: str
1823
published_at: str
@@ -231,20 +236,22 @@ def parse_github_url(self, url: str):
231236
return author, repo, branch
232237
raise ValueError("无效的 GitHub URL")
233238

234-
@staticmethod
235-
def _normalize_archive_root_dir(path: str) -> str:
236-
normalized = os.path.normpath(path)
237-
return "" if normalized == "." else normalized
238-
239239
def unzip_file(self, zip_path: str, target_dir: str) -> None:
240240
"""解压缩文件, 并将压缩包内**第一个**文件夹内的文件移动到 target_dir"""
241241
ensure_dir(target_dir)
242242
update_dir = ""
243243
with zipfile.ZipFile(zip_path, "r") as z:
244-
update_dir = self._normalize_archive_root_dir(z.namelist()[0])
244+
update_dir = normalize_archive_root_dir(z.namelist()[0])
245245
z.extractall(target_dir)
246246
logger.debug(f"解压文件完成: {zip_path}")
247247

248+
if not update_dir:
249+
try:
250+
os.remove(zip_path)
251+
except BaseException:
252+
logger.warning(f"删除更新文件失败,可以手动删除 {zip_path}")
253+
return
254+
248255
update_root_path = os.path.normpath(os.path.join(target_dir, update_dir))
249256
files = os.listdir(update_root_path)
250257
for f in files:

tests/test_updator_socks.py

Lines changed: 58 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -440,15 +440,23 @@ def test_repo_unzip_file_normalizes_windows_extended_length_paths(
440440
) -> None:
441441
import astrbot.core.zip_updator as zip_updator_module
442442

443-
target_dir = r"\\?\\C:\\Users\\admin\\AppData\\Local\\AstrBot\\backend\\app"
444-
expected_root = ntpath.normpath(ntpath.join(target_dir, archive_root))
445-
expected_file = ntpath.normpath(
446-
ntpath.join(target_dir, archive_root, ".dockerignore")
443+
target_dir = r"\\?\C:\Users\admin\AppData\Local\AstrBot\backend\app"
444+
normalized_root = ntpath.normpath(archive_root)
445+
expected_root = (
446+
target_dir
447+
if normalized_root == "."
448+
else ntpath.join(target_dir, normalized_root)
447449
)
448-
captured: dict[str, object] = {}
450+
expected_file = ntpath.join(expected_root, ".dockerignore")
451+
captured: dict[str, object | None] = {
452+
"listdir": None,
453+
"move": None,
454+
"cleanup": None,
455+
"removed": None,
456+
}
449457

450458
def fake_listdir(path: str) -> list[str]:
451-
captured.setdefault("listdir", path)
459+
captured["listdir"] = path
452460
return [".dockerignore"]
453461

454462
monkeypatch.setattr(
@@ -469,41 +477,65 @@ def fake_listdir(path: str) -> list[str]:
469477
monkeypatch.setattr(
470478
zip_updator_module.shutil,
471479
"move",
472-
lambda src, dst: captured.setdefault("move", (src, dst)),
480+
lambda src, dst: captured.__setitem__("move", (src, dst)),
473481
)
474482
monkeypatch.setattr(
475483
zip_updator_module.shutil,
476484
"rmtree",
477-
lambda path, onerror=None: captured.setdefault("cleanup", path),
485+
lambda path, onerror=None: captured.__setitem__("cleanup", path),
478486
)
479487
monkeypatch.setattr(
480488
zip_updator_module.os,
481489
"remove",
482-
lambda path: captured.setdefault("removed", path),
490+
lambda path: captured.__setitem__("removed", path),
483491
)
484492

485493
RepoZipUpdator().unzip_file("temp.zip", target_dir)
486494

495+
assert captured["removed"] == "temp.zip"
496+
if normalized_root == ".":
497+
assert captured["listdir"] is None
498+
assert captured["move"] is None
499+
assert captured["cleanup"] is None
500+
return
501+
487502
assert captured["listdir"] == expected_root
488503
assert captured["move"] == (expected_file, target_dir)
489504
assert captured["cleanup"] == expected_root
490505

491506

507+
@pytest.mark.parametrize(
508+
"archive_root",
509+
[
510+
"AstrBotDevs-demo-39386ee/",
511+
"AstrBotDevs-demo-39386ee",
512+
"owner-repo-branch/subdir/",
513+
".",
514+
],
515+
)
492516
def test_plugin_unzip_file_normalizes_windows_extended_length_paths(
493517
monkeypatch: pytest.MonkeyPatch,
518+
archive_root: str,
494519
) -> None:
495520
import astrbot.core.star.updator as plugin_updator_module
496521

497-
target_dir = r"\\?\\C:\\Users\\admin\\AppData\\Local\\AstrBot\\data\\plugins\\demo"
498-
archive_root = "AstrBotDevs-demo-39386ee/"
499-
expected_root = ntpath.normpath(ntpath.join(target_dir, archive_root))
500-
expected_file = ntpath.normpath(
501-
ntpath.join(target_dir, archive_root, ".dockerignore")
522+
target_dir = r"\\?\C:\Users\admin\AppData\Local\AstrBot\data\plugins\demo"
523+
normalized_root = ntpath.normpath(archive_root)
524+
expected_root = (
525+
target_dir
526+
if normalized_root == "."
527+
else ntpath.join(target_dir, normalized_root)
502528
)
503-
captured: dict[str, object] = {}
529+
expected_file = ntpath.join(expected_root, ".dockerignore")
530+
captured: dict[str, object | None] = {
531+
"listdir": None,
532+
"move": None,
533+
"cleanup": None,
534+
"removed": None,
535+
}
504536

505537
def fake_listdir(path: str) -> list[str]:
506-
captured.setdefault("listdir", path)
538+
captured["listdir"] = path
507539
return [".dockerignore"]
508540

509541
monkeypatch.setattr(
@@ -524,21 +556,28 @@ def fake_listdir(path: str) -> list[str]:
524556
monkeypatch.setattr(
525557
plugin_updator_module.shutil,
526558
"move",
527-
lambda src, dst: captured.setdefault("move", (src, dst)),
559+
lambda src, dst: captured.__setitem__("move", (src, dst)),
528560
)
529561
monkeypatch.setattr(
530562
plugin_updator_module.shutil,
531563
"rmtree",
532-
lambda path, onerror=None: captured.setdefault("cleanup", path),
564+
lambda path, onerror=None: captured.__setitem__("cleanup", path),
533565
)
534566
monkeypatch.setattr(
535567
plugin_updator_module.os,
536568
"remove",
537-
lambda path: captured.setdefault("removed", path),
569+
lambda path: captured.__setitem__("removed", path),
538570
)
539571

540572
PluginUpdator.__new__(PluginUpdator).unzip_file("temp.zip", target_dir)
541573

574+
assert captured["removed"] == "temp.zip"
575+
if normalized_root == ".":
576+
assert captured["listdir"] is None
577+
assert captured["move"] is None
578+
assert captured["cleanup"] is None
579+
return
580+
542581
assert captured["listdir"] == expected_root
543582
assert captured["move"] == (expected_file, target_dir)
544583
assert captured["cleanup"] == expected_root

0 commit comments

Comments
 (0)