fix(contract): deterministic model collection order

This commit is contained in:
2026-10-24 11:37:34 +03:00
parent 7308e3e972
commit 4aadc8a9d8
2 changed files with 30 additions and 10 deletions

View File

@@ -31,7 +31,13 @@ void main(List<String> args) {
final sources = <File, String>{};
final enumNames = <String>{};
for (final file in root.listSync(recursive: true).whereType<File>()) {
final files = root.listSync(recursive: true).whereType<File>().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<String> args) {
}
final contract = <String, Map<String, dynamic>>{};
for (final source in sources.values) {
contract.addAll(extractModels(source));
// modelOrigin: имя модели -> файл, из которого она взята (для предупреждений о коллизиях).
final modelOrigin = <String, String>{};
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'ов.