fingerprintInputsDetailed function

Future<({String digest, Map<String, String> fileDigests})> fingerprintInputsDetailed(
  1. Iterable<String> paths, {
  2. Iterable<String> extras = const [],
})

fingerprintInputs plus the per-file digest map, for oka why (D4).

Implementation

Future<({String digest, Map<String, String> fileDigests})>
fingerprintInputsDetailed(
  Iterable<String> paths, {
  Iterable<String> extras = const [],
}) async {
  final sink = _HashSink();
  for (final extra in extras) {
    sink.add('extra:$extra\n');
  }
  final digests = <String, String>{};
  final sorted = paths.toList()..sort();
  for (final path in sorted) {
    final f = File(path);
    if (!f.existsSync()) {
      sink.add('missing:$path\n');
      digests[path] = '<missing>';
      continue;
    }
    final stat = f.statSync();
    if (stat.size <= 1048576) {
      // Small files (sources, manifests, res): hash CONTENT — regenerated
      // files keep stable fingerprints when their bytes are unchanged.
      final digest = sha256.convert(await f.readAsBytes()).toString();
      sink.add('content:$path:$digest\n');
      digests[path] = digest;
    } else {
      // Large artifacts (jars, AARs): size + mtime is stable and cheap.
      final stamp =
          '${stat.size}:${stat.modified.millisecondsSinceEpoch}';
      sink.add('file:$path:$stamp\n');
      digests[path] = stamp;
    }
  }
  return (digest: sink.digest, fileDigests: digests);
}