mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-05 15:38:19 +08:00
fix 插件恢复前先装配插件市场、包管理、依赖安装等外部服务
This commit is contained in:
@@ -33,7 +33,12 @@ from app.startup.command_initializer import init_command, stop_command, restart_
|
|||||||
from app.startup.domain_initializer import configure_domain_dependencies
|
from app.startup.domain_initializer import configure_domain_dependencies
|
||||||
from app.startup.modules_initializer import init_modules, stop_modules
|
from app.startup.modules_initializer import init_modules, stop_modules
|
||||||
from app.startup.monitor_initializer import stop_monitor, init_monitor
|
from app.startup.monitor_initializer import stop_monitor, init_monitor
|
||||||
from app.startup.plugins_initializer import init_plugins, stop_plugins, sync_plugins
|
from app.startup.plugins_initializer import (
|
||||||
|
configure_plugin_services,
|
||||||
|
init_plugins,
|
||||||
|
stop_plugins,
|
||||||
|
sync_plugins,
|
||||||
|
)
|
||||||
from app.startup.routers_initializer import init_routers
|
from app.startup.routers_initializer import init_routers
|
||||||
from app.startup.scheduler_initializer import (
|
from app.startup.scheduler_initializer import (
|
||||||
stop_scheduler,
|
stop_scheduler,
|
||||||
@@ -112,6 +117,12 @@ async def run_startup_step(
|
|||||||
logger.info("启动%s完成,耗时=%.2fms", name, elapsed_ms)
|
logger.info("启动%s完成,耗时=%.2fms", name, elapsed_ms)
|
||||||
|
|
||||||
|
|
||||||
|
def prepare_plugin_restore() -> None:
|
||||||
|
"""先装配插件外部系统服务,再恢复插件及其依赖。"""
|
||||||
|
configure_plugin_services()
|
||||||
|
SystemChain().restore_plugins()
|
||||||
|
|
||||||
|
|
||||||
def build_lifecycle_components(app: FastAPI) -> tuple[LifecycleComponent, ...]:
|
def build_lifecycle_components(app: FastAPI) -> tuple[LifecycleComponent, ...]:
|
||||||
"""按现有顺序构建应用组件清单,回调在每次 lifespan 启动时重新绑定。"""
|
"""按现有顺序构建应用组件清单,回调在每次 lifespan 启动时重新绑定。"""
|
||||||
return (
|
return (
|
||||||
@@ -166,7 +177,7 @@ def build_lifecycle_components(app: FastAPI) -> tuple[LifecycleComponent, ...]:
|
|||||||
name="插件备份恢复",
|
name="插件备份恢复",
|
||||||
dependencies=("模块服务",),
|
dependencies=("模块服务",),
|
||||||
mode=LifecycleMode.NORMAL_ONLY,
|
mode=LifecycleMode.NORMAL_ONLY,
|
||||||
start=lambda: SystemChain().restore_plugins(),
|
start=prepare_plugin_restore,
|
||||||
start_order=70,
|
start_order=70,
|
||||||
start_timeout_seconds=300,
|
start_timeout_seconds=300,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ def _prepare_legacy_plugin_import(*, plugin_id: str, plugin_dir: Path) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _configure_plugin_services() -> None:
|
def configure_plugin_services() -> None:
|
||||||
"""把兼容诊断、远程上报和站点认证等级装配到插件管理器。"""
|
"""把兼容诊断、远程上报和站点认证等级装配到插件管理器。"""
|
||||||
plugin_helper = PluginHelper()
|
plugin_helper = PluginHelper()
|
||||||
market_client = PluginMarketClient(plugin_helper)
|
market_client = PluginMarketClient(plugin_helper)
|
||||||
@@ -119,7 +119,7 @@ async def sync_plugins() -> bool:
|
|||||||
初始化安装插件,并动态注册后台任务及API
|
初始化安装插件,并动态注册后台任务及API
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
_configure_plugin_services()
|
configure_plugin_services()
|
||||||
loop = global_vars.loop
|
loop = global_vars.loop
|
||||||
plugin_manager = PluginManager()
|
plugin_manager = PluginManager()
|
||||||
|
|
||||||
@@ -172,7 +172,7 @@ def init_plugins():
|
|||||||
"""
|
"""
|
||||||
初始化插件
|
初始化插件
|
||||||
"""
|
"""
|
||||||
_configure_plugin_services()
|
configure_plugin_services()
|
||||||
PluginManager().start()
|
PluginManager().start()
|
||||||
register_plugin_api()
|
register_plugin_api()
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ def _patch_lifespan(monkeypatch, *, failing_step: str | None = None) -> dict:
|
|||||||
"init_workflow",
|
"init_workflow",
|
||||||
):
|
):
|
||||||
monkeypatch.setattr(lifecycle, name, MagicMock())
|
monkeypatch.setattr(lifecycle, name, MagicMock())
|
||||||
|
monkeypatch.setattr(lifecycle, "configure_plugin_services", MagicMock())
|
||||||
monkeypatch.setattr(lifecycle, "init_modules", AsyncMock())
|
monkeypatch.setattr(lifecycle, "init_modules", AsyncMock())
|
||||||
|
|
||||||
# 启动期的引擎预热与额度核算也要打桩。不打的话这些用例会走真实的引擎创建,在测试
|
# 启动期的引擎预热与额度核算也要打桩。不打的话这些用例会走真实的引擎创建,在测试
|
||||||
@@ -124,6 +125,7 @@ def test_lifespan_normal_mode_starts_full_runtime(monkeypatch):
|
|||||||
asyncio.run(run_lifespan())
|
asyncio.run(run_lifespan())
|
||||||
|
|
||||||
lifecycle.init_modules.assert_awaited_once_with()
|
lifecycle.init_modules.assert_awaited_once_with()
|
||||||
|
lifecycle.configure_plugin_services.assert_called_once_with()
|
||||||
for name in (
|
for name in (
|
||||||
"init_plugins",
|
"init_plugins",
|
||||||
"init_scheduler",
|
"init_scheduler",
|
||||||
@@ -137,6 +139,25 @@ def test_lifespan_normal_mode_starts_full_runtime(monkeypatch):
|
|||||||
_assert_completed_once(step)
|
_assert_completed_once(step)
|
||||||
|
|
||||||
|
|
||||||
|
def test_lifespan_configures_plugin_services_before_restore(monkeypatch):
|
||||||
|
"""插件恢复依赖的外部系统服务必须先于恢复阶段完成装配。"""
|
||||||
|
shutdown_steps = _patch_lifespan(monkeypatch)
|
||||||
|
order = []
|
||||||
|
lifecycle.configure_plugin_services.side_effect = lambda: order.append("configure")
|
||||||
|
lifecycle.SystemChain.return_value.restore_plugins.side_effect = (
|
||||||
|
lambda: order.append("restore")
|
||||||
|
)
|
||||||
|
|
||||||
|
async def run_lifespan():
|
||||||
|
async with lifecycle.lifespan(FastAPI()):
|
||||||
|
pass
|
||||||
|
|
||||||
|
asyncio.run(run_lifespan())
|
||||||
|
|
||||||
|
assert order == ["configure", "restore"]
|
||||||
|
_assert_completed_once(shutdown_steps["close_http"])
|
||||||
|
|
||||||
|
|
||||||
def test_lifespan_safe_mode_skips_optional_runtime(monkeypatch):
|
def test_lifespan_safe_mode_skips_optional_runtime(monkeypatch):
|
||||||
"""安全模式只启动基础模块,并跳过插件及可选后台服务。"""
|
"""安全模式只启动基础模块,并跳过插件及可选后台服务。"""
|
||||||
shutdown_steps = _patch_lifespan(monkeypatch)
|
shutdown_steps = _patch_lifespan(monkeypatch)
|
||||||
|
|||||||
Reference in New Issue
Block a user