diff --git a/api_contract/flutter_model_contract.json b/api_contract/flutter_model_contract.json index 6f32d695..449fb1ef 100644 --- a/api_contract/flutter_model_contract.json +++ b/api_contract/flutter_model_contract.json @@ -1654,11 +1654,12 @@ "Ingredient": { "required": [ "name", - "unit", "portion", "product_nutritional_id" ], - "optional": [], + "optional": [ + "unit" + ], "types": { "name": "String", "unit": "String", @@ -3538,18 +3539,18 @@ } }, "Step": { - "required": [], - "optional": [ + "required": [ "image", - "description" + "description_md" ], + "optional": [], "types": { "image": "String", - "description": "String" + "description_md": "String" }, "kinds": { "image": "string", - "description": "string" + "description_md": "string" } }, "StepEntity": { diff --git a/tool/export_api_contract.dart b/tool/export_api_contract.dart index 72321e8c..3db2d296 100644 --- a/tool/export_api_contract.dart +++ b/tool/export_api_contract.dart @@ -31,7 +31,13 @@ void main(List args) { final sources = {}; final enumNames = {}; - for (final file in root.listSync(recursive: true).whereType()) { + final files = root.listSync(recursive: true).whereType().toList(); + // ДЕТЕРМИНИЗМ: сортируем по пути — порядок Directory.listSync зависит от файловой системы, + // а при коллизии имён моделей побеждает последняя обработанная. Без сортировки локально + // и на CI получаются разные манифесты (баг, из-за которого CI падал на чистом PR). + files.sort((a, b) => a.path.compareTo(b.path)); + + for (final file in files) { if (!file.path.endsWith('.dart') || file.path.endsWith('.g.dart') || file.path.endsWith('.freezed.dart')) { continue; } @@ -45,8 +51,21 @@ void main(List args) { } final contract = >{}; - for (final source in sources.values) { - contract.addAll(extractModels(source)); + // modelOrigin: имя модели -> файл, из которого она взята (для предупреждений о коллизиях). + final modelOrigin = {}; + for (final entry in sources.entries) { + final extracted = extractModels(entry.value); + for (final modelName in extracted.keys) { + if (contract.containsKey(modelName)) { + stderr.writeln( + 'WARNING: model "$modelName" defined in both "${modelOrigin[modelName]}" ' + 'and "${entry.key.path}" — keeping the one from "${entry.key.path}" (sorted-order last wins). ' + 'Consider renaming the Dart classes to make the contract unambiguous.', + ); + } + modelOrigin[modelName] = entry.key.path; + contract[modelName] = extracted[modelName]!; + } } // kinds считаются второй проходкой: нужны все имена моделей (contract.keys) и enum'ов.