Commit 02245234 authored by xster's avatar xster Committed by GitHub

Save development certificate choice (#10849)

parent 265257ab
...@@ -13,6 +13,11 @@ class ConfigCommand extends FlutterCommand { ...@@ -13,6 +13,11 @@ class ConfigCommand extends FlutterCommand {
argParser.addFlag('analytics', argParser.addFlag('analytics',
negatable: true, negatable: true,
help: 'Enable or disable reporting anonymously tool usage statistics and crash reports.'); help: 'Enable or disable reporting anonymously tool usage statistics and crash reports.');
argParser.addFlag(
'clear-ios-signing-cert',
negatable: false,
help: 'Clear the saved development certificate choice used to sign apps for iOS device deployment'
);
argParser.addOption('gradle-dir', help: 'The gradle install directory.'); argParser.addOption('gradle-dir', help: 'The gradle install directory.');
argParser.addOption('android-studio-dir', help: 'The Android Studio install directory.'); argParser.addOption('android-studio-dir', help: 'The Android Studio install directory.');
} }
...@@ -60,6 +65,9 @@ class ConfigCommand extends FlutterCommand { ...@@ -60,6 +65,9 @@ class ConfigCommand extends FlutterCommand {
if (argResults.wasParsed('android-studio-dir')) if (argResults.wasParsed('android-studio-dir'))
_updateConfig('android-studio-dir', argResults['android-studio-dir']); _updateConfig('android-studio-dir', argResults['android-studio-dir']);
if (argResults.wasParsed('clear-ios-signing-cert'))
_updateConfig('ios-signing-cert', '');
if (argResults.arguments.isEmpty) if (argResults.arguments.isEmpty)
printStatus(usage); printStatus(usage);
} }
......
...@@ -164,9 +164,21 @@ Future<String> _chooseSigningIdentity(List<String> validCodeSigningIdentities) a ...@@ -164,9 +164,21 @@ Future<String> _chooseSigningIdentity(List<String> validCodeSigningIdentities) a
return validCodeSigningIdentities.first; return validCodeSigningIdentities.first;
if (validCodeSigningIdentities.length > 1) { if (validCodeSigningIdentities.length > 1) {
final String savedCertChoice = config.getValue('ios-signing-cert');
if (savedCertChoice != null) {
if (validCodeSigningIdentities.contains(savedCertChoice)) {
printStatus('Found saved certificate choice "$savedCertChoice". To clear, use "flutter config".');
return savedCertChoice;
}
else {
printError('Saved signing certificate "$savedCertChoice" is not a valid development certificate');
}
}
final int count = validCodeSigningIdentities.length; final int count = validCodeSigningIdentities.length;
printStatus( printStatus(
'Multiple valid development certificates available:', 'Multiple valid development certificates available (your choice will be saved):',
emphasis: true, emphasis: true,
); );
for (int i=0; i<count; i++) { for (int i=0; i<count; i++) {
...@@ -182,10 +194,14 @@ Future<String> _chooseSigningIdentity(List<String> validCodeSigningIdentities) a ...@@ -182,10 +194,14 @@ Future<String> _chooseSigningIdentity(List<String> validCodeSigningIdentities) a
defaultChoiceIndex: 0, // Just pressing enter chooses the first one. defaultChoiceIndex: 0, // Just pressing enter chooses the first one.
); );
if (choice == 'a') if (choice == 'a') {
throwToolExit('Aborted. Code signing is required to build a deployable iOS app.'); throwToolExit('Aborted. Code signing is required to build a deployable iOS app.');
else } else {
return validCodeSigningIdentities[int.parse(choice) - 1]; final String selectedCert = validCodeSigningIdentities[int.parse(choice) - 1];
printStatus('Certificate choice "$savedCertChoice" saved');
config.setValue('ios-signing-cert', selectedCert);
return selectedCert;
}
} }
return null; return null;
......
...@@ -7,9 +7,11 @@ import 'dart:convert'; ...@@ -7,9 +7,11 @@ import 'dart:convert';
import 'package:mockito/mockito.dart'; import 'package:mockito/mockito.dart';
import 'package:flutter_tools/src/application_package.dart'; import 'package:flutter_tools/src/application_package.dart';
import 'package:flutter_tools/src/base/common.dart'; import 'package:flutter_tools/src/base/common.dart';
import 'package:flutter_tools/src/base/config.dart';
import 'package:flutter_tools/src/base/io.dart'; import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/terminal.dart'; import 'package:flutter_tools/src/base/terminal.dart';
import 'package:flutter_tools/src/ios/code_signing.dart'; import 'package:flutter_tools/src/ios/code_signing.dart';
import 'package:flutter_tools/src/globals.dart';
import 'package:process/process.dart'; import 'package:process/process.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
...@@ -18,11 +20,13 @@ import '../src/context.dart'; ...@@ -18,11 +20,13 @@ import '../src/context.dart';
void main() { void main() {
group('Auto signing', () { group('Auto signing', () {
ProcessManager mockProcessManager; ProcessManager mockProcessManager;
Config mockConfig;
BuildableIOSApp app; BuildableIOSApp app;
AnsiTerminal testTerminal; AnsiTerminal testTerminal;
setUp(() { setUp(() {
mockProcessManager = new MockProcessManager(); mockProcessManager = new MockProcessManager();
mockConfig = new MockConfig();
testTerminal = new TestTerminal(); testTerminal = new TestTerminal();
app = new BuildableIOSApp( app = new BuildableIOSApp(
projectBundleId: 'test.app', projectBundleId: 'test.app',
...@@ -198,11 +202,79 @@ void main() { ...@@ -198,11 +202,79 @@ void main() {
expect(testLogger.errorText, isEmpty); expect(testLogger.errorText, isEmpty);
verify(mockOpenSslStdIn.write('This is a mock certificate')); verify(mockOpenSslStdIn.write('This is a mock certificate'));
expect(developmentTeam, '4444DDDD44'); expect(developmentTeam, '4444DDDD44');
verify(config.setValue('ios-signing-cert', 'iPhone Developer: Profile 3 (3333CCCC33)'));
}, },
overrides: <Type, Generator>{ overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager, ProcessManager: () => mockProcessManager,
Config: () => mockConfig,
AnsiTerminal: () => testTerminal, AnsiTerminal: () => testTerminal,
}); });
testUsingContext('Test saved certificate used', () 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''',
''
));
when(mockProcessManager.runSync(
<String>['security', 'find-certificate', '-c', '3333CCCC33', '-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 3 (3333CCCC33)/OU=4444DDDD44/O=My Team/C=US'
))
));
when(mockOpenSslProcess.stderr).thenReturn(mockOpenSslStdErr);
when(mockOpenSslProcess.exitCode).thenReturn(0);
when(mockConfig.getValue('ios-signing-cert')).thenReturn('iPhone Developer: Profile 3 (3333CCCC33)');
final String developmentTeam = await getCodeSigningIdentityDevelopmentTeam(app);
expect(
testLogger.statusText,
contains('Found saved certificate choice "iPhone Developer: Profile 3 (3333CCCC33)". To clear, use "flutter config"')
);
expect(
testLogger.statusText,
contains('Signing iOS app for device deployment using developer identity: "iPhone Developer: Profile 3 (3333CCCC33)"')
);
expect(testLogger.errorText, isEmpty);
verify(mockOpenSslStdIn.write('This is a mock certificate'));
expect(developmentTeam, '4444DDDD44');
},
overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
Config: () => mockConfig,
});
}); });
} }
...@@ -224,6 +296,7 @@ class MockProcessManager extends Mock implements ProcessManager {} ...@@ -224,6 +296,7 @@ class MockProcessManager extends Mock implements ProcessManager {}
class MockProcess extends Mock implements Process {} class MockProcess extends Mock implements Process {}
class MockStream extends Mock implements Stream<List<int>> {} class MockStream extends Mock implements Stream<List<int>> {}
class MockStdIn extends Mock implements IOSink {} class MockStdIn extends Mock implements IOSink {}
class MockConfig extends Mock implements Config {}
Stream<String> mockTerminalStdInStream; Stream<String> mockTerminalStdInStream;
......
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