mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-04 23:17:20 +08:00
fix(plugin): exclude build dependencies from runtime copies (#6096)
This commit is contained in:
+1
-1
@@ -517,7 +517,7 @@ class PluginManager(ConfigReloadMixin, metaclass=Singleton):
|
|||||||
source_dir,
|
source_dir,
|
||||||
dest_dir,
|
dest_dir,
|
||||||
dirs_exist_ok=True,
|
dirs_exist_ok=True,
|
||||||
ignore=shutil.ignore_patterns("__pycache__", "*.pyc", ".DS_Store")
|
ignore=shutil.ignore_patterns("__pycache__", "*.pyc", ".DS_Store", "node_modules")
|
||||||
)
|
)
|
||||||
PluginManager()._recent_local_sync[pid] = time.time()
|
PluginManager()._recent_local_sync[pid] = time.time()
|
||||||
logger.info(f"已同步本地插件 {pid}:{source_dir} -> {dest_dir}")
|
logger.info(f"已同步本地插件 {pid}:{source_dir} -> {dest_dir}")
|
||||||
|
|||||||
@@ -758,7 +758,7 @@ class PluginHelper(metaclass=WeakSingleton):
|
|||||||
source_dir,
|
source_dir,
|
||||||
dest_dir,
|
dest_dir,
|
||||||
dirs_exist_ok=True,
|
dirs_exist_ok=True,
|
||||||
ignore=shutil.ignore_patterns("__pycache__", "*.pyc", ".DS_Store")
|
ignore=shutil.ignore_patterns("__pycache__", "*.pyc", ".DS_Store", "node_modules")
|
||||||
)
|
)
|
||||||
return True, ""
|
return True, ""
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
@@ -1534,22 +1534,50 @@ class TestPluginHelper:
|
|||||||
assert "" == message
|
assert "" == message
|
||||||
assert seen_versions
|
assert seen_versions
|
||||||
|
|
||||||
def test_install_local_delegates_local_repo_url(self, monkeypatch):
|
def test_install_local_copies_runtime_assets_without_build_dependencies(self, monkeypatch, tmp_path):
|
||||||
"""
|
"""
|
||||||
local:// 来源由本地插件安装路径处理,不访问远端仓库。
|
local:// 来源保留运行资产,但不把本地前端构建依赖复制到运行目录。
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
from app.helper.plugin import PluginHelper
|
from app.helper.plugin import PluginHelper
|
||||||
except ModuleNotFoundError as exc:
|
except ModuleNotFoundError as exc:
|
||||||
pytest.skip(f"missing dependency: {exc}")
|
pytest.skip(f"missing dependency: {exc}")
|
||||||
|
|
||||||
helper = PluginHelper()
|
repo_path = tmp_path / "local-plugins"
|
||||||
monkeypatch.setattr(helper, "install_local", lambda pid, repo_url, force_install=False: (True, f"{pid}:{repo_url}"))
|
source_dir = repo_path / "plugins.v2" / PLUGIN_ID.lower()
|
||||||
|
remote_entry = source_dir / "dist" / "assets" / "remoteEntry.js"
|
||||||
|
remote_entry.parent.mkdir(parents=True)
|
||||||
|
remote_entry.write_text("export default {}\n", encoding="utf-8")
|
||||||
|
dependency_file = source_dir / "node_modules" / "example" / "index.js"
|
||||||
|
dependency_file.parent.mkdir(parents=True)
|
||||||
|
dependency_file.write_text("module.exports = {}\n", encoding="utf-8")
|
||||||
|
|
||||||
success, message = helper.install(PLUGIN_ID, f"local://{PLUGIN_ID}?path=/tmp/plugins")
|
runtime_root = tmp_path / "runtime-plugins"
|
||||||
|
helper = PluginHelper()
|
||||||
|
monkeypatch.setattr(
|
||||||
|
helper,
|
||||||
|
"get_local_plugin_candidate",
|
||||||
|
lambda *_args, **_kwargs: {
|
||||||
|
"path": source_dir,
|
||||||
|
"repo_path": repo_path,
|
||||||
|
"package_version": "v2",
|
||||||
|
"version": "1.0.0",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
monkeypatch.setattr("app.helper.plugin.PLUGIN_DIR", runtime_root)
|
||||||
|
monkeypatch.setattr(helper, "refresh_persistent_plugin_backup", lambda _pid: True)
|
||||||
|
|
||||||
|
success, message = helper.install(
|
||||||
|
PLUGIN_ID,
|
||||||
|
helper.make_local_repo_url(PLUGIN_ID, repo_path, "v2"),
|
||||||
|
force_install=True,
|
||||||
|
)
|
||||||
|
|
||||||
assert success
|
assert success
|
||||||
assert message.startswith(f"{PLUGIN_ID}:local://{PLUGIN_ID}")
|
assert "" == message
|
||||||
|
runtime_dir = runtime_root / PLUGIN_ID.lower()
|
||||||
|
assert (runtime_dir / "dist" / "assets" / "remoteEntry.js").is_file()
|
||||||
|
assert not (runtime_dir / "node_modules").exists()
|
||||||
|
|
||||||
def test_install_release_download_failure_falls_back_to_filelist(self, monkeypatch):
|
def test_install_release_download_failure_falls_back_to_filelist(self, monkeypatch):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ def plugin_manager() -> Iterator[PluginManager]:
|
|||||||
|
|
||||||
|
|
||||||
def _build_local_plugin_repo(tmp_path: Path) -> tuple[Path, Path]:
|
def _build_local_plugin_repo(tmp_path: Path) -> tuple[Path, Path]:
|
||||||
"""构造带系统版本要求的本地 v2 插件仓库。"""
|
"""构造带运行资产、构建依赖和系统版本要求的本地 v2 插件仓库。"""
|
||||||
repo_path = tmp_path / "local-plugins"
|
repo_path = tmp_path / "local-plugins"
|
||||||
source_dir = repo_path / "plugins.v2" / "demoplugin"
|
source_dir = repo_path / "plugins.v2" / "demoplugin"
|
||||||
source_file = source_dir / "__init__.py"
|
source_file = source_dir / "__init__.py"
|
||||||
@@ -32,6 +32,12 @@ def _build_local_plugin_repo(tmp_path: Path) -> tuple[Path, Path]:
|
|||||||
" plugin_name = 'Demo'\n",
|
" plugin_name = 'Demo'\n",
|
||||||
encoding="utf-8",
|
encoding="utf-8",
|
||||||
)
|
)
|
||||||
|
remote_entry = source_dir / "dist" / "assets" / "remoteEntry.js"
|
||||||
|
remote_entry.parent.mkdir(parents=True)
|
||||||
|
remote_entry.write_text("export default {}\n", encoding="utf-8")
|
||||||
|
dependency_file = source_dir / "node_modules" / "example" / "index.js"
|
||||||
|
dependency_file.parent.mkdir(parents=True)
|
||||||
|
dependency_file.write_text("module.exports = {}\n", encoding="utf-8")
|
||||||
(repo_path / "package.v2.json").write_text(
|
(repo_path / "package.v2.json").write_text(
|
||||||
'{"DemoPlugin": {"version": "1.0.0", "system_version": ">=2.13.11"}}',
|
'{"DemoPlugin": {"version": "1.0.0", "system_version": ">=2.13.11"}}',
|
||||||
encoding="utf-8",
|
encoding="utf-8",
|
||||||
@@ -62,6 +68,8 @@ def test_dev_local_plugin_candidate_keeps_hot_sync_allowed_when_system_version_l
|
|||||||
assert candidate.get("compatible") is not False
|
assert candidate.get("compatible") is not False
|
||||||
assert plugin_manager._sync_local_plugin_if_installed("DemoPlugin", candidate)
|
assert plugin_manager._sync_local_plugin_if_installed("DemoPlugin", candidate)
|
||||||
assert (runtime_dir / "__init__.py").read_text(encoding="utf-8") == source_file.read_text(encoding="utf-8")
|
assert (runtime_dir / "__init__.py").read_text(encoding="utf-8") == source_file.read_text(encoding="utf-8")
|
||||||
|
assert (runtime_dir / "dist" / "assets" / "remoteEntry.js").is_file()
|
||||||
|
assert not (runtime_dir / "node_modules").exists()
|
||||||
|
|
||||||
|
|
||||||
def test_local_plugin_candidate_keeps_system_version_gate_outside_dev(
|
def test_local_plugin_candidate_keeps_system_version_gate_outside_dev(
|
||||||
|
|||||||
Reference in New Issue
Block a user