From 39e24a6d0130fc29019d3347c92f0b431a7ca0e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80=20?= =?UTF-8?q?=D0=97=D0=B8=D0=BC=D0=B8=D0=BD?= Date: Tue, 2 Jun 2026 11:11:53 +0000 Subject: [PATCH] =?UTF-8?q?fix(bundle):=20semver-aware=20schema=20update?= =?UTF-8?q?=20=E2=80=94=20=D0=BD=D0=B5=20=D0=BE=D1=82=D0=BA=D0=B0=D1=82?= =?UTF-8?q?=D1=8B=D0=B2=D0=B0=D1=82=D1=8C=20user-published?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ordinis/cuod/CuodBundleImporter.java | 49 ++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/ordinis-cuod-bundle/src/main/java/cloud/nstart/terravault/ordinis/cuod/CuodBundleImporter.java b/ordinis-cuod-bundle/src/main/java/cloud/nstart/terravault/ordinis/cuod/CuodBundleImporter.java index 5517f8d..40cb7ad 100644 --- a/ordinis-cuod-bundle/src/main/java/cloud/nstart/terravault/ordinis/cuod/CuodBundleImporter.java +++ b/ordinis-cuod-bundle/src/main/java/cloud/nstart/terravault/ordinis/cuod/CuodBundleImporter.java @@ -103,7 +103,14 @@ public class CuodBundleImporter { Optional existing = repository.findByName(entry.name()); if (existing.isPresent()) { DictionaryDefinition def = existing.get(); - if (props.updateExistingSchema() && !entry.schemaVersion().equals(def.getSchemaVersion())) { + // Semver-aware update: применяем bundle только если он СТРОГО НОВЕЕ + // чем то что в БД. Иначе bundle с 1.0.0 откатывал бы user-published + // 1.1.0 на каждом рестарте writer'а (regression наблюдалась на staging + // 2026-06-02 — ground_station). User-published > bundle-shipped по + // умолчанию: dev опубликовал — это новее manifest.json. + boolean bundleNewer = props.updateExistingSchema() + && isStrictlyNewer(entry.schemaVersion(), def.getSchemaVersion()); + if (bundleNewer) { JsonNode prevSchema = def.getSchemaJson(); def.setSchemaJson(schema); def.setSchemaVersion(entry.schemaVersion()); @@ -304,4 +311,44 @@ public class CuodBundleImporter { } return null; } + + /** + * True если {@code bundle} строго новее {@code db} по semver (X.Y.Z). + * Используется чтобы bundle-import не откатывал user-published schema: + * bundle ship'ит 1.0.0, dev опубликовал 1.1.0 → bundle skip. + * Bundle ship'ит 1.2.0 → bundle wins. + * + *

Non-semver формат: fallback на string equals (т.е. update только + * если "разные"). Не худшее поведение — раньше так и было всегда. + */ + static boolean isStrictlyNewer(String bundleVersion, String dbVersion) { + if (bundleVersion == null) return false; + if (dbVersion == null) return true; + int[] bundle = parseSemver(bundleVersion); + int[] db = parseSemver(dbVersion); + if (bundle == null || db == null) { + // Fallback: non-semver — обновим если "разные" чтобы сохранить + // прежнее поведение для custom версионных схем. + return !bundleVersion.equals(dbVersion); + } + for (int i = 0; i < 3; i++) { + if (bundle[i] > db[i]) return true; + if (bundle[i] < db[i]) return false; + } + return false; // equal + } + + private static int[] parseSemver(String s) { + String[] parts = s.split("\\."); + if (parts.length != 3) return null; + try { + return new int[] { + Integer.parseInt(parts[0]), + Integer.parseInt(parts[1]), + Integer.parseInt(parts[2]), + }; + } catch (NumberFormatException e) { + return null; + } + } }