fix(bundle): semver-aware schema update — не откатывать user-published

This commit is contained in:
Александр Зимин
2026-06-02 11:11:53 +00:00
parent c892f30b3e
commit 39e24a6d01
@@ -103,7 +103,14 @@ public class CuodBundleImporter {
Optional<DictionaryDefinition> existing = repository.findByName(entry.name()); Optional<DictionaryDefinition> existing = repository.findByName(entry.name());
if (existing.isPresent()) { if (existing.isPresent()) {
DictionaryDefinition def = existing.get(); 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(); JsonNode prevSchema = def.getSchemaJson();
def.setSchemaJson(schema); def.setSchemaJson(schema);
def.setSchemaVersion(entry.schemaVersion()); def.setSchemaVersion(entry.schemaVersion());
@@ -304,4 +311,44 @@ public class CuodBundleImporter {
} }
return null; 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.
*
* <p>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;
}
}
} }