fix(plugin): add version comparison and migration run on version mismatch
Bug 6: install() and activate() now compare the plugin's manifest version with the stored DB version. If they differ, migrations are re-run to bring the schema up to date and the DB version field is updated. This ensures that updating a plugin with a new manifest version triggers the migration runner automatically.
This commit is contained in:
+49
-2
@@ -211,12 +211,53 @@ class PluginRegistry:
|
||||
)
|
||||
return warnings
|
||||
|
||||
# ── Version Comparison & Update Path ──
|
||||
|
||||
async def _check_and_run_version_migrations(
|
||||
self, db: AsyncSession, name: str, record: PluginModel
|
||||
) -> bool:
|
||||
"""Compare manifest version with DB version and run migrations if different.
|
||||
|
||||
If the plugin's manifest version differs from the stored DB version,
|
||||
re-run all migrations to bring the schema up to date, then update
|
||||
the DB record's version field.
|
||||
|
||||
Returns True if migrations were run, False otherwise.
|
||||
"""
|
||||
plugin = self.get_plugin(name)
|
||||
if plugin is None:
|
||||
return False
|
||||
|
||||
manifest_version = plugin.manifest.version
|
||||
db_version = record.version
|
||||
|
||||
if manifest_version == db_version:
|
||||
return False
|
||||
|
||||
logger.info(
|
||||
f"Plugin '{name}' version mismatch: DB={db_version}, manifest={manifest_version}. "
|
||||
f"Running migrations to update."
|
||||
)
|
||||
|
||||
# Re-run migrations to apply any new migration files
|
||||
if plugin.manifest.migrations:
|
||||
await self.migration_runner.run_all_migrations(db, name, plugin.manifest.migrations)
|
||||
|
||||
# Update DB version to match manifest
|
||||
record.version = manifest_version
|
||||
await db.flush()
|
||||
self._db_status[name] = record
|
||||
|
||||
logger.info(f"Plugin '{name}' updated to version {manifest_version}.")
|
||||
return True
|
||||
|
||||
# ── Install / Activate / Deactivate / Uninstall ──
|
||||
|
||||
async def install(self, db: AsyncSession, name: str) -> PluginModel:
|
||||
"""Install a plugin: run migrations and create DB record.
|
||||
|
||||
Idempotent: if already installed, returns existing record.
|
||||
Idempotent: if already installed, checks for version updates and
|
||||
returns existing record.
|
||||
Checks that all declared dependencies are installed first.
|
||||
"""
|
||||
plugin = self.get_plugin(name)
|
||||
@@ -226,6 +267,8 @@ class PluginRegistry:
|
||||
# Check if already installed in DB
|
||||
existing = await self._get_plugin_record(db, name)
|
||||
if existing is not None:
|
||||
# Check for version update — run migrations if version changed
|
||||
await self._check_and_run_version_migrations(db, name, existing)
|
||||
return existing
|
||||
|
||||
# Check dependencies are installed
|
||||
@@ -256,7 +299,8 @@ class PluginRegistry:
|
||||
async def activate(self, db: AsyncSession, name: str) -> PluginModel:
|
||||
"""Activate a plugin: register routes, event listeners, set status=active.
|
||||
|
||||
Idempotent: if already active, returns existing record without error.
|
||||
Idempotent: if already active, checks for version updates and
|
||||
returns existing record without error.
|
||||
Checks that all declared dependencies are active first.
|
||||
Performs a soft permission check and logs warnings for unknown permissions.
|
||||
"""
|
||||
@@ -268,6 +312,9 @@ class PluginRegistry:
|
||||
if record is None:
|
||||
raise ValueError(f"Plugin '{name}' is not installed — install first")
|
||||
|
||||
# Check for version update — run migrations if version changed
|
||||
await self._check_and_run_version_migrations(db, name, record)
|
||||
|
||||
# Idempotent: already active
|
||||
if record.active and record.status == "active":
|
||||
return record
|
||||
|
||||
Reference in New Issue
Block a user