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
|
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 ──
|
# ── Install / Activate / Deactivate / Uninstall ──
|
||||||
|
|
||||||
async def install(self, db: AsyncSession, name: str) -> PluginModel:
|
async def install(self, db: AsyncSession, name: str) -> PluginModel:
|
||||||
"""Install a plugin: run migrations and create DB record.
|
"""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.
|
Checks that all declared dependencies are installed first.
|
||||||
"""
|
"""
|
||||||
plugin = self.get_plugin(name)
|
plugin = self.get_plugin(name)
|
||||||
@@ -226,6 +267,8 @@ class PluginRegistry:
|
|||||||
# Check if already installed in DB
|
# Check if already installed in DB
|
||||||
existing = await self._get_plugin_record(db, name)
|
existing = await self._get_plugin_record(db, name)
|
||||||
if existing is not None:
|
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
|
return existing
|
||||||
|
|
||||||
# Check dependencies are installed
|
# Check dependencies are installed
|
||||||
@@ -256,7 +299,8 @@ class PluginRegistry:
|
|||||||
async def activate(self, db: AsyncSession, name: str) -> PluginModel:
|
async def activate(self, db: AsyncSession, name: str) -> PluginModel:
|
||||||
"""Activate a plugin: register routes, event listeners, set status=active.
|
"""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.
|
Checks that all declared dependencies are active first.
|
||||||
Performs a soft permission check and logs warnings for unknown permissions.
|
Performs a soft permission check and logs warnings for unknown permissions.
|
||||||
"""
|
"""
|
||||||
@@ -268,6 +312,9 @@ class PluginRegistry:
|
|||||||
if record is None:
|
if record is None:
|
||||||
raise ValueError(f"Plugin '{name}' is not installed — install first")
|
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
|
# Idempotent: already active
|
||||||
if record.active and record.status == "active":
|
if record.active and record.status == "active":
|
||||||
return record
|
return record
|
||||||
|
|||||||
Reference in New Issue
Block a user