Save development certificate choice (#10849)
This commit is contained in:
parent
265257ab6d
commit
02245234b6
@ -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 {
|
|||||||
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
|
|||||||
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
|
|||||||
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';
|
|||||||
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';
|
|||||||
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() {
|
|||||||
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 {}
|
|||||||
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;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user