find_engine_commit.dart 3.54 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:io';

void _validate(List<String> args) {
  bool errors = false;
  if (!File('bin/internal/engine.version').existsSync()) {
    errors = true;
    print('This program must be run from the root of your flutter repository.');
  }
  if (!File('../engine/src/flutter/DEPS').existsSync()) {
    errors = true;
    print('This program assumes the engine directory is a sibling to the flutter repository directory.');
  }
  if (args.length != 1) {
    errors = true;
    print('This program takes the engine revision as a single argument.');
  }
  if (errors) {
    exit(-1);
  }
}

const String engineRepo = '../engine/src/flutter';

Future<void> main(List<String> args) async {
  _validate(args);
  await _fetchUpstream();
  await _fetchUpstream(engineRepo);
32
  String? flutterRevision;
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
  await for (final FlutterEngineRevision revision in _logEngineVersions()) {
    if (!await containsRevision(args[0], revision.engineRevision)) {
      if (flutterRevision == null) {
        print('Revision not found.');
        exit(-1);
      }
      print('earliest revision: $flutterRevision');
      print('Tags that contain this engine revision:');
      print(await _tagsForRevision(flutterRevision));
      exit(0);
    }
    flutterRevision = revision.flutterRevision;
  }
}

Future<void> _fetchUpstream([String workingDirectory = '.']) async {
  print('Fetching remotes for "$workingDirectory" - you may be prompted for SSH credentials by git.');
  final ProcessResult fetchResult = await Process.run(
    'git',
    <String>[
      'fetch',
      '--all',
    ],
    workingDirectory: workingDirectory,
  );
  if (fetchResult.exitCode != 0) {
    throw Exception('Failed to fetch upstream in repository $workingDirectory');
  }
}

Future<String> _tagsForRevision(String flutterRevision) async {
  final ProcessResult tagResult = await Process.run(
    'git',
    <String>[
      'tag',
      '--contains',
      flutterRevision,
    ],
  );
  return tagResult.stdout as String;
}

Future<bool> containsRevision(String ancestorRevision, String revision) async {
  final ProcessResult result = await Process.run(
    'git',
    <String>[
      'merge-base',
      '--is-ancestor',
      ancestorRevision,
      revision,
    ],
    workingDirectory: engineRepo,
  );
  return result.exitCode == 0;
}

Stream<FlutterEngineRevision> _logEngineVersions() async* {
  final ProcessResult result = await Process.run(
    'git',
    <String>[
      'log',
      '--oneline',
      '-p',
      '--',
      'bin/internal/engine.version',
    ],
  );
  if (result.exitCode != 0) {
    print(result.stderr);
    throw Exception('Failed to log bin/internal/engine.version');
  }

  final List<String> lines = (result.stdout as String).split('\n');
  int index = 0;
  while (index < lines.length - 1) {
    final String flutterRevision = lines[index].split(' ').first;
    index += 1;
    while (!lines[index].startsWith('+') || lines[index].startsWith('+++')) {
      index += 1;
    }
    if (index >= lines.length) {
      break;
    }
    final String engineRevision = lines[index].substring(1);
    yield FlutterEngineRevision(flutterRevision, engineRevision);
    index += lines[index + 1].startsWith(r'\ ') ? 2 : 1;
  }
}

class FlutterEngineRevision {
  const FlutterEngineRevision(this.flutterRevision, this.engineRevision);

  final String flutterRevision;
  final String engineRevision;

  @override
  String toString() => '$flutterRevision: $engineRevision';
}