Commit 57746f38 authored by xster's avatar xster Committed by GitHub

Guess sign iOS with the first certificate when running in machine mode (#10870)

* Guess sign with the first certificate when multiple are available in machine mode

* review
parent a41e354a
...@@ -341,6 +341,7 @@ class AndroidDevice extends Device { ...@@ -341,6 +341,7 @@ class AndroidDevice extends Device {
String kernelPath, String kernelPath,
bool prebuiltApplication: false, bool prebuiltApplication: false,
bool applicationNeedsRebuild: false, bool applicationNeedsRebuild: false,
bool usesTerminalUi: true,
}) async { }) async {
if (!await _checkForSupportedAdbVersion() || !await _checkForSupportedAndroidVersion()) if (!await _checkForSupportedAdbVersion() || !await _checkForSupportedAndroidVersion())
return new LaunchResult.failed(); return new LaunchResult.failed();
......
...@@ -260,6 +260,7 @@ Future<LaunchResult> _startApp(DriveCommand command) async { ...@@ -260,6 +260,7 @@ Future<LaunchResult> _startApp(DriveCommand command) async {
diagnosticPort: command.diagnosticPort, diagnosticPort: command.diagnosticPort,
), ),
platformArgs: platformArgs, platformArgs: platformArgs,
usesTerminalUi: false,
); );
if (!result.started) { if (!result.started) {
......
...@@ -222,6 +222,11 @@ abstract class Device { ...@@ -222,6 +222,11 @@ abstract class Device {
/// ///
/// [platformArgs] allows callers to pass platform-specific arguments to the /// [platformArgs] allows callers to pass platform-specific arguments to the
/// start call. The build mode is not used by all platforms. /// start call. The build mode is not used by all platforms.
///
/// If [usesTerminalUi] is true, Flutter Tools may attempt to prompt the
/// user to resolve fixable issues such as selecting a signing certificate
/// for iOS device deployment. Set to false if stdin cannot be read from while
/// attempting to start the app.
Future<LaunchResult> startApp( Future<LaunchResult> startApp(
ApplicationPackage package, ApplicationPackage package,
BuildMode mode, { BuildMode mode, {
...@@ -231,7 +236,8 @@ abstract class Device { ...@@ -231,7 +236,8 @@ abstract class Device {
Map<String, dynamic> platformArgs, Map<String, dynamic> platformArgs,
String kernelPath, String kernelPath,
bool prebuiltApplication: false, bool prebuiltApplication: false,
bool applicationNeedsRebuild: false bool applicationNeedsRebuild: false,
bool usesTerminalUi: true,
}); });
/// Does this device implement support for hot reloading / restarting? /// Does this device implement support for hot reloading / restarting?
......
...@@ -68,6 +68,7 @@ class FuchsiaDevice extends Device { ...@@ -68,6 +68,7 @@ class FuchsiaDevice extends Device {
bool prebuiltApplication: false, bool prebuiltApplication: false,
String kernelPath, String kernelPath,
bool applicationNeedsRebuild: false, bool applicationNeedsRebuild: false,
bool usesTerminalUi: false,
}) => new Future<Null>.error('unimplemented'); }) => new Future<Null>.error('unimplemented');
@override @override
......
...@@ -80,7 +80,7 @@ final RegExp _certificateOrganizationalUnitExtractionPattern = new RegExp(r'OU=( ...@@ -80,7 +80,7 @@ final RegExp _certificateOrganizationalUnitExtractionPattern = new RegExp(r'OU=(
/// ///
/// Will return null if none are found, if the user cancels or if the Xcode /// Will return null if none are found, if the user cancels or if the Xcode
/// project has a development team set in the project's build settings. /// project has a development team set in the project's build settings.
Future<String> getCodeSigningIdentityDevelopmentTeam(BuildableIOSApp iosApp) async{ Future<String> getCodeSigningIdentityDevelopmentTeam({BuildableIOSApp iosApp, bool usesTerminalUi: true}) async{
if (iosApp.buildSettings == null) if (iosApp.buildSettings == null)
return null; return null;
...@@ -115,7 +115,7 @@ Future<String> getCodeSigningIdentityDevelopmentTeam(BuildableIOSApp iosApp) asy ...@@ -115,7 +115,7 @@ Future<String> getCodeSigningIdentityDevelopmentTeam(BuildableIOSApp iosApp) asy
.toSet() // Unique. .toSet() // Unique.
.toList(); .toList();
final String signingIdentity = await _chooseSigningIdentity(validCodeSigningIdentities); final String signingIdentity = await _chooseSigningIdentity(validCodeSigningIdentities, usesTerminalUi);
// If none are chosen, return null. // If none are chosen, return null.
if (signingIdentity == null) if (signingIdentity == null)
...@@ -153,7 +153,7 @@ Future<String> getCodeSigningIdentityDevelopmentTeam(BuildableIOSApp iosApp) asy ...@@ -153,7 +153,7 @@ Future<String> getCodeSigningIdentityDevelopmentTeam(BuildableIOSApp iosApp) asy
?.group(1); ?.group(1);
} }
Future<String> _chooseSigningIdentity(List<String> validCodeSigningIdentities) async { Future<String> _chooseSigningIdentity(List<String> validCodeSigningIdentities, bool usesTerminalUi) async {
// The user has no valid code signing identities. // The user has no valid code signing identities.
if (validCodeSigningIdentities.isEmpty) { if (validCodeSigningIdentities.isEmpty) {
printError(noCertificatesInstruction, emphasis: true); printError(noCertificatesInstruction, emphasis: true);
...@@ -176,6 +176,11 @@ Future<String> _chooseSigningIdentity(List<String> validCodeSigningIdentities) a ...@@ -176,6 +176,11 @@ Future<String> _chooseSigningIdentity(List<String> validCodeSigningIdentities) a
} }
} }
// If terminal UI can't be used, just attempt with the first valid certificate
// since we can't ask the user.
if (!usesTerminalUi)
return validCodeSigningIdentities.first;
final int count = validCodeSigningIdentities.length; final int count = validCodeSigningIdentities.length;
printStatus( printStatus(
'Multiple valid development certificates available (your choice will be saved):', 'Multiple valid development certificates available (your choice will be saved):',
......
...@@ -175,13 +175,20 @@ class IOSDevice extends Device { ...@@ -175,13 +175,20 @@ class IOSDevice extends Device {
bool prebuiltApplication: false, bool prebuiltApplication: false,
String kernelPath, String kernelPath,
bool applicationNeedsRebuild: false, bool applicationNeedsRebuild: false,
bool usesTerminalUi: true,
}) async { }) async {
if (!prebuiltApplication) { if (!prebuiltApplication) {
// TODO(chinmaygarde): Use mainPath, route. // TODO(chinmaygarde): Use mainPath, route.
printTrace('Building ${app.name} for $id'); printTrace('Building ${app.name} for $id');
// Step 1: Build the precompiled/DBC application if necessary. // Step 1: Build the precompiled/DBC application if necessary.
final XcodeBuildResult buildResult = await buildXcodeProject(app: app, mode: mode, target: mainPath, buildForDevice: true); final XcodeBuildResult buildResult = await buildXcodeProject(
app: app,
mode: mode,
target: mainPath,
buildForDevice: true,
usesTerminalUi: usesTerminalUi,
);
if (!buildResult.success) { if (!buildResult.success) {
printError('Could not build the precompiled application for the device.'); printError('Could not build the precompiled application for the device.');
await diagnoseXcodeBuildFailure(buildResult, app); await diagnoseXcodeBuildFailure(buildResult, app);
......
...@@ -193,7 +193,8 @@ Future<XcodeBuildResult> buildXcodeProject({ ...@@ -193,7 +193,8 @@ Future<XcodeBuildResult> buildXcodeProject({
BuildMode mode, BuildMode mode,
String target: flx.defaultMainPath, String target: flx.defaultMainPath,
bool buildForDevice, bool buildForDevice,
bool codesign: true bool codesign: true,
bool usesTerminalUi: true,
}) async { }) async {
if (!_checkXcodeVersion()) if (!_checkXcodeVersion())
return new XcodeBuildResult(success: false); return new XcodeBuildResult(success: false);
...@@ -205,7 +206,7 @@ Future<XcodeBuildResult> buildXcodeProject({ ...@@ -205,7 +206,7 @@ Future<XcodeBuildResult> buildXcodeProject({
String developmentTeam; String developmentTeam;
if (codesign && buildForDevice) if (codesign && buildForDevice)
developmentTeam = await getCodeSigningIdentityDevelopmentTeam(app); developmentTeam = await getCodeSigningIdentityDevelopmentTeam(iosApp: app, usesTerminalUi: usesTerminalUi);
// Before the build, all service definitions must be updated and the dylibs // Before the build, all service definitions must be updated and the dylibs
// copied over to a location that is suitable for Xcodebuild to find them. // copied over to a location that is suitable for Xcodebuild to find them.
......
...@@ -315,6 +315,7 @@ class IOSSimulator extends Device { ...@@ -315,6 +315,7 @@ class IOSSimulator extends Device {
String kernelPath, String kernelPath,
bool prebuiltApplication: false, bool prebuiltApplication: false,
bool applicationNeedsRebuild: false, bool applicationNeedsRebuild: false,
bool usesTerminalUi: true,
}) async { }) async {
if (!prebuiltApplication) { if (!prebuiltApplication) {
printTrace('Building ${app.name} for $id.'); printTrace('Building ${app.name} for $id.');
......
...@@ -239,7 +239,8 @@ class FlutterDevice { ...@@ -239,7 +239,8 @@ class FlutterDevice {
route: route, route: route,
prebuiltApplication: prebuiltMode, prebuiltApplication: prebuiltMode,
kernelPath: hotRunner.kernelFilePath, kernelPath: hotRunner.kernelFilePath,
applicationNeedsRebuild: shouldBuild || hasDirtyDependencies applicationNeedsRebuild: shouldBuild || hasDirtyDependencies,
usesTerminalUi: hotRunner.usesTerminalUI,
); );
final LaunchResult result = await futureResult; final LaunchResult result = await futureResult;
...@@ -298,7 +299,8 @@ class FlutterDevice { ...@@ -298,7 +299,8 @@ class FlutterDevice {
platformArgs: platformArgs, platformArgs: platformArgs,
route: route, route: route,
prebuiltApplication: prebuiltMode, prebuiltApplication: prebuiltMode,
applicationNeedsRebuild: shouldBuild || hasDirtyDependencies applicationNeedsRebuild: shouldBuild || hasDirtyDependencies,
usesTerminalUi: coldRunner.usesTerminalUI,
); );
if (!result.started) { if (!result.started) {
......
...@@ -38,7 +38,7 @@ void main() { ...@@ -38,7 +38,7 @@ void main() {
testUsingContext('No auto-sign if Xcode project settings are not available', () async { testUsingContext('No auto-sign if Xcode project settings are not available', () async {
app = new BuildableIOSApp(projectBundleId: 'test.app'); app = new BuildableIOSApp(projectBundleId: 'test.app');
final String developmentTeam = await getCodeSigningIdentityDevelopmentTeam(app); final String developmentTeam = await getCodeSigningIdentityDevelopmentTeam(iosApp: app);
expect(developmentTeam, isNull); expect(developmentTeam, isNull);
}); });
...@@ -49,7 +49,7 @@ void main() { ...@@ -49,7 +49,7 @@ void main() {
'DEVELOPMENT_TEAM': 'abc', 'DEVELOPMENT_TEAM': 'abc',
}, },
); );
final String developmentTeam = await getCodeSigningIdentityDevelopmentTeam(app); final String developmentTeam = await getCodeSigningIdentityDevelopmentTeam(iosApp: app);
expect(developmentTeam, isNull); expect(developmentTeam, isNull);
expect(testLogger.statusText, equals( expect(testLogger.statusText, equals(
'Automatically signing iOS for device deployment using specified development team in Xcode project: abc\n' 'Automatically signing iOS for device deployment using specified development team in Xcode project: abc\n'
...@@ -59,7 +59,7 @@ void main() { ...@@ -59,7 +59,7 @@ void main() {
testUsingContext('No auto-sign if security or openssl not available', () async { testUsingContext('No auto-sign if security or openssl not available', () async {
when(mockProcessManager.runSync(<String>['which', 'security'])) when(mockProcessManager.runSync(<String>['which', 'security']))
.thenReturn(exitsFail); .thenReturn(exitsFail);
final String developmentTeam = await getCodeSigningIdentityDevelopmentTeam(app); final String developmentTeam = await getCodeSigningIdentityDevelopmentTeam(iosApp: app);
expect(developmentTeam, isNull); expect(developmentTeam, isNull);
}, },
overrides: <Type, Generator>{ overrides: <Type, Generator>{
...@@ -77,7 +77,7 @@ void main() { ...@@ -77,7 +77,7 @@ void main() {
String developmentTeam; String developmentTeam;
try { try {
developmentTeam = await getCodeSigningIdentityDevelopmentTeam(app); developmentTeam = await getCodeSigningIdentityDevelopmentTeam(iosApp: app);
fail('No identity should throw tool error'); fail('No identity should throw tool error');
} on ToolExit { } on ToolExit {
expect(developmentTeam, isNull); expect(developmentTeam, isNull);
...@@ -131,7 +131,7 @@ void main() { ...@@ -131,7 +131,7 @@ void main() {
when(mockProcess.stderr).thenReturn(mockStdErr); when(mockProcess.stderr).thenReturn(mockStdErr);
when(mockProcess.exitCode).thenReturn(0); when(mockProcess.exitCode).thenReturn(0);
final String developmentTeam = await getCodeSigningIdentityDevelopmentTeam(app); final String developmentTeam = await getCodeSigningIdentityDevelopmentTeam(iosApp: app);
expect(testLogger.statusText, contains('iPhone Developer: Profile 1 (1111AAAA11)')); expect(testLogger.statusText, contains('iPhone Developer: Profile 1 (1111AAAA11)'));
expect(testLogger.errorText, isEmpty); expect(testLogger.errorText, isEmpty);
...@@ -189,7 +189,7 @@ void main() { ...@@ -189,7 +189,7 @@ void main() {
when(mockOpenSslProcess.stderr).thenReturn(mockOpenSslStdErr); when(mockOpenSslProcess.stderr).thenReturn(mockOpenSslStdErr);
when(mockOpenSslProcess.exitCode).thenReturn(0); when(mockOpenSslProcess.exitCode).thenReturn(0);
final String developmentTeam = await getCodeSigningIdentityDevelopmentTeam(app); final String developmentTeam = await getCodeSigningIdentityDevelopmentTeam(iosApp: app);
expect( expect(
testLogger.statusText, testLogger.statusText,
...@@ -211,6 +211,68 @@ void main() { ...@@ -211,6 +211,68 @@ void main() {
AnsiTerminal: () => testTerminal, AnsiTerminal: () => testTerminal,
}); });
testUsingContext('Test multiple identity in machine mode works', () async {
when(mockProcessManager.runSync(<String>['which', 'security']))
.thenReturn(exitsHappy);
when(mockProcessManager.runSync(<String>['which', 'openssl']))
.thenReturn(exitsHappy);
when(mockProcessManager.runSync(
argThat(contains('find-identity')), environment: any, workingDirectory: any,
)).thenReturn(new ProcessResult(
1, // pid
0, // exitCode
'''
1) 86f7e437faa5a7fce15d1ddcb9eaeaea377667b8 "iPhone Developer: Profile 1 (1111AAAA11)"
2) da4b9237bacccdf19c0760cab7aec4a8359010b0 "iPhone Developer: Profile 2 (2222BBBB22)"
3) 5bf1fd927dfb8679496a2e6cf00cbe50c1c87145 "iPhone Developer: Profile 3 (3333CCCC33)"
3 valid identities found''',
''
));
mockTerminalStdInStream =
new Stream<String>.fromFuture(new Future<String>.error(new Exception('Cannot read from StdIn')));
when(mockProcessManager.runSync(
<String>['security', 'find-certificate', '-c', '1111AAAA11', '-p'],
environment: any,
workingDirectory: any,
)).thenReturn(new ProcessResult(
1, // pid
0, // exitCode
'This is a mock certificate',
'',
));
final MockProcess mockOpenSslProcess = new MockProcess();
final MockStdIn mockOpenSslStdIn = new MockStdIn();
final MockStream mockOpenSslStdErr = new MockStream();
when(mockProcessManager.start(
argThat(contains('openssl')), environment: any, workingDirectory: any,
)).thenReturn(new Future<Process>.value(mockOpenSslProcess));
when(mockOpenSslProcess.stdin).thenReturn(mockOpenSslStdIn);
when(mockOpenSslProcess.stdout).thenReturn(new Stream<List<int>>.fromFuture(
new Future<List<int>>.value(UTF8.encode(
'subject= /CN=iPhone Developer: Profile 1 (1111AAAA11)/OU=5555EEEE55/O=My Team/C=US'
)),
));
when(mockOpenSslProcess.stderr).thenReturn(mockOpenSslStdErr);
when(mockOpenSslProcess.exitCode).thenReturn(0);
final String developmentTeam = await getCodeSigningIdentityDevelopmentTeam(iosApp: app, usesTerminalUi: false);
expect(
testLogger.statusText,
contains('Signing iOS app for device deployment using developer identity: "iPhone Developer: Profile 1 (1111AAAA11)"'),
);
expect(testLogger.errorText, isEmpty);
verify(mockOpenSslStdIn.write('This is a mock certificate'));
expect(developmentTeam, '5555EEEE55');
},
overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
AnsiTerminal: () => testTerminal,
});
testUsingContext('Test saved certificate used', () async { testUsingContext('Test saved certificate used', () async {
when(mockProcessManager.runSync(<String>['which', 'security'])) when(mockProcessManager.runSync(<String>['which', 'security']))
.thenReturn(exitsHappy); .thenReturn(exitsHappy);
...@@ -257,7 +319,7 @@ void main() { ...@@ -257,7 +319,7 @@ void main() {
when(mockOpenSslProcess.exitCode).thenReturn(0); when(mockOpenSslProcess.exitCode).thenReturn(0);
when(mockConfig.getValue('ios-signing-cert')).thenReturn('iPhone Developer: Profile 3 (3333CCCC33)'); when(mockConfig.getValue('ios-signing-cert')).thenReturn('iPhone Developer: Profile 3 (3333CCCC33)');
final String developmentTeam = await getCodeSigningIdentityDevelopmentTeam(app); final String developmentTeam = await getCodeSigningIdentityDevelopmentTeam(iosApp: app);
expect( expect(
testLogger.statusText, testLogger.statusText,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment