Delete unused engine_hash.dart
script (and test), simplify engine_hash.sh
. (#160549)
Closes https://github.com/flutter/flutter/issues/160527. We don't use the Dart script anyway, and the shell script could be simplified to the single use in g3 (cl/688973229).
This commit is contained in:
parent
65ff060283
commit
62c6859e59
@ -2579,6 +2579,7 @@ const Set<String> kExecutableAllowlist = <String>{
|
||||
'dev/tools/repackage_gradle_wrapper.sh',
|
||||
'dev/tools/bin/engine_hash.sh',
|
||||
'dev/tools/format.sh',
|
||||
'dev/tools/test/mock_git.sh',
|
||||
|
||||
'packages/flutter_tools/bin/macos_assemble.sh',
|
||||
'packages/flutter_tools/bin/tool_backend.sh',
|
||||
|
@ -1,154 +0,0 @@
|
||||
// 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.
|
||||
|
||||
// ---------------------------------- NOTE ----------------------------------
|
||||
//
|
||||
// We must keep the logic in this file consistent with the logic in the
|
||||
// `engine_hash.sh` script in the same directory to ensure that Flutter
|
||||
// continues to work across all platforms!
|
||||
//
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:args/args.dart';
|
||||
import 'package:crypto/crypto.dart';
|
||||
|
||||
enum GitRevisionStrategy { mergeBase, head }
|
||||
|
||||
final RegExp _hashRegex = RegExp(r'^([a-fA-F0-9]+)');
|
||||
|
||||
final ArgParser parser =
|
||||
ArgParser()
|
||||
..addOption(
|
||||
'strategy',
|
||||
abbr: 's',
|
||||
allowed: <String>['head', 'mergeBase'],
|
||||
defaultsTo: 'head',
|
||||
allowedHelp: <String, String>{
|
||||
'head': 'hash from git HEAD',
|
||||
'mergeBase': 'hash from the merge-base of HEAD and upstream/master',
|
||||
},
|
||||
)
|
||||
..addFlag('help', abbr: 'h', negatable: false);
|
||||
|
||||
Never printHelp({String? error}) {
|
||||
final Stdout out = error != null ? stderr : stdout;
|
||||
if (error != null) {
|
||||
out.writeln(error);
|
||||
out.writeln();
|
||||
}
|
||||
out.writeln('''
|
||||
Calculate the hash signature for the Flutter Engine
|
||||
${parser.usage}
|
||||
''');
|
||||
exit(error != null ? 1 : 0);
|
||||
}
|
||||
|
||||
Future<int> main(List<String> args) async {
|
||||
final ArgResults arguments;
|
||||
try {
|
||||
arguments = parser.parse(args);
|
||||
} catch (e) {
|
||||
printHelp(error: '$e');
|
||||
}
|
||||
|
||||
if (arguments.wasParsed('help')) {
|
||||
printHelp();
|
||||
}
|
||||
|
||||
final String result;
|
||||
try {
|
||||
result = await engineHash(
|
||||
(List<String> command) =>
|
||||
Process.run(command.first, command.sublist(1), stdoutEncoding: utf8),
|
||||
revisionStrategy: GitRevisionStrategy.values.byName(arguments.option('strategy')!),
|
||||
);
|
||||
} catch (e) {
|
||||
stderr.writeln('Error calculating engine hash: $e');
|
||||
return 1;
|
||||
}
|
||||
|
||||
stdout.writeln(result);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Returns the hash signature for the engine source code.
|
||||
Future<String> engineHash(
|
||||
Future<ProcessResult> Function(List<String> command) runProcess, {
|
||||
GitRevisionStrategy revisionStrategy = GitRevisionStrategy.mergeBase,
|
||||
}) async {
|
||||
// First figure out the hash we're working with
|
||||
final String base;
|
||||
switch (revisionStrategy) {
|
||||
case GitRevisionStrategy.head:
|
||||
base = 'HEAD';
|
||||
case GitRevisionStrategy.mergeBase:
|
||||
final ProcessResult processResult = await runProcess(<String>[
|
||||
'git',
|
||||
'merge-base',
|
||||
'upstream/master',
|
||||
'HEAD',
|
||||
]);
|
||||
|
||||
if (processResult.exitCode != 0) {
|
||||
throw '''
|
||||
Unable to find merge-base hash of the repository:
|
||||
${processResult.stderr}''';
|
||||
}
|
||||
|
||||
final Match? baseHash = _hashRegex.matchAsPrefix(processResult.stdout as String);
|
||||
if (baseHash?.groupCount != 1) {
|
||||
throw '''
|
||||
Unable to parse merge-base hash of the repository
|
||||
${processResult.stdout}''';
|
||||
}
|
||||
base = baseHash![1]!;
|
||||
}
|
||||
|
||||
// List the tree (not the working tree) recursively for the merge-base.
|
||||
// This is important for future filtering of files, but also do not include
|
||||
// the developer's changes / in flight PRs.
|
||||
// The presence `engine` and `DEPS` are signals that you live in a monorepo world.
|
||||
final ProcessResult processResult = await runProcess(<String>[
|
||||
'git',
|
||||
'ls-tree',
|
||||
'-r',
|
||||
base,
|
||||
'engine',
|
||||
'DEPS',
|
||||
]);
|
||||
|
||||
if (processResult.exitCode != 0) {
|
||||
throw '''
|
||||
Unable to list tree
|
||||
${processResult.stderr}''';
|
||||
}
|
||||
|
||||
// Ensure stable line endings so our hash calculation is stable
|
||||
final String lsTree = processResult.stdout as String;
|
||||
if (lsTree.trim().isEmpty) {
|
||||
throw 'Not in a monorepo';
|
||||
}
|
||||
|
||||
final Iterable<String> treeLines = LineSplitter.split(processResult.stdout as String);
|
||||
|
||||
// We could call `git hash-object --stdin` which would just take the input, calculate the size,
|
||||
// and then sha1sum it like: `blob $size\0$string'. However, that can have different line endings.
|
||||
// Instead this is equivalent to:
|
||||
// git ls-tree -r $(git merge-base upstream/main HEAD) | <only newlines> | sha1sum
|
||||
final StreamController<Digest> output = StreamController<Digest>();
|
||||
final ByteConversionSink sink = sha1.startChunkedConversion(output);
|
||||
for (final String line in treeLines) {
|
||||
sink.add(utf8.encode(line));
|
||||
sink.add(<int>[0x0a]);
|
||||
}
|
||||
sink.close();
|
||||
final Digest digest = await output.stream.first;
|
||||
|
||||
return '$digest';
|
||||
}
|
@ -5,70 +5,65 @@
|
||||
|
||||
# ---------------------------------- NOTE ---------------------------------- #
|
||||
#
|
||||
# We must keep the logic in this file consistent with the logic in the
|
||||
# `engine_hash.dart` script in the same directory to ensure that Flutter
|
||||
# continues to work across all platforms!
|
||||
# This file will appear unused within the monorepo. It is used internally
|
||||
# (in google3) as part of the roll process, and care should be put before
|
||||
# making changes.
|
||||
#
|
||||
# See cl/688973229.
|
||||
#
|
||||
# -------------------------------------------------------------------------- #
|
||||
|
||||
# TODO(codefu): Add a test that this always outputs the same hash as
|
||||
# `engine_hash.dart` when the repositories are merged
|
||||
# Needed because if it is set, cd may print the path it changed to.
|
||||
unset CDPATH
|
||||
|
||||
STRATEGY=head
|
||||
|
||||
HELP=$(
|
||||
cat <<EOF
|
||||
Calculate the hash signature for the Flutter Engine\n
|
||||
\t-s|--strategy\t<head,mergeBase>\n
|
||||
\t\tthead: hash from git HEAD\n
|
||||
\t\tmergeBase: hash from the merge-base of HEAD and upstream/master\n
|
||||
EOF
|
||||
# On Mac OS, readlink -f doesn't work, so follow_links traverses the path one
|
||||
# link at a time, and then cds into the link destination and find out where it
|
||||
# ends up.
|
||||
#
|
||||
# The returned filesystem path must be a format usable by Dart's URI parser,
|
||||
# since the Dart command line tool treats its argument as a file URI, not a
|
||||
# filename. For instance, multiple consecutive slashes should be reduced to a
|
||||
# single slash, since double-slashes indicate a URI "authority", and these are
|
||||
# supposed to be filenames. There is an edge case where this will return
|
||||
# multiple slashes: when the input resolves to the root directory. However, if
|
||||
# that were the case, we wouldn't be running this shell, so we don't do anything
|
||||
# about it.
|
||||
#
|
||||
# The function is enclosed in a subshell to avoid changing the working directory
|
||||
# of the caller.
|
||||
function follow_links() (
|
||||
cd -P "$(dirname -- "$1")"
|
||||
file="$PWD/$(basename -- "$1")"
|
||||
while [[ -h "$file" ]]; do
|
||||
cd -P "$(dirname -- "$file")"
|
||||
file="$(readlink -- "$file")"
|
||||
cd -P "$(dirname -- "$file")"
|
||||
file="$PWD/$(basename -- "$file")"
|
||||
done
|
||||
echo "$file"
|
||||
)
|
||||
|
||||
function print_help() {
|
||||
if [ "${1:-0}" -eq 0 ]; then
|
||||
echo -e $HELP
|
||||
exit 0
|
||||
PROG_NAME="$(follow_links "${BASH_SOURCE[0]}")"
|
||||
BIN_DIR="$(cd "${PROG_NAME%/*}" ; pwd -P)"
|
||||
FLUTTER_ROOT="$(cd "${BIN_DIR}/../../.." ; pwd -P)"
|
||||
|
||||
# Allow using a mock git for testing.
|
||||
if [ -z "$GIT" ]; then
|
||||
# By default, use git on PATH.
|
||||
GIT_BIN="git"
|
||||
else
|
||||
echo >&2 -e $HELP
|
||||
exit $1
|
||||
# Use the provide GIT executable.
|
||||
GIT_BIN="$GIT"
|
||||
fi
|
||||
}
|
||||
|
||||
while [[ "$#" -gt 0 ]]; do
|
||||
case $1 in
|
||||
-s | --strategy)
|
||||
STRATEGY="$2"
|
||||
shift # past argument
|
||||
shift # past value
|
||||
;;
|
||||
-h | --help)
|
||||
print_help
|
||||
;;
|
||||
-* | --*)
|
||||
echo >&2 -e "Unknown option $1\n"
|
||||
print_help 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
BASE=HEAD
|
||||
case $STRATEGY in
|
||||
head) ;;
|
||||
mergeBase)
|
||||
BASE=$(git merge-base upstream/master HEAD)
|
||||
;;
|
||||
*)
|
||||
echo >&2 -e "Unknown strategy $1\n"
|
||||
print_help 1
|
||||
;;
|
||||
esac
|
||||
|
||||
LSTREE=$(git ls-tree -r $BASE engine DEPS)
|
||||
if [ ${#LSTREE} -eq 0 ]; then
|
||||
echo >&2 Error calculating engine hash: Not in a monorepo
|
||||
# Test for fusion repository
|
||||
if [ -f "$FLUTTER_ROOT/DEPS" ]; then
|
||||
ENGINE_VERSION=$($GIT_BIN -C "$FLUTTER_ROOT" merge-base HEAD origin/master)
|
||||
elif [ -f "$FLUTTER_ROOT/bin/internal/engine.version" ]; then
|
||||
ENGINE_VERSION=$(cat "$FLUTTER_ROOT/bin/internal/engine.version")
|
||||
else
|
||||
>&2 echo "Not a valid FLUTTER_ROOT: $FLUTTER_ROOT"
|
||||
exit 1
|
||||
else
|
||||
HASH=$(echo "$LSTREE" | sha1sum | head -c 40)
|
||||
echo $HASH
|
||||
fi
|
||||
|
||||
echo $ENGINE_VERSION
|
||||
|
@ -2,98 +2,71 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
@TestOn('posix')
|
||||
library;
|
||||
|
||||
import 'dart:io' as io;
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import '../bin/engine_hash.dart' show GitRevisionStrategy, engineHash;
|
||||
|
||||
/// Tests that `/dev/tools/bin/engine_hash.sh` _appears_ to work.
|
||||
void main() {
|
||||
test('Produces an engine hash for merge-base', () async {
|
||||
final Future<io.ProcessResult> Function(List<String>) runProcess = _fakeProcesses(
|
||||
processes: <FakeProcess>[
|
||||
(
|
||||
exe: 'git',
|
||||
command: 'merge-base',
|
||||
rest: <String>['upstream/master', 'HEAD'],
|
||||
exitCode: 0,
|
||||
stdout: 'abcdef1234',
|
||||
stderr: null,
|
||||
),
|
||||
(
|
||||
exe: 'git',
|
||||
command: 'ls-tree',
|
||||
rest: <String>['-r', 'abcdef1234', 'engine', 'DEPS'],
|
||||
exitCode: 0,
|
||||
stdout: 'one\r\ntwo\r\n',
|
||||
stderr: null,
|
||||
),
|
||||
],
|
||||
);
|
||||
late final io.File engineHashSh;
|
||||
|
||||
final Future<String> result = engineHash(runProcess);
|
||||
|
||||
expect(result, completion('c708d7ef841f7e1748436b8ef5670d0b2de1a227'));
|
||||
setUpAll(() {
|
||||
engineHashSh = io.File(p.join(p.current, 'bin', 'engine_hash.sh'));
|
||||
if (!engineHashSh.existsSync()) {
|
||||
fail('No engine_hash.sh at "${p.absolute(engineHashSh.path)}".');
|
||||
}
|
||||
});
|
||||
|
||||
test('Produces an engine hash for HEAD', () async {
|
||||
final Future<io.ProcessResult> Function(List<String>) runProcess = _fakeProcesses(
|
||||
processes: <FakeProcess>[
|
||||
(
|
||||
exe: 'git',
|
||||
command: 'ls-tree',
|
||||
rest: <String>['-r', 'HEAD', 'engine', 'DEPS'],
|
||||
exitCode: 0,
|
||||
stdout: 'one\ntwo\n',
|
||||
stderr: null,
|
||||
),
|
||||
],
|
||||
);
|
||||
late io.Directory tmpFlutterRoot;
|
||||
|
||||
final Future<String> result = engineHash(
|
||||
runProcess,
|
||||
revisionStrategy: GitRevisionStrategy.head,
|
||||
);
|
||||
setUp(() {
|
||||
tmpFlutterRoot = io.Directory.systemTemp.createTempSync('engine_hash_test.');
|
||||
|
||||
expect(result, completion('c708d7ef841f7e1748436b8ef5670d0b2de1a227'));
|
||||
// Create engine_hash.sh at the same component it would be in the real root.
|
||||
io.Directory(p.join(tmpFlutterRoot.path, 'dev', 'tools', 'bin')).createSync(recursive: true);
|
||||
engineHashSh.copySync(p.join(tmpFlutterRoot.path, 'dev', 'tools', 'bin', 'engine_hash.sh'));
|
||||
|
||||
// Create FLUTTER_ROOT/DEPS.
|
||||
io.File(p.join(tmpFlutterRoot.path, 'DEPS')).createSync();
|
||||
});
|
||||
|
||||
test('Returns error in non-monorepo', () async {
|
||||
final Future<io.ProcessResult> Function(List<String>) runProcess = _fakeProcesses(
|
||||
processes: <FakeProcess>[
|
||||
(
|
||||
exe: 'git',
|
||||
command: 'ls-tree',
|
||||
rest: <String>['-r', 'HEAD', 'engine', 'DEPS'],
|
||||
exitCode: 0,
|
||||
stdout: '',
|
||||
stderr: null,
|
||||
),
|
||||
],
|
||||
);
|
||||
tearDown(() {
|
||||
tmpFlutterRoot.deleteSync(recursive: true);
|
||||
});
|
||||
|
||||
final Future<String> result = engineHash(
|
||||
runProcess,
|
||||
revisionStrategy: GitRevisionStrategy.head,
|
||||
);
|
||||
test('omission of FLUTTER_ROOT/DEPS falls back to engine.version', () {
|
||||
io.File(p.join(tmpFlutterRoot.path, 'bin', 'internal', 'engine.version'))
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync('12345');
|
||||
io.File(p.join(tmpFlutterRoot.path, 'DEPS')).deleteSync();
|
||||
|
||||
expect(result, throwsA('Not in a monorepo'));
|
||||
final io.ProcessResult result = io.Process.runSync(
|
||||
p.join(tmpFlutterRoot.path, 'dev', 'tools', 'bin', 'engine_hash.sh'),
|
||||
<String>[],
|
||||
);
|
||||
expect(result.exitCode, 0, reason: result.stderr.toString());
|
||||
expect(result.stdout, '12345\n');
|
||||
});
|
||||
|
||||
test('uses git -C merge-base HEAD origin/master', () {
|
||||
final io.ProcessResult result = io.Process.runSync(
|
||||
p.join(tmpFlutterRoot.path, 'dev', 'tools', 'bin', 'engine_hash.sh'),
|
||||
<String>[],
|
||||
environment: <String, String>{'GIT': p.join(p.current, 'test', 'mock_git.sh')},
|
||||
);
|
||||
expect(result.exitCode, 0, reason: result.stderr.toString());
|
||||
expect(
|
||||
result.stdout,
|
||||
stringContainsInOrder(<String>[
|
||||
'Mock Git: -C',
|
||||
'engine_hash_test',
|
||||
// This needs to be origin/master if the google3 script is running from a fresh checkout.
|
||||
'merge-base HEAD origin/master',
|
||||
]),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
typedef FakeProcess =
|
||||
({String exe, String command, List<String> rest, dynamic stdout, dynamic stderr, int exitCode});
|
||||
|
||||
Future<io.ProcessResult> Function(List<String>) _fakeProcesses({
|
||||
required List<FakeProcess> processes,
|
||||
}) => (List<String> cmd) async {
|
||||
for (final FakeProcess process in processes) {
|
||||
if (process.exe.endsWith(cmd[0]) &&
|
||||
process.command.endsWith(cmd[1]) &&
|
||||
process.rest.equals(cmd.sublist(2))) {
|
||||
return io.ProcessResult(1, process.exitCode, process.stdout, process.stderr);
|
||||
}
|
||||
}
|
||||
return io.ProcessResult(1, -42, '', '404 command not found: $cmd');
|
||||
};
|
||||
|
6
dev/tools/test/mock_git.sh
Executable file
6
dev/tools/test/mock_git.sh
Executable file
@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
# 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.
|
||||
|
||||
echo "Mock Git: $@"
|
Loading…
x
Reference in New Issue
Block a user