blob: c8d67af0c1d30484c9ad94a07527a110195e5061 [file] [log] [blame]
Dan Field889e6062020-02-24 14:49:12 -08001// Copyright 2014 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5import 'dart:io';
6
7void _validate(List<String> args) {
8 bool errors = false;
9 if (!File('bin/internal/engine.version').existsSync()) {
10 errors = true;
11 print('This program must be run from the root of your flutter repository.');
12 }
13 if (!File('../engine/src/flutter/DEPS').existsSync()) {
14 errors = true;
15 print('This program assumes the engine directory is a sibling to the flutter repository directory.');
16 }
17 if (args.length != 1) {
18 errors = true;
19 print('This program takes the engine revision as a single argument.');
20 }
21 if (errors) {
22 exit(-1);
23 }
24}
25
26const String engineRepo = '../engine/src/flutter';
27
28Future<void> main(List<String> args) async {
29 _validate(args);
30 await _fetchUpstream();
31 await _fetchUpstream(engineRepo);
Christopher Fujino8654e4a2021-04-23 20:24:03 -070032 String? flutterRevision;
Dan Field889e6062020-02-24 14:49:12 -080033 await for (final FlutterEngineRevision revision in _logEngineVersions()) {
34 if (!await containsRevision(args[0], revision.engineRevision)) {
35 if (flutterRevision == null) {
36 print('Revision not found.');
37 exit(-1);
38 }
39 print('earliest revision: $flutterRevision');
40 print('Tags that contain this engine revision:');
41 print(await _tagsForRevision(flutterRevision));
42 exit(0);
43 }
44 flutterRevision = revision.flutterRevision;
45 }
46}
47
48Future<void> _fetchUpstream([String workingDirectory = '.']) async {
49 print('Fetching remotes for "$workingDirectory" - you may be prompted for SSH credentials by git.');
50 final ProcessResult fetchResult = await Process.run(
51 'git',
52 <String>[
53 'fetch',
54 '--all',
55 ],
56 workingDirectory: workingDirectory,
57 );
58 if (fetchResult.exitCode != 0) {
59 throw Exception('Failed to fetch upstream in repository $workingDirectory');
60 }
61}
62
63Future<String> _tagsForRevision(String flutterRevision) async {
64 final ProcessResult tagResult = await Process.run(
65 'git',
66 <String>[
67 'tag',
68 '--contains',
69 flutterRevision,
70 ],
71 );
72 return tagResult.stdout as String;
73}
74
75Future<bool> containsRevision(String ancestorRevision, String revision) async {
76 final ProcessResult result = await Process.run(
77 'git',
78 <String>[
79 'merge-base',
80 '--is-ancestor',
81 ancestorRevision,
82 revision,
83 ],
84 workingDirectory: engineRepo,
85 );
86 return result.exitCode == 0;
87}
88
89Stream<FlutterEngineRevision> _logEngineVersions() async* {
90 final ProcessResult result = await Process.run(
91 'git',
92 <String>[
93 'log',
94 '--oneline',
95 '-p',
96 '--',
97 'bin/internal/engine.version',
98 ],
99 );
100 if (result.exitCode != 0) {
101 print(result.stderr);
102 throw Exception('Failed to log bin/internal/engine.version');
103 }
104
105 final List<String> lines = (result.stdout as String).split('\n');
106 int index = 0;
107 while (index < lines.length - 1) {
108 final String flutterRevision = lines[index].split(' ').first;
109 index += 1;
110 while (!lines[index].startsWith('+') || lines[index].startsWith('+++')) {
111 index += 1;
112 }
113 if (index >= lines.length) {
114 break;
115 }
116 final String engineRevision = lines[index].substring(1);
117 yield FlutterEngineRevision(flutterRevision, engineRevision);
118 index += lines[index + 1].startsWith(r'\ ') ? 2 : 1;
119 }
120}
121
122class FlutterEngineRevision {
123 const FlutterEngineRevision(this.flutterRevision, this.engineRevision);
124
125 final String flutterRevision;
126 final String engineRevision;
127
128 @override
129 String toString() => '$flutterRevision: $engineRevision';
130}