Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
13 changes: 8 additions & 5 deletions scripts/ci/package_windows_portable.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,12 @@
"""
TAURI_CONFIG_RELATIVE_PATH = pathlib.Path("src-tauri") / "tauri.conf.json"
CARGO_TOML_RELATIVE_PATH = pathlib.Path("src-tauri") / "Cargo.toml"
# These point to the source resource directories inside the repository checkout.
BACKEND_RESOURCE_RELATIVE_PATH = pathlib.Path("resources") / "backend"
WEBUI_RESOURCE_RELATIVE_PATH = pathlib.Path("resources") / "webui"
# These are the runtime-visible locations emitted into the portable package root.
PORTABLE_BACKEND_LAYOUT_RELATIVE_PATH = pathlib.Path("backend")
PORTABLE_WEBUI_LAYOUT_RELATIVE_PATH = pathlib.Path("webui")
WINDOWS_CLEANUP_SCRIPT_RELATIVE_PATH = (
pathlib.Path("src-tauri") / "windows" / "kill-backend-processes.ps1"
)
Expand Down Expand Up @@ -263,16 +267,15 @@ def populate_portable_root(
if cleanup_script.is_file():
shutil.copy2(cleanup_script, destination_root / "kill-backend-processes.ps1")

resources_root = destination_root / "resources"
backend_src = project_config.root / BACKEND_RESOURCE_RELATIVE_PATH
if not backend_src.is_dir():
raise FileNotFoundError(f"Required directory not found: {backend_src}")
shutil.copytree(backend_src, resources_root / "backend")
shutil.copytree(backend_src, destination_root / PORTABLE_BACKEND_LAYOUT_RELATIVE_PATH)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

When packaging the backend resources, it is a good practice to exclude development artifacts like __pycache__ and compiled Python files (.pyc, .pyo). These files are not necessary for the portable runtime and can bloat the package or cause issues if they were generated with a different Python version.

Suggested change
shutil.copytree(backend_src, destination_root / PORTABLE_BACKEND_LAYOUT_RELATIVE_PATH)
shutil.copytree(
backend_src,
destination_root / PORTABLE_BACKEND_LAYOUT_RELATIVE_PATH,
ignore=shutil.ignore_patterns("__pycache__", "*.pyc", "*.pyo")
)


webui_src = project_config.root / WEBUI_RESOURCE_RELATIVE_PATH
if not webui_src.is_dir():
raise FileNotFoundError(f"Required directory not found: {webui_src}")
shutil.copytree(webui_src, resources_root / "webui")
shutil.copytree(webui_src, destination_root / PORTABLE_WEBUI_LAYOUT_RELATIVE_PATH)

add_portable_runtime_files(destination_root, project_config)
validate_portable_root(destination_root)
Expand All @@ -292,8 +295,8 @@ def add_portable_runtime_files(

def validate_portable_root(destination_root: pathlib.Path) -> None:
expected_paths = [
destination_root / "resources" / "backend" / "runtime-manifest.json",
destination_root / "resources" / "webui" / "index.html",
destination_root / PORTABLE_BACKEND_LAYOUT_RELATIVE_PATH / "runtime-manifest.json",
destination_root / PORTABLE_WEBUI_LAYOUT_RELATIVE_PATH / "index.html",
]
missing = [
str(path.relative_to(destination_root))
Expand Down
26 changes: 12 additions & 14 deletions scripts/ci/test_package_windows_portable.py
Original file line number Diff line number Diff line change
Expand Up @@ -423,13 +423,11 @@ def test_populate_portable_root_copies_release_bundle_contents(self):
self.assertFalse((destination_root / "astrbot-desktop-tauri.exe").exists())
self.assertTrue((destination_root / "WebView2Loader.dll").is_file())
self.assertTrue(
(
destination_root / "resources" / "backend" / "runtime-manifest.json"
).is_file()
)
self.assertTrue(
(destination_root / "resources" / "webui" / "index.html").is_file()
(destination_root / "backend" / "runtime-manifest.json").is_file()
)
self.assertTrue((destination_root / "webui" / "index.html").is_file())
self.assertFalse((destination_root / "resources" / "backend").exists())
self.assertFalse((destination_root / "resources" / "webui").exists())
self.assertTrue((destination_root / "kill-backend-processes.ps1").is_file())
self.assertTrue((destination_root / "portable.flag").is_file())
self.assertTrue((destination_root / MODULE.PORTABLE_README_NAME).is_file())
Comment on lines 487 to 507
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion (testing): Assert that the legacy resources/backend and resources/webui paths are not created anymore

Please also have this test assert that the old resources/backend and resources/webui directories do not exist (e.g. self.assertFalse((destination_root / "resources" / "backend").exists()) and similarly for webui). This will catch any future change that mistakenly writes files to both the old and new layouts.

Suggested change
self.assertFalse((destination_root / "astrbot-desktop-tauri.exe").exists())
self.assertTrue((destination_root / "WebView2Loader.dll").is_file())
self.assertTrue(
(
destination_root / "resources" / "backend" / "runtime-manifest.json"
).is_file()
)
self.assertTrue(
(destination_root / "resources" / "webui" / "index.html").is_file()
(destination_root / "backend" / "runtime-manifest.json").is_file()
)
self.assertTrue((destination_root / "webui" / "index.html").is_file())
self.assertTrue((destination_root / "kill-backend-processes.ps1").is_file())
self.assertTrue((destination_root / "portable.flag").is_file())
self.assertTrue((destination_root / MODULE.PORTABLE_README_NAME).is_file())
self.assertFalse((destination_root / "astrbot-desktop-tauri.exe").exists())
self.assertTrue((destination_root / "WebView2Loader.dll").is_file())
self.assertTrue(
(destination_root / "backend" / "runtime-manifest.json").is_file()
)
self.assertTrue((destination_root / "webui" / "index.html").is_file())
# Legacy layout paths must no longer be created
self.assertFalse((destination_root / "resources" / "backend").exists())
self.assertFalse((destination_root / "resources" / "webui").exists())
self.assertTrue((destination_root / "kill-backend-processes.ps1").is_file())
self.assertTrue((destination_root / "portable.flag").is_file())
self.assertTrue((destination_root / MODULE.PORTABLE_README_NAME).is_file())

Expand Down Expand Up @@ -480,10 +478,10 @@ def test_validate_portable_root_accepts_expected_layout(self):
with tempfile.TemporaryDirectory() as tmpdir:
root = Path(tmpdir)
(root / "AstrBot.exe").write_text("binary")
(root / "resources" / "backend").mkdir(parents=True)
(root / "resources" / "webui").mkdir(parents=True)
(root / "resources" / "backend" / "runtime-manifest.json").write_text("{}")
(root / "resources" / "webui" / "index.html").write_text("<html></html>")
(root / "backend").mkdir(parents=True)
(root / "webui").mkdir(parents=True)
(root / "backend" / "runtime-manifest.json").write_text("{}")
(root / "webui" / "index.html").write_text("<html></html>")

MODULE.validate_portable_root(root)

Expand All @@ -498,10 +496,10 @@ def test_validate_portable_root_requires_expected_files(self):
def test_validate_portable_root_requires_top_level_exe(self):
with tempfile.TemporaryDirectory() as tmpdir:
root = Path(tmpdir)
(root / "resources" / "backend").mkdir(parents=True)
(root / "resources" / "webui").mkdir(parents=True)
(root / "resources" / "backend" / "runtime-manifest.json").write_text("{}")
(root / "resources" / "webui" / "index.html").write_text("<html></html>")
(root / "backend").mkdir(parents=True)
(root / "webui").mkdir(parents=True)
(root / "backend" / "runtime-manifest.json").write_text("{}")
(root / "webui" / "index.html").write_text("<html></html>")

with self.assertRaisesRegex(ValueError, r"top-level \*\.exe"):
MODULE.validate_portable_root(root)
Expand Down