fix: use semver comparison for extension version upgrades
Previously the system used simple string inequality to detect available updates, causing downgrades to appear as upgrades (e.g. 2.5.1 → 2.5.0). Now both backend (SQL) and frontend (JS) compare version tuples properly. Also fixes latest_version in DB on registry sync startup. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
+12
-2
@@ -156,6 +156,15 @@ def sync_extension_registry():
|
|||||||
(name,), fetch=None)
|
(name,), fetch=None)
|
||||||
deactivated.append(name)
|
deactivated.append(name)
|
||||||
|
|
||||||
|
# Fix latest_version: set to the highest version from extension_versions
|
||||||
|
query("""UPDATE extensions e SET latest_version = sub.max_ver, updated_at = NOW()
|
||||||
|
FROM (SELECT extension_id, version as max_ver FROM extension_versions ev1
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM extension_versions ev2
|
||||||
|
WHERE ev2.extension_id = ev1.extension_id
|
||||||
|
AND string_to_array(ev2.version, '.')::int[] > string_to_array(ev1.version, '.')::int[])
|
||||||
|
) sub WHERE e.id = sub.extension_id AND (e.latest_version IS NULL OR e.latest_version != sub.max_ver)""",
|
||||||
|
fetch=None)
|
||||||
|
|
||||||
# Reload MODULES from DB
|
# Reload MODULES from DB
|
||||||
_load_modules_from_db()
|
_load_modules_from_db()
|
||||||
|
|
||||||
@@ -363,8 +372,9 @@ def build_and_publish(module, version):
|
|||||||
SELECT id, %s, %s, %s FROM extensions WHERE name = %s
|
SELECT id, %s, %s, %s FROM extensions WHERE name = %s
|
||||||
ON CONFLICT (extension_id, version) DO NOTHING""",
|
ON CONFLICT (extension_id, version) DO NOTHING""",
|
||||||
(version, f'{module}-{version}.zip', tag, module))
|
(version, f'{module}-{version}.zip', tag, module))
|
||||||
cur.execute("UPDATE extensions SET latest_version = %s, updated_at = NOW() WHERE name = %s",
|
cur.execute("""UPDATE extensions SET latest_version = %s, updated_at = NOW()
|
||||||
(version, module))
|
WHERE name = %s AND (latest_version IS NULL OR string_to_array(latest_version, '.')::int[] < string_to_array(%s, '.')::int[])""",
|
||||||
|
(version, module, version))
|
||||||
conn.commit()
|
conn.commit()
|
||||||
cur.close()
|
cur.close()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|||||||
@@ -456,7 +456,8 @@ function CustomerDetail({ customerId, onDelete }) {
|
|||||||
<tbody className="divide-y divide-slate-50">
|
<tbody className="divide-y divide-slate-50">
|
||||||
{sorted.map(e => {
|
{sorted.map(e => {
|
||||||
const installed = e.installed_version
|
const installed = e.installed_version
|
||||||
const hasUpdate = installed && e.latest_version && installed !== e.latest_version
|
const semverGt = (a, b) => { const pa = a.split('.').map(Number), pb = b.split('.').map(Number); for (let i = 0; i < Math.max(pa.length, pb.length); i++) { if ((pa[i]||0) > (pb[i]||0)) return true; if ((pa[i]||0) < (pb[i]||0)) return false; } return false }
|
||||||
|
const hasUpdate = installed && e.latest_version && semverGt(e.latest_version, installed)
|
||||||
const missingDeps = (deps[e.name] || []).filter(d => !installedNames.has(d))
|
const missingDeps = (deps[e.name] || []).filter(d => !installedNames.has(d))
|
||||||
const blocked = !installed && missingDeps.length > 0
|
const blocked = !installed && missingDeps.length > 0
|
||||||
return (
|
return (
|
||||||
|
|||||||
Reference in New Issue
Block a user