Plugin discovery fix

Fixed problem where plugins were trying to load before DB was ready.
This commit is contained in:
Dispatcharr 2025-09-07 17:38:34 -05:00
parent 5b31440018
commit 1200d7d894
2 changed files with 59 additions and 11 deletions

View file

@ -1,4 +1,7 @@
from django.apps import AppConfig
import os
import sys
from django.db.models.signals import post_migrate
class PluginsConfig(AppConfig):
@ -6,14 +9,46 @@ class PluginsConfig(AppConfig):
verbose_name = "Plugins"
def ready(self):
# Perform plugin discovery on startup
try:
from .loader import PluginManager
"""Wire up plugin discovery without hitting the DB during app init.
PluginManager.get().discover_plugins()
- Skip during common management commands that don't need discovery.
- Register post_migrate handler to sync plugin registry to DB after migrations.
- Do an in-memory discovery (no DB) so registry is available early.
"""
try:
# Allow explicit opt-out via env var
if os.environ.get("DISPATCHARR_SKIP_PLUGIN_AUTODISCOVERY", "").lower() in ("1", "true", "yes"):
return
argv = sys.argv[1:] if len(sys.argv) > 1 else []
mgmt_cmds_to_skip = {
# Skip immediate discovery for these commands
"makemigrations", "collectstatic", "check", "test", "shell", "showmigrations",
}
if argv and argv[0] in mgmt_cmds_to_skip:
return
# Run discovery with DB sync after the plugins app has been migrated
def _post_migrate_discover(sender=None, app_config=None, **kwargs):
try:
if app_config and getattr(app_config, 'label', None) != 'plugins':
return
from .loader import PluginManager
PluginManager.get().discover_plugins(sync_db=True)
except Exception:
import logging
logging.getLogger(__name__).exception("Plugin discovery failed in post_migrate")
post_migrate.connect(
_post_migrate_discover,
dispatch_uid="apps.plugins.post_migrate_discover",
)
# Perform non-DB discovery now to populate in-memory registry.
from .loader import PluginManager
PluginManager.get().discover_plugins(sync_db=False)
except Exception:
# Avoid breaking startup due to plugin errors
import logging
logging.getLogger(__name__).exception("Plugin discovery failed during app ready")
logging.getLogger(__name__).exception("Plugin discovery wiring failed during app ready")

View file

@ -45,8 +45,11 @@ class PluginManager:
if self.plugins_dir not in sys.path:
sys.path.append(self.plugins_dir)
def discover_plugins(self) -> Dict[str, LoadedPlugin]:
logger.info(f"Discovering plugins in {self.plugins_dir}")
def discover_plugins(self, *, sync_db: bool = True) -> Dict[str, LoadedPlugin]:
if sync_db:
logger.info(f"Discovering plugins in {self.plugins_dir}")
else:
logger.debug(f"Discovering plugins (no DB sync) in {self.plugins_dir}")
self._registry.clear()
try:
@ -66,8 +69,13 @@ class PluginManager:
except FileNotFoundError:
logger.warning(f"Plugins directory not found: {self.plugins_dir}")
# Sync DB records
self._sync_db_with_registry()
# Sync DB records (optional)
if sync_db:
try:
self._sync_db_with_registry()
except Exception:
# Defer sync if database is not ready (e.g., first startup before migrate)
logger.exception("Deferring plugin DB sync; database not ready yet")
return self._registry
def _load_plugin(self, key: str, path: str):
@ -156,7 +164,12 @@ class PluginManager:
from .models import PluginConfig
plugins: List[Dict[str, Any]] = []
configs = {c.key: c for c in PluginConfig.objects.all()}
try:
configs = {c.key: c for c in PluginConfig.objects.all()}
except Exception as e:
# Database might not be migrated yet; fall back to registry only
logger.warning("PluginConfig table unavailable; listing registry only: %s", e)
configs = {}
# First, include all discovered plugins
for key, lp in self._registry.items():