feat(plugins): fold first-party plugins in-tree; bundled + external roots
First-party plugins (host_agent, http, snmp, traefik, unifi, docker) are now tracked under plugins/ and baked into the image, so they version atomically with core — ending the cross-repo import drift the roundtable->steward rename exposed. History for these files is preserved in the archived Roundtable-plugins repo. Plugin discovery becomes multi-root: PLUGIN_DIR (single) -> PLUGIN_DIRS (bundled first, then external) + PLUGIN_INSTALL_DIR. Bundled ships in the image; third-party plugins still mount at runtime into the external root (STEWARD_PLUGIN_DIR, default /data/plugins) and downloads/installs land there. Bundled shadows external on a name collision. - config.py: load_bootstrap returns plugin_dirs + plugin_install_dir - app.py: iterate PLUGIN_DIRS at the migration + load sites - migration_runner.py: discover_all_in() unions every plugin root - plugin_manager.py: resolve_plugin_path() (pure, first-root-wins); load / install / hot-reload span all roots; installs target the external root - settings/routes.py: _discover_plugins scans all roots, dedup bundled-first - Dockerfile: COPY plugins/ ; docker-compose: drop host bind, document external - tests/test_plugin_dirs.py: resolution, multi-root discovery, bootstrap split Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+8
-6
@@ -20,13 +20,15 @@ def create_app(
|
||||
bootstrap = {
|
||||
"database_url": "postgresql+asyncpg://test/test",
|
||||
"secret_key": "test-secret-key",
|
||||
"plugin_dir": "plugins",
|
||||
"plugin_dirs": ["plugins"],
|
||||
"plugin_install_dir": "plugins",
|
||||
}
|
||||
|
||||
app.config.update(
|
||||
SECRET_KEY=bootstrap["secret_key"],
|
||||
DATABASE_URL=bootstrap["database_url"],
|
||||
PLUGIN_DIR=bootstrap["plugin_dir"],
|
||||
PLUGIN_DIRS=bootstrap["plugin_dirs"],
|
||||
PLUGIN_INSTALL_DIR=bootstrap["plugin_install_dir"],
|
||||
TESTING=testing,
|
||||
)
|
||||
|
||||
@@ -42,7 +44,7 @@ def create_app(
|
||||
from .core.migration_runner import run_core_migrations
|
||||
run_core_migrations(
|
||||
app.config["DATABASE_URL"],
|
||||
plugin_dir=Path(app.config["PLUGIN_DIR"]).resolve(),
|
||||
plugin_dirs=[Path(d).resolve() for d in app.config["PLUGIN_DIRS"]],
|
||||
)
|
||||
|
||||
# ── 4. Load all settings from DB → populate app.config ────────────────────
|
||||
@@ -81,9 +83,9 @@ def create_app(
|
||||
# so this is a no-op on normal startup. We still run it with all discovered dirs so
|
||||
# Alembic can resolve the full revision graph regardless of which plugins are enabled.
|
||||
if not testing:
|
||||
from .core.migration_runner import run_plugin_migrations, discover_all_plugin_migration_dirs
|
||||
_plugin_dir = Path(app.config["PLUGIN_DIR"]).resolve()
|
||||
_all_plugin_dirs = discover_all_plugin_migration_dirs(_plugin_dir)
|
||||
from .core.migration_runner import run_plugin_migrations, discover_all_in
|
||||
_plugin_dirs = [Path(d).resolve() for d in app.config["PLUGIN_DIRS"]]
|
||||
_all_plugin_dirs = discover_all_in(_plugin_dirs)
|
||||
run_plugin_migrations(app.config["DATABASE_URL"], _all_plugin_dirs)
|
||||
|
||||
# ── 6. Alert pipeline ──────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user