blob: c8d67af0c1d30484c9ad94a07527a110195e5061 [file] [log] [blame]
// 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);
String? flutterRevision;
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';
}