read static method
Read and verify the published bundle under baseDir. Throws
FormatException on a missing/malformed manifest or a SHA-256
mismatch (a corrupt object must never reach a deploy).
Implementation
static PulledBundle read(String baseDir) {
final headFile = File(p.join(baseDir, 'manifest.json'));
if (!headFile.existsSync()) {
throw FormatException('No bundle channel head at ${headFile.path}.');
}
final head = _decodeObject(headFile.readAsStringSync(), headFile.path);
final manifestRel = head['manifest'];
if (manifestRel is! String) {
throw const FormatException('Channel head is missing `manifest`.');
}
final manifestFile = File(p.join(baseDir, manifestRel));
if (!manifestFile.existsSync()) {
throw FormatException('Bundle manifest missing at ${manifestFile.path}.');
}
final manifest = _decodeObject(
manifestFile.readAsStringSync(),
manifestFile.path,
);
final versionDir = p.dirname(manifestFile.path);
final locales = <PulledLocale>[];
final rawLocales = manifest['locales'];
if (rawLocales is! List) {
throw const FormatException('Bundle manifest `locales` must be a list.');
}
for (final entry in rawLocales) {
if (entry is! Map) continue;
final locale = entry['locale'] as String;
final file = entry['file'] as String;
final expected = entry['sha256'] as String;
final content = File(p.join(versionDir, file)).readAsStringSync();
final actual = sha256.convert(utf8.encode(content)).toString();
if (actual != expected) {
throw FormatException(
'Bundle integrity check failed for `$locale` ($file): expected '
'sha256 $expected, got $actual.',
);
}
locales.add(
PulledLocale(
locale: locale,
file: file,
content: content,
keys: entry['keys'] is int ? entry['keys'] as int : null,
),
);
}
return PulledBundle(
version:
manifest['bundle_version'] as String? ?? head['current'] as String,
format: manifest['format'] as String? ?? 'icu-json',
locales: locales,
);
}