Revert "Improve codesign script (#71244)" (#73961)

This reverts commit b7f5aef11a.
This commit is contained in:
Christopher Fujino 2021-01-14 18:40:29 +00:00 committed by GitHub
parent b7f5aef11a
commit 66ba4b244d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 385 additions and 1181 deletions

View file

@ -2,24 +2,184 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:io' as io;
import 'dart:io';
import 'package:path/path.dart' as path;
// TODO(fujino): delete this script once PR #71244 lands on stable.
void main(List<String> args) {
final String scriptPath = io.Platform.script.toFilePath();
final String scriptDir = path.dirname(scriptPath);
final String repoRoot = path.normalize(path.join(scriptDir, '..', '..'));
final io.ProcessResult result = io.Process.runSync(
path.join(repoRoot, 'dev', 'tools', 'bin', 'conductor'),
<String>['codesign', '--verify'],
String get repoRoot => path.normalize(path.join(path.dirname(Platform.script.toFilePath()), '..', '..'));
String get cacheDirectory => path.normalize(path.join(repoRoot, 'bin', 'cache'));
/// Check mime-type of file at [filePath] to determine if it is binary
bool isBinary(String filePath) {
final ProcessResult result = Process.runSync(
'file',
<String>[
'--mime-type',
'-b', // is binary
filePath,
],
);
if (result.exitCode != 0) {
print('codesign script exited with code $result.exitCode');
print('stdout:\n${result.stdout}\n');
print('stderr:\n${result.stderr}\n');
io.exit(1);
}
print('codesign script succeeded.');
print('stdout:\n${result.stdout}');
return (result.stdout as String).contains('application/x-mach-binary');
}
/// Find every binary file in the given [rootDirectory]
List<String> findBinaryPaths([String rootDirectory]) {
rootDirectory ??= cacheDirectory;
final ProcessResult result = Process.runSync(
'find',
<String>[
rootDirectory,
'-type',
'f',
'-perm',
'+111', // is executable
],
);
final List<String> allFiles = (result.stdout as String).split('\n').where((String s) => s.isNotEmpty).toList();
return allFiles.where(isBinary).toList();
}
/// Given the path to a stamp file, read the contents.
///
/// Will throw if the file doesn't exist.
String readStamp(String filePath) {
final File file = File(filePath);
if (!file.existsSync()) {
throw 'Error! Stamp file $filePath does not exist!';
}
return file.readAsStringSync().trim();
}
/// Return whether or not the flutter cache is up to date.
bool checkCacheIsCurrent() {
try {
final String dartSdkStamp = readStamp(path.join(cacheDirectory, 'engine-dart-sdk.stamp'));
final String engineVersion = readStamp(path.join(repoRoot, 'bin', 'internal', 'engine.version'));
return dartSdkStamp == engineVersion;
} catch (e) {
print(e);
return false;
}
}
List<String> get binariesWithEntitlements => List<String>.unmodifiable(<String>[
'ideviceinfo',
'idevicename',
'idevicescreenshot',
'idevicesyslog',
'libimobiledevice.6.dylib',
'libplist.3.dylib',
'iproxy',
'libusbmuxd.4.dylib',
'libssl.1.0.0.dylib',
'libcrypto.1.0.0.dylib',
'libzip.5.0.dylib',
'libzip.5.dylib',
'gen_snapshot',
'dart',
'flutter_tester',
'gen_snapshot_arm64',
'gen_snapshot_armv7',
]);
List<String> get expectedEntitlements => List<String>.unmodifiable(<String>[
'com.apple.security.cs.allow-jit',
'com.apple.security.cs.allow-unsigned-executable-memory',
'com.apple.security.cs.allow-dyld-environment-variables',
'com.apple.security.network.client',
'com.apple.security.network.server',
'com.apple.security.cs.disable-library-validation',
]);
/// Check if the binary has the expected entitlements.
bool hasExpectedEntitlements(String binaryPath) {
try {
final ProcessResult entitlementResult = Process.runSync(
'codesign',
<String>[
'--display',
'--entitlements',
':-',
binaryPath,
],
);
if (entitlementResult.exitCode != 0) {
print('The `codesign --entitlements` command failed with exit code ${entitlementResult.exitCode}:\n'
'${entitlementResult.stderr}\n');
return false;
}
bool passes = true;
final String output = entitlementResult.stdout as String;
for (final String entitlement in expectedEntitlements) {
final bool entitlementExpected = binariesWithEntitlements.contains(path.basename(binaryPath));
if (output.contains(entitlement) != entitlementExpected) {
print('File "$binaryPath" ${entitlementExpected ? 'does not have expected' : 'has unexpected'} entitlement $entitlement.');
passes = false;
}
}
return passes;
} catch (e) {
print(e);
return false;
}
}
void main() {
if (!Platform.isMacOS) {
print('Error! Expected operating system "macos", actual operating system '
'is: "${Platform.operatingSystem}"');
exit(1);
}
if (!checkCacheIsCurrent()) {
print(
'Warning! Your cache is either not present or not matching your flutter\n'
'version. Run a `flutter` command to update your cache, and re-try this\n'
'test.');
exit(1);
}
final List<String> unsignedBinaries = <String>[];
final List<String> wrongEntitlementBinaries = <String>[];
for (final String binaryPath in findBinaryPaths(cacheDirectory)) {
print('Verifying the code signature of $binaryPath');
final ProcessResult codeSignResult = Process.runSync(
'codesign',
<String>[
'-vvv',
binaryPath,
],
);
if (codeSignResult.exitCode != 0) {
unsignedBinaries.add(binaryPath);
print('File "$binaryPath" does not appear to be codesigned.\n'
'The `codesign` command failed with exit code ${codeSignResult.exitCode}:\n'
'${codeSignResult.stderr}\n');
continue;
} else {
print('Verifying entitlements of $binaryPath');
if (!hasExpectedEntitlements(binaryPath)) {
wrongEntitlementBinaries.add(binaryPath);
}
}
}
if (unsignedBinaries.isNotEmpty) {
print('Found ${unsignedBinaries.length} unsigned binaries:');
unsignedBinaries.forEach(print);
}
if (wrongEntitlementBinaries.isNotEmpty) {
print('Found ${wrongEntitlementBinaries.length} binaries with unexpected entitlements:');
wrongEntitlementBinaries.forEach(print);
}
if (unsignedBinaries.isNotEmpty) {
// TODO(jmagman): Also exit if `wrongEntitlementBinaries.isNotEmpty` after https://github.com/flutter/flutter/issues/46704 is done.
exit(1);
}
print('Verified that binaries are codesigned and have expected entitlements.');
}

View file

@ -694,7 +694,7 @@ Future<void> _runFrameworkTests() async {
await _pubRunTest(path.join(flutterRoot, 'dev', 'bots'), tableData: bigqueryApi?.tabledata);
await _pubRunTest(path.join(flutterRoot, 'dev', 'devicelab'), tableData: bigqueryApi?.tabledata);
await _pubRunTest(path.join(flutterRoot, 'dev', 'snippets'), tableData: bigqueryApi?.tabledata);
await _pubRunTest(path.join(flutterRoot, 'dev', 'tools'), tableData: bigqueryApi?.tabledata, forceSingleCore: true);
await _pubRunTest(path.join(flutterRoot, 'dev', 'tools'), tableData: bigqueryApi?.tabledata);
await _pubRunTest(path.join(flutterRoot, 'dev', 'benchmarks', 'metrics_center'), tableData: bigqueryApi?.tabledata);
await _runFlutterTest(path.join(flutterRoot, 'dev', 'integration_tests', 'android_semantics_testing'), tableData: bigqueryApi?.tabledata);
await _runFlutterTest(path.join(flutterRoot, 'dev', 'manual_tests'), tableData: bigqueryApi?.tabledata);

View file

@ -10,17 +10,15 @@
import 'dart:io' as io;
import 'package:args/command_runner.dart';
import 'package:dev_tools/codesign.dart';
import 'package:dev_tools/globals.dart';
import 'package:dev_tools/roll_dev.dart';
import 'package:dev_tools/repository.dart';
import 'package:dev_tools/stdio.dart';
import 'package:file/file.dart';
import 'package:file/local.dart';
import 'package:platform/platform.dart';
import 'package:process/process.dart';
import 'package:dev_tools/repository.dart';
import 'package:dev_tools/roll_dev.dart';
import 'package:dev_tools/stdio.dart';
Future<void> main(List<String> args) async {
void main(List<String> args) {
const FileSystem fileSystem = LocalFileSystem();
const ProcessManager processManager = LocalProcessManager();
const Platform platform = LocalPlatform();
@ -31,12 +29,9 @@ Future<void> main(List<String> args) async {
);
final Checkouts checkouts = Checkouts(
fileSystem: fileSystem,
parentDirectory: localFlutterRoot.parent,
platform: platform,
processManager: processManager,
stdio: stdio,
);
final CommandRunner<void> runner = CommandRunner<void>(
'conductor',
'A tool for coordinating Flutter releases.',
@ -44,16 +39,17 @@ Future<void> main(List<String> args) async {
);
<Command<void>>[
RollDevCommand(
checkouts: checkouts,
RollDev(
fileSystem: fileSystem,
platform: platform,
repository: checkouts.addRepo(
fileSystem: fileSystem,
platform: platform,
repoType: RepositoryType.framework,
stdio: stdio,
),
stdio: stdio,
),
CodesignCommand(
checkouts: checkouts,
flutterRoot: localFlutterRoot,
),
].forEach(runner.addCommand);
if (!assertsEnabled()) {
@ -62,9 +58,20 @@ Future<void> main(List<String> args) async {
}
try {
await runner.run(args);
runner.run(args);
} on Exception catch (e) {
stdio.printError(e.toString());
io.exit(1);
}
}
bool assertsEnabled() {
// Verify asserts enabled
bool assertsEnabled = false;
assert(() {
assertsEnabled = true;
return true;
}());
return assertsEnabled;
}

View file

@ -1,369 +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.
import 'dart:io' as io;
import 'package:args/command_runner.dart';
import 'package:file/file.dart';
import 'package:meta/meta.dart';
import 'package:platform/platform.dart';
import 'package:process/process.dart';
import './globals.dart';
import './repository.dart';
import './stdio.dart';
const List<String> expectedEntitlements = <String>[
'com.apple.security.cs.allow-jit',
'com.apple.security.cs.allow-unsigned-executable-memory',
'com.apple.security.cs.allow-dyld-environment-variables',
'com.apple.security.network.client',
'com.apple.security.network.server',
'com.apple.security.cs.disable-library-validation',
];
const String kVerify = 'verify';
const String kSignatures = 'signatures';
const String kRevision = 'revision';
const String kUpstream = 'upstream';
/// Command to codesign and verify the signatures of cached binaries.
class CodesignCommand extends Command<void> {
CodesignCommand({
@required this.checkouts,
@required this.flutterRoot,
}) : assert(flutterRoot != null),
fileSystem = checkouts.fileSystem,
platform = checkouts.platform,
stdio = checkouts.stdio,
processManager = checkouts.processManager {
argParser.addFlag(
kVerify,
help:
'Only verify expected binaries exist and are codesigned with entitlements.',
);
argParser.addFlag(
kSignatures,
defaultsTo: true,
help:
'When off, this command will only verify the existence of binaries, and not their\n'
'signatures or entitlements. Must be used with --verify flag.',
);
argParser.addOption(
kUpstream,
defaultsTo: FrameworkRepository.defaultUpstream,
help: 'The git remote URL to use as the Flutter framework\'s upstream.',
);
argParser.addOption(
kRevision,
help: 'The Flutter framework revision to use.',
);
}
final Checkouts checkouts;
final FileSystem fileSystem;
final Platform platform;
final ProcessManager processManager;
final Stdio stdio;
/// Root directory of the Flutter repository.
final Directory flutterRoot;
FrameworkRepository _framework;
FrameworkRepository get framework => _framework ??= FrameworkRepository.localRepoAsUpstream(
checkouts,
upstreamPath: flutterRoot.path,
);
@visibleForTesting
set framework(FrameworkRepository framework) => _framework = framework;
@override
String get name => 'codesign';
@override
String get description =>
'For codesigning and verifying the signatures of engine binaries.';
@override
void run() {
if (!platform.isMacOS) {
throw ConductorException(
'Error! Expected operating system "macos", actual operating system is: '
'"${platform.operatingSystem}"');
}
if (argResults['verify'] as bool != true) {
throw ConductorException(
'Sorry, but codesigning is not implemented yet. Please pass the '
'--$kVerify flag to verify signatures.');
}
String revision;
if (argResults.wasParsed(kRevision)) {
stdio.printError('Warning! When providing an arbitrary revision, the contents of the cache may not');
stdio.printError('match the expected binaries in the conductor tool. It is preferred to check out');
stdio.printError('the desired revision and run that version of the conductor.\n');
revision = argResults[kRevision] as String;
} else {
revision = (processManager.runSync(
<String>['git', 'rev-parse', 'HEAD'],
workingDirectory: framework.checkoutDirectory.path,
).stdout as String).trim();
assert(revision.isNotEmpty);
}
framework.checkout(revision);
// Ensure artifacts present
framework.runFlutter(<String>['precache', '--ios', '--macos']);
verifyExist();
if (argResults[kSignatures] as bool) {
verifySignatures();
}
}
/// Binaries that are expected to be codesigned and have entitlements.
///
/// This list should be kept in sync with the actual contents of Flutter's
/// cache.
List<String> get binariesWithEntitlements {
return <String>[
'artifacts/engine/android-arm-profile/darwin-x64/gen_snapshot',
'artifacts/engine/android-arm-release/darwin-x64/gen_snapshot',
'artifacts/engine/android-arm64-profile/darwin-x64/gen_snapshot',
'artifacts/engine/android-arm64-release/darwin-x64/gen_snapshot',
'artifacts/engine/android-x64-profile/darwin-x64/gen_snapshot',
'artifacts/engine/android-x64-release/darwin-x64/gen_snapshot',
'artifacts/engine/darwin-x64-profile/gen_snapshot',
'artifacts/engine/darwin-x64-release/gen_snapshot',
'artifacts/engine/darwin-x64/flutter_tester',
'artifacts/engine/darwin-x64/gen_snapshot',
'artifacts/engine/ios-profile/gen_snapshot_arm64',
'artifacts/engine/ios-profile/gen_snapshot_armv7',
'artifacts/engine/ios-release/gen_snapshot_arm64',
'artifacts/engine/ios-release/gen_snapshot_armv7',
'artifacts/engine/ios/gen_snapshot_arm64',
'artifacts/engine/ios/gen_snapshot_armv7',
'artifacts/ios-deploy/ios-deploy',
'artifacts/libimobiledevice/idevicescreenshot',
'artifacts/libimobiledevice/idevicesyslog',
'artifacts/libimobiledevice/libimobiledevice-1.0.6.dylib',
'artifacts/libplist/libplist-2.0.3.dylib',
'artifacts/openssl/libcrypto.1.1.dylib',
'artifacts/openssl/libssl.1.1.dylib',
'artifacts/usbmuxd/iproxy',
'artifacts/usbmuxd/libusbmuxd-2.0.6.dylib',
'dart-sdk/bin/dart',
'dart-sdk/bin/dartaotruntime',
'dart-sdk/bin/utils/gen_snapshot',
].map((String relativePath) => fileSystem.path.join(framework.cacheDirectory, relativePath)).toList();
}
/// Binaries that are only expected to be codesigned.
///
/// This list should be kept in sync with the actual contents of Flutter's
/// cache.
List<String> get binariesWithoutEntitlements {
return <String>[
'artifacts/engine/darwin-x64-profile/FlutterMacOS.framework/Versions/A/FlutterMacOS',
'artifacts/engine/darwin-x64-release/FlutterMacOS.framework/Versions/A/FlutterMacOS',
'artifacts/engine/darwin-x64/FlutterMacOS.framework/Versions/A/FlutterMacOS',
'artifacts/engine/darwin-x64/font-subset',
'artifacts/engine/ios-profile/Flutter.xcframework/ios-armv7_arm64/Flutter.framework/Flutter',
'artifacts/engine/ios-profile/Flutter.xcframework/ios-x86_64-simulator/Flutter.framework/Flutter',
'artifacts/engine/ios-release/Flutter.xcframework/ios-armv7_arm64/Flutter.framework/Flutter',
'artifacts/engine/ios-release/Flutter.xcframework/ios-x86_64-simulator/Flutter.framework/Flutter',
'artifacts/engine/ios/Flutter.xcframework/ios-armv7_arm64/Flutter.framework/Flutter',
'artifacts/engine/ios/Flutter.xcframework/ios-x86_64-simulator/Flutter.framework/Flutter',
].map((String relativePath) => fileSystem.path.join(framework.cacheDirectory, relativePath)).toList();
}
/// Verify the existence of all expected binaries in cache.
///
/// This function ignores code signatures and entitlements, and is intended to
/// be run on every commit. It should throw if either new binaries are added
/// to the cache or expected binaries removed. In either case, this class'
/// [binariesWithEntitlements] or [binariesWithoutEntitlements] lists should
/// be updated accordingly.
@visibleForTesting
void verifyExist() {
final Set<String> foundFiles = <String>{};
for (final String binaryPath in findBinaryPaths(framework.cacheDirectory)) {
if (binariesWithEntitlements.contains(binaryPath)) {
foundFiles.add(binaryPath);
} else if (binariesWithoutEntitlements.contains(binaryPath)) {
foundFiles.add(binaryPath);
} else {
throw ConductorException('Found unexpected binary in cache: $binaryPath');
}
}
final List<String> allExpectedFiles = binariesWithEntitlements + binariesWithoutEntitlements;
if (foundFiles.length < allExpectedFiles.length) {
final List<String> unfoundFiles = allExpectedFiles.where(
(String file) => !foundFiles.contains(file),
).toList();
stdio.printError('Expected binaries not found in cache:\n\n${unfoundFiles.join('\n')}\n');
stdio.printError('If this commit is removing binaries from the cache, this test should be fixed by');
stdio.printError('removing the relevant entry from either the `binariesWithEntitlements` or');
stdio.printError('`binariesWithoutEntitlements` getters in dev/tools/lib/codesign.dart.');
throw ConductorException('Did not find all expected binaries!');
}
stdio.printStatus('All expected binaries present.');
}
/// Verify code signatures and entitlements of all binaries in the cache.
@visibleForTesting
void verifySignatures() {
final List<String> unsignedBinaries = <String>[];
final List<String> wrongEntitlementBinaries = <String>[];
final List<String> unexpectedBinaries = <String>[];
for (final String binaryPath in findBinaryPaths(framework.cacheDirectory)) {
bool verifySignature = false;
bool verifyEntitlements = false;
if (binariesWithEntitlements.contains(binaryPath)) {
verifySignature = true;
verifyEntitlements = true;
}
if (binariesWithoutEntitlements.contains(binaryPath)) {
verifySignature = true;
}
if (!verifySignature && !verifyEntitlements) {
unexpectedBinaries.add(binaryPath);
stdio.printError('Unexpected binary $binaryPath found in cache!');
continue;
}
stdio.printTrace('Verifying the code signature of $binaryPath');
final io.ProcessResult codeSignResult = processManager.runSync(
<String>[
'codesign',
'-vvv',
binaryPath,
],
);
if (codeSignResult.exitCode != 0) {
unsignedBinaries.add(binaryPath);
stdio.printError(
'File "$binaryPath" does not appear to be codesigned.\n'
'The `codesign` command failed with exit code ${codeSignResult.exitCode}:\n'
'${codeSignResult.stderr}\n');
continue;
}
if (verifyEntitlements) {
stdio.printTrace('Verifying entitlements of $binaryPath');
if (!hasExpectedEntitlements(binaryPath)) {
wrongEntitlementBinaries.add(binaryPath);
}
}
}
// First print all deviations from expectations
if (unsignedBinaries.isNotEmpty) {
stdio.printError('Found ${unsignedBinaries.length} unsigned binaries:');
unsignedBinaries.forEach(print);
}
if (wrongEntitlementBinaries.isNotEmpty) {
stdio.printError(
'Found ${wrongEntitlementBinaries.length} binaries with unexpected entitlements:');
wrongEntitlementBinaries.forEach(print);
}
if (unexpectedBinaries.isNotEmpty) {
stdio.printError('Found ${unexpectedBinaries.length} unexpected binaries in the cache:');
unexpectedBinaries.forEach(print);
}
// Finally, exit on any invalid state
if (unsignedBinaries.isNotEmpty) {
throw ConductorException('Test failed because unsigned binaries detected.');
}
if (wrongEntitlementBinaries.isNotEmpty) {
throw ConductorException(
'Test failed because files found with the wrong entitlements:\n'
'${wrongEntitlementBinaries.join('\n')}');
}
if (unexpectedBinaries.isNotEmpty) {
throw ConductorException('Test failed because unexpected binaries found in the cache.');
}
stdio.printStatus(
'Verified that binaries for commit ${argResults[kRevision] as String} are codesigned and have '
'expected entitlements.');
}
List<String> _allBinaryPaths;
/// Find every binary file in the given [rootDirectory].
List<String> findBinaryPaths(String rootDirectory) {
if (_allBinaryPaths != null) {
return _allBinaryPaths;
}
final io.ProcessResult result = processManager.runSync(
<String>[
'find',
rootDirectory,
'-type',
'f',
],
);
final List<String> allFiles = (result.stdout as String)
.split('\n')
.where((String s) => s.isNotEmpty)
.toList();
_allBinaryPaths = allFiles.where(isBinary).toList();
return _allBinaryPaths;
}
/// Check mime-type of file at [filePath] to determine if it is binary.
bool isBinary(String filePath) {
final io.ProcessResult result = processManager.runSync(
<String>[
'file',
'--mime-type',
'-b', // is binary
filePath,
],
);
return (result.stdout as String).contains('application/x-mach-binary');
}
/// Check if the binary has the expected entitlements.
bool hasExpectedEntitlements(String binaryPath) {
final io.ProcessResult entitlementResult = processManager.runSync(
<String>[
'codesign',
'--display',
'--entitlements',
':-',
binaryPath,
],
);
if (entitlementResult.exitCode != 0) {
stdio.printError(
'The `codesign --entitlements` command failed with exit code ${entitlementResult.exitCode}:\n'
'${entitlementResult.stderr}\n');
return false;
}
bool passes = true;
final String output = entitlementResult.stdout as String;
for (final String entitlement in expectedEntitlements) {
final bool entitlementExpected = binariesWithEntitlements.contains(binaryPath);
if (output.contains(entitlement) != entitlementExpected) {
stdio.printError(
'File "$binaryPath" ${entitlementExpected ? 'does not have expected' : 'has unexpected'} '
'entitlement $entitlement.');
passes = false;
}
}
return passes;
}
}

View file

@ -45,7 +45,6 @@ class Git {
return processManager.runSync(
<String>['git', ...args],
workingDirectory: workingDirectory,
environment: <String, String>{'GIT_TRACE': '1'},
);
}

View file

@ -2,10 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:file/file.dart';
import 'package:file/local.dart';
import 'package:platform/platform.dart';
const String kIncrement = 'increment';
const String kCommit = 'commit';
const String kRemoteName = 'remote';
@ -28,61 +24,3 @@ String stdoutToString(dynamic input) {
final String str = input as String;
return str.trim();
}
class ConductorException implements Exception {
ConductorException(this.message);
final String message;
@override
String toString() => 'Exception: $message';
}
Directory _flutterRoot;
Directory get localFlutterRoot {
if (_flutterRoot != null) {
return _flutterRoot;
}
String filePath;
const FileSystem fileSystem = LocalFileSystem();
const Platform platform = LocalPlatform();
// If a test
if (platform.script.scheme == 'data') {
final RegExp pattern = RegExp(
r'(file:\/\/[^"]*[/\\]dev\/tools[/\\][^"]+\.dart)',
multiLine: true,
);
final Match match =
pattern.firstMatch(Uri.decodeFull(platform.script.path));
if (match == null) {
throw Exception(
'Cannot determine path of script!\n${platform.script.path}',
);
}
filePath = Uri.parse(match.group(1)).path.replaceAll(r'%20', ' ');
} else {
filePath = platform.script.toFilePath();
}
final String checkoutsDirname = fileSystem.path.normalize(
fileSystem.path.join(
fileSystem.path.dirname(filePath),
'..', // flutter/dev/tools
'..', // flutter/dev
'..', // flutter
),
);
_flutterRoot = fileSystem.directory(checkoutsDirname);
return _flutterRoot;
}
bool assertsEnabled() {
// Verify asserts enabled
bool assertsEnabled = false;
assert(() {
assertsEnabled = true;
return true;
}());
return assertsEnabled;
}

View file

@ -16,7 +16,7 @@ import './stdio.dart';
import './version.dart';
/// A source code repository.
abstract class Repository {
class Repository {
Repository({
@required this.name,
@required this.upstream,
@ -46,35 +46,22 @@ abstract class Repository {
Directory _checkoutDirectory;
/// Directory for the repository checkout.
/// Lazily-loaded directory for the repository checkout.
///
/// Since cloning a repository takes a long time, we do not ensure it is
/// cloned on the filesystem until this getter is accessed.
/// Cloning a repository is time-consuming, thus the repository is not cloned
/// until this getter is called.
Directory get checkoutDirectory {
if (_checkoutDirectory != null) {
return _checkoutDirectory;
}
_checkoutDirectory = parentDirectory.childDirectory(name);
if (!useExistingCheckout && _checkoutDirectory.existsSync()) {
stdio.printTrace('Deleting $name from ${_checkoutDirectory.path}...');
_checkoutDirectory.deleteSync(recursive: true);
} else if (useExistingCheckout && _checkoutDirectory.existsSync()) {
git.run(
<String>['checkout', 'master'],
'Checkout to master branch',
workingDirectory: _checkoutDirectory.path,
);
git.run(
<String>['pull', '--ff-only'],
'Updating $name repo',
workingDirectory: _checkoutDirectory.path,
);
if (checkoutDirectory.existsSync() && !useExistingCheckout) {
deleteDirectory();
}
if (!_checkoutDirectory.existsSync()) {
stdio.printTrace(
'Cloning $name from $upstream to ${_checkoutDirectory.path}...');
if (!checkoutDirectory.existsSync()) {
stdio.printTrace('Cloning $name to ${checkoutDirectory.path}...');
git.run(
<String>['clone', '--', upstream, _checkoutDirectory.path],
<String>['clone', '--', upstream, checkoutDirectory.path],
'Cloning $name repo',
workingDirectory: parentDirectory.path,
);
@ -85,18 +72,29 @@ abstract class Repository {
git.run(
<String>['checkout', channel, '--'],
'check out branch $channel locally',
workingDirectory: _checkoutDirectory.path,
workingDirectory: checkoutDirectory.path,
);
}
}
} else {
stdio.printTrace(
'Using existing $name repo at ${checkoutDirectory.path}...',
);
}
final String revision = reverseParse('HEAD');
stdio
.printTrace('Repository $name is checked out at revision "$revision".');
return _checkoutDirectory;
}
void deleteDirectory() {
if (!checkoutDirectory.existsSync()) {
stdio.printTrace(
'Tried to delete ${checkoutDirectory.path} but it does not exist.',
);
return;
}
stdio.printTrace('Deleting $name from ${checkoutDirectory.path}...');
checkoutDirectory.deleteSync(recursive: true);
}
/// The URL of the remote named [remoteName].
String remoteUrl(String remoteName) {
assert(remoteName != null);
@ -126,14 +124,6 @@ abstract class Repository {
);
}
void checkout(String revision) {
git.run(
<String>['checkout', revision],
'checkout $revision',
workingDirectory: checkoutDirectory.path,
);
}
/// Obtain the version tag of the previous dev release.
String getFullTag(String remoteName) {
const String glob = '*.*.*-*.*.pre';
@ -152,7 +142,7 @@ abstract class Repository {
<String>['rev-parse', ref],
'look up the commit for the ref $ref',
workingDirectory: checkoutDirectory.path,
).trim();
);
assert(revisionHash.isNotEmpty);
return revisionHash;
}
@ -226,6 +216,24 @@ abstract class Repository {
);
}
Version flutterVersion() {
// Build tool
processManager.runSync(<String>[
fileSystem.path.join(checkoutDirectory.path, 'bin', 'flutter'),
'help',
]);
// Check version
final io.ProcessResult result = processManager.runSync(<String>[
fileSystem.path.join(checkoutDirectory.path, 'bin', 'flutter'),
'--version',
'--machine',
]);
final Map<String, dynamic> versionJson = jsonDecode(
globals.stdoutToString(result.stdout),
) as Map<String, dynamic>;
return Version.fromString(versionJson['frameworkVersion'] as String);
}
/// Create an empty commit and return the revision.
@visibleForTesting
String authorEmptyCommit([String message = 'An empty commit']) {
@ -253,120 +261,20 @@ abstract class Repository {
///
/// This method is for testing purposes.
@visibleForTesting
Repository cloneRepository(String cloneName);
}
class FrameworkRepository extends Repository {
FrameworkRepository(
this.checkouts, {
String name = 'framework',
String upstream = FrameworkRepository.defaultUpstream,
bool localUpstream = false,
bool useExistingCheckout = false,
}) : super(
name: name,
upstream: upstream,
fileSystem: checkouts.fileSystem,
localUpstream: localUpstream,
parentDirectory: checkouts.directory,
platform: checkouts.platform,
processManager: checkouts.processManager,
stdio: checkouts.stdio,
useExistingCheckout: useExistingCheckout,
);
/// A [FrameworkRepository] with the host conductor's repo set as upstream.
///
/// This is useful when testing a commit that has not been merged upstream
/// yet.
factory FrameworkRepository.localRepoAsUpstream(
Checkouts checkouts, {
String name = 'framework',
bool useExistingCheckout = false,
@required String upstreamPath,
}) {
return FrameworkRepository(
checkouts,
name: name,
upstream: 'file://$upstreamPath/',
localUpstream: false,
useExistingCheckout: useExistingCheckout,
);
}
final Checkouts checkouts;
static const String defaultUpstream =
'https://github.com/flutter/flutter.git';
String get cacheDirectory => fileSystem.path.join(
checkoutDirectory.path,
'bin',
'cache',
);
@override
Repository cloneRepository(String cloneName) {
assert(localUpstream);
cloneName ??= 'clone-of-$name';
return FrameworkRepository(
checkouts,
return Repository(
fileSystem: fileSystem,
name: cloneName,
parentDirectory: parentDirectory,
platform: platform,
processManager: processManager,
stdio: stdio,
upstream: 'file://${checkoutDirectory.path}/',
useExistingCheckout: useExistingCheckout,
);
}
void _ensureToolReady() {
final File toolsStamp =
fileSystem.directory(cacheDirectory).childFile('flutter_tools.stamp');
if (toolsStamp.existsSync()) {
final String toolsStampHash = toolsStamp.readAsStringSync().trim();
final String repoHeadHash = reverseParse('HEAD');
if (toolsStampHash == repoHeadHash) {
return;
}
}
stdio.printTrace('Building tool...');
// Build tool
processManager.runSync(<String>[
fileSystem.path.join(checkoutDirectory.path, 'bin', 'flutter'),
'help',
]);
}
io.ProcessResult runFlutter(List<String> args) {
_ensureToolReady();
return processManager.runSync(<String>[
fileSystem.path.join(checkoutDirectory.path, 'bin', 'flutter'),
...args,
]);
}
@override
void checkout(String revision) {
super.checkout(revision);
// The tool will overwrite old cached artifacts, but not delete unused
// artifacts from a previous version. Thus, delete the entire cache and
// re-populate.
final Directory cache = fileSystem.directory(cacheDirectory);
if (cache.existsSync()) {
stdio.printTrace('Deleting cache...');
cache.deleteSync(recursive: true);
}
_ensureToolReady();
}
Version flutterVersion() {
// Check version
final io.ProcessResult result =
runFlutter(<String>['--version', '--machine']);
final Map<String, dynamic> versionJson = jsonDecode(
globals.stdoutToString(result.stdout),
) as Map<String, dynamic>;
return Version.fromString(versionJson['frameworkVersion'] as String);
}
}
/// An enum of all the repositories that the Conductor supports.
@ -377,22 +285,81 @@ enum RepositoryType {
class Checkouts {
Checkouts({
@required Platform platform,
@required this.fileSystem,
@required this.platform,
@required this.processManager,
@required this.stdio,
@required Directory parentDirectory,
String directoryName = 'flutter_conductor_checkouts',
}) : assert(parentDirectory != null),
directory = parentDirectory.childDirectory(directoryName) {
Directory parentDirectory,
String directoryName = 'checkouts',
}) {
if (parentDirectory != null) {
directory = parentDirectory.childDirectory(directoryName);
} else {
String filePath;
// If a test
if (platform.script.scheme == 'data') {
final RegExp pattern = RegExp(
r'(file:\/\/[^"]*[/\\]dev\/tools[/\\][^"]+\.dart)',
multiLine: true,
);
final Match match =
pattern.firstMatch(Uri.decodeFull(platform.script.path));
if (match == null) {
throw Exception(
'Cannot determine path of script!\n${platform.script.path}',
);
}
filePath = Uri.parse(match.group(1)).path.replaceAll(r'%20', ' ');
} else {
filePath = platform.script.toFilePath();
}
final String checkoutsDirname = fileSystem.path.normalize(
fileSystem.path.join(
fileSystem.path.dirname(filePath),
'..',
'checkouts',
),
);
directory = fileSystem.directory(checkoutsDirname);
}
if (!directory.existsSync()) {
directory.createSync(recursive: true);
}
}
final Directory directory;
Directory directory;
final FileSystem fileSystem;
final Platform platform;
final ProcessManager processManager;
final Stdio stdio;
Repository addRepo({
@required RepositoryType repoType,
@required Stdio stdio,
@required Platform platform,
FileSystem fileSystem,
String upstream,
String name,
bool localUpstream = false,
bool useExistingCheckout = false,
}) {
switch (repoType) {
case RepositoryType.framework:
name ??= 'framework';
upstream ??= 'https://github.com/flutter/flutter.git';
break;
case RepositoryType.engine:
name ??= 'engine';
upstream ??= 'https://github.com/flutter/engine.git';
break;
}
return Repository(
name: name,
upstream: upstream,
stdio: stdio,
platform: platform,
fileSystem: fileSystem,
parentDirectory: directory,
processManager: processManager,
localUpstream: localUpstream,
useExistingCheckout: useExistingCheckout,
);
}
}

View file

@ -14,12 +14,12 @@ import './stdio.dart';
import './version.dart';
/// Create a new dev release without cherry picks.
class RollDevCommand extends Command<void> {
RollDevCommand({
@required this.checkouts,
@required this.fileSystem,
@required this.platform,
@required this.stdio,
class RollDev extends Command<void> {
RollDev({
this.fileSystem,
this.platform,
this.repository,
this.stdio,
}) {
argParser.addOption(
kIncrement,
@ -60,10 +60,10 @@ class RollDevCommand extends Command<void> {
argParser.addFlag(kYes, negatable: false, abbr: 'y', help: 'Skip the confirmation prompt.');
}
final Checkouts checkouts;
final FileSystem fileSystem;
final Platform platform;
final Stdio stdio;
final Repository repository;
@override
String get name => 'roll-dev';
@ -76,7 +76,9 @@ class RollDevCommand extends Command<void> {
void run() {
rollDev(
argResults: argResults,
repository: FrameworkRepository(checkouts),
fileSystem: fileSystem,
platform: platform,
repository: repository,
stdio: stdio,
usage: argParser.usage,
);
@ -91,7 +93,9 @@ bool rollDev({
@required String usage,
@required ArgResults argResults,
@required Stdio stdio,
@required FrameworkRepository repository,
@required Platform platform,
@required FileSystem fileSystem,
@required Repository repository,
String remoteName = 'origin',
}) {
final String level = argResults[kIncrement] as String;

View file

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:io' as io;
import 'dart:io';
import 'package:meta/meta.dart';
@ -31,15 +31,9 @@ class VerboseStdio extends Stdio {
@required this.stdin,
}) : assert(stdout != null), assert(stderr != null), assert(stdin != null);
factory VerboseStdio.local() => VerboseStdio(
stdout: io.stdout,
stderr: io.stderr,
stdin: io.stdin,
);
final io.Stdout stdout;
final io.Stdout stderr;
final io.Stdin stdin;
final Stdout stdout;
final Stdout stderr;
final Stdin stdin;
@override
void printError(String message) {

View file

@ -1,71 +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.
import 'package:args/command_runner.dart';
import 'package:file/file.dart';
import 'package:file/local.dart';
import 'package:platform/platform.dart';
import 'package:process/process.dart';
import 'package:dev_tools/codesign.dart' show CodesignCommand;
import 'package:dev_tools/globals.dart';
import 'package:dev_tools/repository.dart' show Checkouts;
import './common.dart';
/// Verify all binaries in the Flutter cache are expected by Conductor.
void main() {
test(
'validate the expected binaries from the conductor codesign command are present in the cache',
() async {
const Platform platform = LocalPlatform();
const FileSystem fileSystem = LocalFileSystem();
const ProcessManager processManager = LocalProcessManager();
final TestStdio stdio = TestStdio(verbose: true);
final Checkouts checkouts = Checkouts(
fileSystem: fileSystem,
parentDirectory: localFlutterRoot.parent,
platform: platform,
processManager: processManager,
stdio: stdio,
);
final CommandRunner<void> runner = CommandRunner<void>('codesign-test', '')
..addCommand(
CodesignCommand(checkouts: checkouts, flutterRoot: localFlutterRoot));
try {
await runner.run(<String>[
'codesign',
'--verify',
// Only verify if the correct binaries are in the cache
'--no-signatures',
]);
} on ConductorException catch (e) {
print(fixItInstructions);
fail(e.message);
} on Exception {
print('stdout:\n${stdio.stdout}');
print('stderr:\n${stdio.error}');
rethrow;
}
}, onPlatform: <String, dynamic>{
'windows': const Skip('codesign command is only supported on macos'),
'linux': const Skip('codesign command is only supported on macos'),
});
}
const String fixItInstructions = '''
Codesign integration test failed.
This means that the binary files found in the Flutter cache do not match those
expected by the conductor tool (either an expected file was not found in the
cache or an unexpected file was found in the cache).
This usually happens either during an engine roll or a change to the caching
logic in flutter_tools. If this is a valid change, then the conductor source
code should be updated, specifically either the [binariesWithEntitlements] or
[binariesWithoutEntitlements] lists, depending on if the file should have macOS
entitlements applied during codesigning.
''';

View file

@ -1,412 +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.
import 'package:args/command_runner.dart';
import 'package:dev_tools/codesign.dart';
import 'package:dev_tools/globals.dart';
import 'package:dev_tools/repository.dart';
import 'package:file/file.dart';
import 'package:file/memory.dart';
import 'package:meta/meta.dart';
import 'package:platform/platform.dart';
import '../../../packages/flutter_tools/test/src/fake_process_manager.dart';
import './common.dart';
void main() {
group('codesign command', () {
const String flutterRoot = '/flutter';
const String checkoutsParentDirectory = '$flutterRoot/dev/tools/';
const String flutterCache =
'${checkoutsParentDirectory}flutter_conductor_checkouts/framework/bin/cache';
const String flutterBin =
'${checkoutsParentDirectory}flutter_conductor_checkouts/framework/bin/flutter';
const String revision = 'abcd1234';
CommandRunner<void> runner;
Checkouts checkouts;
MemoryFileSystem fileSystem;
FakePlatform platform;
TestStdio stdio;
FakeProcessManager processManager;
const List<String> binariesWithEntitlements = <String>[
'$flutterCache/dart-sdk/bin/dart',
'$flutterCache/dart-sdk/bin/dartaotruntime',
];
const List<String> binariesWithoutEntitlements = <String>[
'$flutterCache/engine/darwin-x64/font-subset',
];
const List<String> allBinaries = <String>[
...binariesWithEntitlements,
...binariesWithoutEntitlements,
];
void createRunner({
String operatingSystem = 'macos',
List<FakeCommand> commands,
}) {
stdio = TestStdio();
fileSystem = MemoryFileSystem.test();
platform = FakePlatform(operatingSystem: operatingSystem);
processManager = FakeProcessManager.list(commands ?? <FakeCommand>[]);
checkouts = Checkouts(
fileSystem: fileSystem,
parentDirectory: fileSystem.directory(checkoutsParentDirectory),
platform: platform,
processManager: processManager,
stdio: stdio,
);
final FakeCodesignCommand command = FakeCodesignCommand(
checkouts: checkouts,
binariesWithEntitlements: binariesWithEntitlements,
binariesWithoutEntitlements: binariesWithoutEntitlements,
flutterRoot: fileSystem.directory(flutterRoot),
);
runner = CommandRunner<void>('codesign-test', '')
..addCommand(command);
}
test('throws exception if not run from macos', () async {
createRunner(operatingSystem: 'linux');
expect(
() async => await runner.run(<String>['codesign']),
throwsExceptionWith('Error! Expected operating system "macos"'),
);
});
test('throws exception if verify flag is not provided', () async {
createRunner();
expect(
() async => await runner.run(<String>['codesign']),
throwsExceptionWith(
'Sorry, but codesigning is not implemented yet. Please pass the --$kVerify flag to verify signatures'),
);
});
test('succeeds if every binary is codesigned and has correct entitlements', () async {
final List<FakeCommand> codesignCheckCommands = <FakeCommand>[];
for (final String bin in binariesWithEntitlements) {
codesignCheckCommands.add(
FakeCommand(
command: <String>['codesign', '-vvv', bin],
),
);
codesignCheckCommands.add(
FakeCommand(
command: <String>['codesign', '--display', '--entitlements', ':-', bin],
stdout: expectedEntitlements.join('\n'),
),
);
}
for (final String bin in binariesWithoutEntitlements) {
codesignCheckCommands.add(
FakeCommand(
command: <String>['codesign', '-vvv', bin],
),
);
}
createRunner(commands: <FakeCommand>[
const FakeCommand(command: <String>[
'git',
'clone',
'--',
'file://$flutterRoot/',
'${checkoutsParentDirectory}flutter_conductor_checkouts/framework',
]),
const FakeCommand(command: <String>[
'git',
'rev-parse',
'HEAD',
], stdout: revision),
const FakeCommand(command: <String>[
'git',
'checkout',
revision,
]),
const FakeCommand(command: <String>[
flutterBin,
'help',
]),
const FakeCommand(command: <String>[
flutterBin,
'help',
]),
const FakeCommand(command: <String>[
flutterBin,
'precache',
'--ios',
'--macos',
]),
FakeCommand(
command: const <String>[
'find',
'${checkoutsParentDirectory}flutter_conductor_checkouts/framework/bin/cache',
'-type',
'f',
],
stdout: allBinaries.join('\n'),
),
for (String bin in allBinaries)
FakeCommand(
command: <String>['file', '--mime-type', '-b', bin],
stdout: 'application/x-mach-binary',
),
...codesignCheckCommands,
]);
await runner.run(<String>['codesign', '--$kVerify', '--$kRevision', revision]);
expect(processManager.hasRemainingExpectations, false);
});
test('fails if a single binary is not codesigned', () async {
final List<FakeCommand> codesignCheckCommands = <FakeCommand>[];
codesignCheckCommands.add(
const FakeCommand(
command: <String>['codesign', '-vvv', '$flutterCache/dart-sdk/bin/dart'],
),
);
codesignCheckCommands.add(
FakeCommand(
command: const <String>[
'codesign',
'--display',
'--entitlements',
':-',
'$flutterCache/dart-sdk/bin/dart',
],
stdout: expectedEntitlements.join('\n'),
)
);
// Not signed
codesignCheckCommands.add(
const FakeCommand(
command: <String>['codesign', '-vvv', '$flutterCache/dart-sdk/bin/dartaotruntime'],
exitCode: 1,
),
);
codesignCheckCommands.add(
const FakeCommand(
command: <String>['codesign', '-vvv', '$flutterCache/engine/darwin-x64/font-subset'],
),
);
createRunner(commands: <FakeCommand>[
const FakeCommand(command: <String>[
'git',
'clone',
'--',
'file://$flutterRoot/',
'${checkoutsParentDirectory}flutter_conductor_checkouts/framework',
]),
const FakeCommand(command: <String>[
'git',
'rev-parse',
'HEAD',
], stdout: revision),
const FakeCommand(command: <String>[
'git',
'checkout',
revision,
]),
const FakeCommand(command: <String>[
flutterBin,
'help',
]),
const FakeCommand(command: <String>[
flutterBin,
'help',
]),
const FakeCommand(command: <String>[
flutterBin,
'precache',
'--ios',
'--macos',
]),
FakeCommand(
command: const <String>[
'find',
'${checkoutsParentDirectory}flutter_conductor_checkouts/framework/bin/cache',
'-type',
'f',
],
stdout: allBinaries.join('\n'),
),
for (String bin in allBinaries)
FakeCommand(
command: <String>['file', '--mime-type', '-b', bin],
stdout: 'application/x-mach-binary',
),
...codesignCheckCommands,
]);
expect(
() async => await runner.run(<String>['codesign', '--$kVerify', '--$kRevision', revision]),
throwsExceptionWith('Test failed because unsigned binaries detected.'),
);
expect(processManager.hasRemainingExpectations, false);
});
test('fails if a single binary has the wrong entitlements', () async {
final List<FakeCommand> codesignCheckCommands = <FakeCommand>[];
codesignCheckCommands.add(
const FakeCommand(
command: <String>['codesign', '-vvv', '$flutterCache/dart-sdk/bin/dart'],
),
);
codesignCheckCommands.add(
FakeCommand(
command: const <String>['codesign', '--display', '--entitlements', ':-', '$flutterCache/dart-sdk/bin/dart'],
stdout: expectedEntitlements.join('\n'),
)
);
codesignCheckCommands.add(
const FakeCommand(
command: <String>['codesign', '-vvv', '$flutterCache/dart-sdk/bin/dartaotruntime'],
),
);
// No entitlements
codesignCheckCommands.add(
const FakeCommand(
command: <String>['codesign', '--display', '--entitlements', ':-', '$flutterCache/dart-sdk/bin/dartaotruntime'],
)
);
codesignCheckCommands.add(
const FakeCommand(
command: <String>['codesign', '-vvv', '$flutterCache/engine/darwin-x64/font-subset'],
),
);
createRunner(commands: <FakeCommand>[
const FakeCommand(command: <String>[
'git',
'clone',
'--',
'file://$flutterRoot/',
'${checkoutsParentDirectory}flutter_conductor_checkouts/framework',
]),
const FakeCommand(command: <String>[
'git',
'rev-parse',
'HEAD',
], stdout: revision),
const FakeCommand(command: <String>[
'git',
'checkout',
revision,
]),
const FakeCommand(command: <String>[
flutterBin,
'help',
]),
const FakeCommand(command: <String>[
flutterBin,
'help',
]),
const FakeCommand(command: <String>[
flutterBin,
'precache',
'--ios',
'--macos',
]),
FakeCommand(
command: const <String>[
'find',
'${checkoutsParentDirectory}flutter_conductor_checkouts/framework/bin/cache',
'-type',
'f',
],
stdout: allBinaries.join('\n'),
),
for (String bin in allBinaries)
FakeCommand(
command: <String>['file', '--mime-type', '-b', bin],
stdout: 'application/x-mach-binary',
),
...codesignCheckCommands,
]);
expect(
() async => await runner.run(<String>['codesign', '--$kVerify', '--$kRevision', revision]),
throwsExceptionWith('Test failed because files found with the wrong entitlements'),
);
expect(processManager.hasRemainingExpectations, false);
});
test('does not check signatures or entitlements if --no-$kSignatures specified', () async {
createRunner(commands: <FakeCommand>[
const FakeCommand(command: <String>[
'git',
'clone',
'--',
'file://$flutterRoot/',
'${checkoutsParentDirectory}flutter_conductor_checkouts/framework',
]),
const FakeCommand(command: <String>[
'git',
'rev-parse',
'HEAD',
], stdout: revision),
const FakeCommand(command: <String>[
'git',
'checkout',
revision,
]),
const FakeCommand(command: <String>[
flutterBin,
'help',
]),
const FakeCommand(command: <String>[
flutterBin,
'help',
]),
const FakeCommand(command: <String>[
flutterBin,
'precache',
'--ios',
'--macos',
]),
FakeCommand(
command: const <String>[
'find',
'${checkoutsParentDirectory}flutter_conductor_checkouts/framework/bin/cache',
'-type',
'f',
],
stdout: allBinaries.join('\n'),
),
for (String bin in allBinaries)
FakeCommand(
command: <String>['file', '--mime-type', '-b', bin],
stdout: 'application/x-mach-binary',
),
]);
try {
await runner.run(<String>[
'codesign',
'--$kVerify',
'--no-$kSignatures',
'--$kRevision',
revision,
]);
} on ConductorException {
//print(stdio.error);
rethrow;
}
expect(
processManager.hasRemainingExpectations,
false,
);
});
});
}
class FakeCodesignCommand extends CodesignCommand {
FakeCodesignCommand({
@required Checkouts checkouts,
@required this.binariesWithEntitlements,
@required this.binariesWithoutEntitlements,
@required Directory flutterRoot,
}) : super(checkouts: checkouts, flutterRoot: flutterRoot);
@override
final List<String> binariesWithEntitlements;
@override
final List<String> binariesWithoutEntitlements;
}

View file

@ -4,7 +4,6 @@
import 'dart:io';
import 'package:file/file.dart';
import 'package:test/test.dart' hide TypeMatcher, isInstanceOf;
import 'package:test/test.dart' as test_package show TypeMatcher;

View file

@ -7,7 +7,6 @@ import 'package:file/local.dart';
import 'package:platform/platform.dart';
import 'package:process/process.dart';
import 'package:dev_tools/globals.dart';
import 'package:dev_tools/roll_dev.dart' show rollDev;
import 'package:dev_tools/repository.dart';
import 'package:dev_tools/version.dart';
@ -23,8 +22,8 @@ void main() {
const String usageString = 'Usage: flutter conductor.';
Checkouts checkouts;
FrameworkRepository frameworkUpstream;
FrameworkRepository framework;
Repository frameworkUpstream;
Repository framework;
setUp(() {
platform = const LocalPlatform();
@ -33,20 +32,22 @@ void main() {
stdio = TestStdio(verbose: true);
checkouts = Checkouts(
fileSystem: fileSystem,
parentDirectory: localFlutterRoot.parent,
platform: platform,
processManager: processManager,
stdio: stdio,
);
frameworkUpstream = FrameworkRepository(checkouts, localUpstream: true);
frameworkUpstream = checkouts.addRepo(
repoType: RepositoryType.framework,
name: 'framework-upstream',
stdio: stdio,
platform: platform,
localUpstream: true,
fileSystem: fileSystem,
useExistingCheckout: false,
);
// This repository has [frameworkUpstream] set as its push/pull remote.
framework = FrameworkRepository(
checkouts,
name: 'test-framework',
upstream: 'file://${frameworkUpstream.checkoutDirectory.path}/',
);
framework = frameworkUpstream.cloneRepository('test-framework');
});
test('increment m', () {
@ -67,6 +68,8 @@ void main() {
usage: usageString,
argResults: fakeArgResults,
stdio: stdio,
fileSystem: fileSystem,
platform: platform,
repository: framework,
),
true,
@ -104,6 +107,8 @@ void main() {
usage: usageString,
argResults: fakeArgResults,
stdio: stdio,
fileSystem: fileSystem,
platform: platform,
repository: framework,
),
true,

View file

@ -24,7 +24,7 @@ void main() {
FakeArgResults fakeArgResults;
MemoryFileSystem fileSystem;
TestStdio stdio;
FrameworkRepository repo;
Repository repo;
Checkouts checkouts;
FakePlatform platform;
FakeProcessManager processManager;
@ -39,9 +39,12 @@ void main() {
parentDirectory: fileSystem.directory(checkoutsParentDirectory),
platform: platform,
processManager: processManager,
);
repo = checkouts.addRepo(
platform: platform,
repoType: RepositoryType.framework,
stdio: stdio,
);
repo = FrameworkRepository(checkouts);
});
test('returns false if level not provided', () {
@ -53,6 +56,8 @@ void main() {
expect(
rollDev(
argResults: fakeArgResults,
fileSystem: fileSystem,
platform: platform,
repository: repo,
stdio: stdio,
usage: usage,
@ -70,6 +75,8 @@ void main() {
expect(
rollDev(
argResults: fakeArgResults,
fileSystem: fileSystem,
platform: platform,
repository: repo,
stdio: stdio,
usage: usage,
@ -85,13 +92,8 @@ void main() {
'clone',
'--',
kUpstreamRemote,
'${checkoutsParentDirectory}flutter_conductor_checkouts/framework',
'${checkoutsParentDirectory}checkouts/framework',
]),
const FakeCommand(command: <String>[
'git',
'rev-parse',
'HEAD',
], stdout: commit),
const FakeCommand(command: <String>[
'git',
'remote',
@ -113,6 +115,8 @@ void main() {
try {
rollDev(
argResults: fakeArgResults,
fileSystem: fileSystem,
platform: platform,
repository: repo,
stdio: stdio,
usage: usage,
@ -133,13 +137,8 @@ void main() {
'clone',
'--',
kUpstreamRemote,
'${checkoutsParentDirectory}flutter_conductor_checkouts/framework',
'${checkoutsParentDirectory}checkouts/framework',
]),
const FakeCommand(command: <String>[
'git',
'rev-parse',
'HEAD',
], stdout: commit),
const FakeCommand(command: <String>[
'git',
'remote',
@ -188,6 +187,8 @@ void main() {
rollDev(
usage: usage,
argResults: fakeArgResults,
fileSystem: fileSystem,
platform: platform,
repository: repo,
stdio: stdio,
),
@ -205,13 +206,8 @@ void main() {
'clone',
'--',
kUpstreamRemote,
'${checkoutsParentDirectory}flutter_conductor_checkouts/framework',
'${checkoutsParentDirectory}checkouts/framework',
]),
const FakeCommand(command: <String>[
'git',
'rev-parse',
'HEAD',
], stdout: commit),
const FakeCommand(command: <String>[
'git',
'remote',
@ -271,6 +267,8 @@ void main() {
() => rollDev(
usage: usage,
argResults: fakeArgResults,
fileSystem: fileSystem,
platform: platform,
repository: repo,
stdio: stdio,
),
@ -285,13 +283,8 @@ void main() {
'clone',
'--',
kUpstreamRemote,
'${checkoutsParentDirectory}flutter_conductor_checkouts/framework',
'${checkoutsParentDirectory}checkouts/framework',
]),
const FakeCommand(command: <String>[
'git',
'rev-parse',
'HEAD',
], stdout: commit),
const FakeCommand(command: <String>[
'git',
'remote',
@ -340,6 +333,8 @@ void main() {
() => rollDev(
usage: usage,
argResults: fakeArgResults,
fileSystem: fileSystem,
platform: platform,
repository: repo,
stdio: stdio,
),
@ -358,13 +353,8 @@ void main() {
'clone',
'--',
kUpstreamRemote,
'${checkoutsParentDirectory}flutter_conductor_checkouts/framework',
'${checkoutsParentDirectory}checkouts/framework',
]),
const FakeCommand(command: <String>[
'git',
'rev-parse',
'HEAD',
], stdout: commit),
const FakeCommand(command: <String>[
'git',
'remote',
@ -420,6 +410,8 @@ void main() {
expect(
() => rollDev(
argResults: fakeArgResults,
fileSystem: fileSystem,
platform: platform,
repository: repo,
stdio: stdio,
usage: usage,
@ -435,13 +427,8 @@ void main() {
'clone',
'--',
kUpstreamRemote,
'${checkoutsParentDirectory}flutter_conductor_checkouts/framework',
'${checkoutsParentDirectory}checkouts/framework',
]),
const FakeCommand(command: <String>[
'git',
'rev-parse',
'HEAD',
], stdout: commit),
const FakeCommand(command: <String>[
'git',
'remote',
@ -514,6 +501,8 @@ void main() {
rollDev(
usage: usage,
argResults: fakeArgResults,
fileSystem: fileSystem,
platform: platform,
repository: repo,
stdio: stdio,
),
@ -528,13 +517,8 @@ void main() {
'clone',
'--',
kUpstreamRemote,
'${checkoutsParentDirectory}flutter_conductor_checkouts/framework',
'${checkoutsParentDirectory}checkouts/framework',
]),
const FakeCommand(command: <String>[
'git',
'rev-parse',
'HEAD',
], stdout: commit),
const FakeCommand(command: <String>[
'git',
'remote',
@ -611,6 +595,8 @@ void main() {
rollDev(
usage: usage,
argResults: fakeArgResults,
fileSystem: fileSystem,
platform: platform,
repository: repo,
stdio: stdio,
),
@ -625,13 +611,8 @@ void main() {
'clone',
'--',
kUpstreamRemote,
'${checkoutsParentDirectory}flutter_conductor_checkouts/framework',
'${checkoutsParentDirectory}checkouts/framework',
]),
const FakeCommand(command: <String>[
'git',
'rev-parse',
'HEAD',
], stdout: commit),
const FakeCommand(command: <String>[
'git',
'remote',
@ -703,6 +684,8 @@ void main() {
expect(
rollDev(
argResults: fakeArgResults,
fileSystem: fileSystem,
platform: platform,
repository: repo,
stdio: stdio,
usage: usage,