Skip to content

Commit ffb76ba

Browse files
xstergspencergoog
authored andcommitted
Guess sign iOS with the first certificate when running in machine mode (flutter#10870)
* Guess sign with the first certificate when multiple are available in machine mode * review
1 parent 16c593a commit ffb76ba

File tree

10 files changed

+103
-16
lines changed

10 files changed

+103
-16
lines changed

packages/flutter_tools/lib/src/android/android_device.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,7 @@ class AndroidDevice extends Device {
341341
String kernelPath,
342342
bool prebuiltApplication: false,
343343
bool applicationNeedsRebuild: false,
344+
bool usesTerminalUi: true,
344345
}) async {
345346
if (!await _checkForSupportedAdbVersion() || !await _checkForSupportedAndroidVersion())
346347
return new LaunchResult.failed();

packages/flutter_tools/lib/src/commands/drive.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,7 @@ Future<LaunchResult> _startApp(DriveCommand command) async {
260260
diagnosticPort: command.diagnosticPort,
261261
),
262262
platformArgs: platformArgs,
263+
usesTerminalUi: false,
263264
);
264265

265266
if (!result.started) {

packages/flutter_tools/lib/src/device.dart

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,11 @@ abstract class Device {
222222
///
223223
/// [platformArgs] allows callers to pass platform-specific arguments to the
224224
/// start call. The build mode is not used by all platforms.
225+
///
226+
/// If [usesTerminalUi] is true, Flutter Tools may attempt to prompt the
227+
/// user to resolve fixable issues such as selecting a signing certificate
228+
/// for iOS device deployment. Set to false if stdin cannot be read from while
229+
/// attempting to start the app.
225230
Future<LaunchResult> startApp(
226231
ApplicationPackage package,
227232
BuildMode mode, {
@@ -231,7 +236,8 @@ abstract class Device {
231236
Map<String, dynamic> platformArgs,
232237
String kernelPath,
233238
bool prebuiltApplication: false,
234-
bool applicationNeedsRebuild: false
239+
bool applicationNeedsRebuild: false,
240+
bool usesTerminalUi: true,
235241
});
236242

237243
/// Does this device implement support for hot reloading / restarting?

packages/flutter_tools/lib/src/fuchsia/fuchsia_device.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ class FuchsiaDevice extends Device {
6868
bool prebuiltApplication: false,
6969
String kernelPath,
7070
bool applicationNeedsRebuild: false,
71+
bool usesTerminalUi: false,
7172
}) => new Future<Null>.error('unimplemented');
7273

7374
@override

packages/flutter_tools/lib/src/ios/code_signing.dart

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ final RegExp _certificateOrganizationalUnitExtractionPattern = new RegExp(r'OU=(
8080
///
8181
/// Will return null if none are found, if the user cancels or if the Xcode
8282
/// project has a development team set in the project's build settings.
83-
Future<String> getCodeSigningIdentityDevelopmentTeam(BuildableIOSApp iosApp) async{
83+
Future<String> getCodeSigningIdentityDevelopmentTeam({BuildableIOSApp iosApp, bool usesTerminalUi: true}) async{
8484
if (iosApp.buildSettings == null)
8585
return null;
8686

@@ -115,7 +115,7 @@ Future<String> getCodeSigningIdentityDevelopmentTeam(BuildableIOSApp iosApp) asy
115115
.toSet() // Unique.
116116
.toList();
117117

118-
final String signingIdentity = await _chooseSigningIdentity(validCodeSigningIdentities);
118+
final String signingIdentity = await _chooseSigningIdentity(validCodeSigningIdentities, usesTerminalUi);
119119

120120
// If none are chosen, return null.
121121
if (signingIdentity == null)
@@ -153,7 +153,7 @@ Future<String> getCodeSigningIdentityDevelopmentTeam(BuildableIOSApp iosApp) asy
153153
?.group(1);
154154
}
155155

156-
Future<String> _chooseSigningIdentity(List<String> validCodeSigningIdentities) async {
156+
Future<String> _chooseSigningIdentity(List<String> validCodeSigningIdentities, bool usesTerminalUi) async {
157157
// The user has no valid code signing identities.
158158
if (validCodeSigningIdentities.isEmpty) {
159159
printError(noCertificatesInstruction, emphasis: true);
@@ -176,6 +176,11 @@ Future<String> _chooseSigningIdentity(List<String> validCodeSigningIdentities) a
176176
}
177177
}
178178

179+
// If terminal UI can't be used, just attempt with the first valid certificate
180+
// since we can't ask the user.
181+
if (!usesTerminalUi)
182+
return validCodeSigningIdentities.first;
183+
179184
final int count = validCodeSigningIdentities.length;
180185
printStatus(
181186
'Multiple valid development certificates available (your choice will be saved):',

packages/flutter_tools/lib/src/ios/devices.dart

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,13 +175,20 @@ class IOSDevice extends Device {
175175
bool prebuiltApplication: false,
176176
String kernelPath,
177177
bool applicationNeedsRebuild: false,
178+
bool usesTerminalUi: true,
178179
}) async {
179180
if (!prebuiltApplication) {
180181
// TODO(chinmaygarde): Use mainPath, route.
181182
printTrace('Building ${app.name} for $id');
182183

183184
// Step 1: Build the precompiled/DBC application if necessary.
184-
final XcodeBuildResult buildResult = await buildXcodeProject(app: app, mode: mode, target: mainPath, buildForDevice: true);
185+
final XcodeBuildResult buildResult = await buildXcodeProject(
186+
app: app,
187+
mode: mode,
188+
target: mainPath,
189+
buildForDevice: true,
190+
usesTerminalUi: usesTerminalUi,
191+
);
185192
if (!buildResult.success) {
186193
printError('Could not build the precompiled application for the device.');
187194
await diagnoseXcodeBuildFailure(buildResult, app);

packages/flutter_tools/lib/src/ios/mac.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,8 @@ Future<XcodeBuildResult> buildXcodeProject({
193193
BuildMode mode,
194194
String target: flx.defaultMainPath,
195195
bool buildForDevice,
196-
bool codesign: true
196+
bool codesign: true,
197+
bool usesTerminalUi: true,
197198
}) async {
198199
if (!_checkXcodeVersion())
199200
return new XcodeBuildResult(success: false);
@@ -205,7 +206,7 @@ Future<XcodeBuildResult> buildXcodeProject({
205206

206207
String developmentTeam;
207208
if (codesign && buildForDevice)
208-
developmentTeam = await getCodeSigningIdentityDevelopmentTeam(app);
209+
developmentTeam = await getCodeSigningIdentityDevelopmentTeam(iosApp: app, usesTerminalUi: usesTerminalUi);
209210

210211
// Before the build, all service definitions must be updated and the dylibs
211212
// copied over to a location that is suitable for Xcodebuild to find them.

packages/flutter_tools/lib/src/ios/simulators.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,7 @@ class IOSSimulator extends Device {
315315
String kernelPath,
316316
bool prebuiltApplication: false,
317317
bool applicationNeedsRebuild: false,
318+
bool usesTerminalUi: true,
318319
}) async {
319320
if (!prebuiltApplication) {
320321
printTrace('Building ${app.name} for $id.');

packages/flutter_tools/lib/src/resident_runner.dart

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,8 @@ class FlutterDevice {
239239
route: route,
240240
prebuiltApplication: prebuiltMode,
241241
kernelPath: hotRunner.kernelFilePath,
242-
applicationNeedsRebuild: shouldBuild || hasDirtyDependencies
242+
applicationNeedsRebuild: shouldBuild || hasDirtyDependencies,
243+
usesTerminalUi: hotRunner.usesTerminalUI,
243244
);
244245

245246
final LaunchResult result = await futureResult;
@@ -298,7 +299,8 @@ class FlutterDevice {
298299
platformArgs: platformArgs,
299300
route: route,
300301
prebuiltApplication: prebuiltMode,
301-
applicationNeedsRebuild: shouldBuild || hasDirtyDependencies
302+
applicationNeedsRebuild: shouldBuild || hasDirtyDependencies,
303+
usesTerminalUi: coldRunner.usesTerminalUI,
302304
);
303305

304306
if (!result.started) {

packages/flutter_tools/test/ios/code_signing_test.dart

Lines changed: 69 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ void main() {
3838

3939
testUsingContext('No auto-sign if Xcode project settings are not available', () async {
4040
app = new BuildableIOSApp(projectBundleId: 'test.app');
41-
final String developmentTeam = await getCodeSigningIdentityDevelopmentTeam(app);
41+
final String developmentTeam = await getCodeSigningIdentityDevelopmentTeam(iosApp: app);
4242
expect(developmentTeam, isNull);
4343
});
4444

@@ -49,7 +49,7 @@ void main() {
4949
'DEVELOPMENT_TEAM': 'abc',
5050
},
5151
);
52-
final String developmentTeam = await getCodeSigningIdentityDevelopmentTeam(app);
52+
final String developmentTeam = await getCodeSigningIdentityDevelopmentTeam(iosApp: app);
5353
expect(developmentTeam, isNull);
5454
expect(testLogger.statusText, equals(
5555
'Automatically signing iOS for device deployment using specified development team in Xcode project: abc\n'
@@ -59,7 +59,7 @@ void main() {
5959
testUsingContext('No auto-sign if security or openssl not available', () async {
6060
when(mockProcessManager.runSync(<String>['which', 'security']))
6161
.thenReturn(exitsFail);
62-
final String developmentTeam = await getCodeSigningIdentityDevelopmentTeam(app);
62+
final String developmentTeam = await getCodeSigningIdentityDevelopmentTeam(iosApp: app);
6363
expect(developmentTeam, isNull);
6464
},
6565
overrides: <Type, Generator>{
@@ -77,7 +77,7 @@ void main() {
7777

7878
String developmentTeam;
7979
try {
80-
developmentTeam = await getCodeSigningIdentityDevelopmentTeam(app);
80+
developmentTeam = await getCodeSigningIdentityDevelopmentTeam(iosApp: app);
8181
fail('No identity should throw tool error');
8282
} on ToolExit {
8383
expect(developmentTeam, isNull);
@@ -131,7 +131,7 @@ void main() {
131131
when(mockProcess.stderr).thenReturn(mockStdErr);
132132
when(mockProcess.exitCode).thenReturn(0);
133133

134-
final String developmentTeam = await getCodeSigningIdentityDevelopmentTeam(app);
134+
final String developmentTeam = await getCodeSigningIdentityDevelopmentTeam(iosApp: app);
135135

136136
expect(testLogger.statusText, contains('iPhone Developer: Profile 1 (1111AAAA11)'));
137137
expect(testLogger.errorText, isEmpty);
@@ -189,7 +189,7 @@ void main() {
189189
when(mockOpenSslProcess.stderr).thenReturn(mockOpenSslStdErr);
190190
when(mockOpenSslProcess.exitCode).thenReturn(0);
191191

192-
final String developmentTeam = await getCodeSigningIdentityDevelopmentTeam(app);
192+
final String developmentTeam = await getCodeSigningIdentityDevelopmentTeam(iosApp: app);
193193

194194
expect(
195195
testLogger.statusText,
@@ -211,6 +211,68 @@ void main() {
211211
AnsiTerminal: () => testTerminal,
212212
});
213213

214+
testUsingContext('Test multiple identity in machine mode works', () async {
215+
when(mockProcessManager.runSync(<String>['which', 'security']))
216+
.thenReturn(exitsHappy);
217+
when(mockProcessManager.runSync(<String>['which', 'openssl']))
218+
.thenReturn(exitsHappy);
219+
when(mockProcessManager.runSync(
220+
argThat(contains('find-identity')), environment: any, workingDirectory: any,
221+
)).thenReturn(new ProcessResult(
222+
1, // pid
223+
0, // exitCode
224+
'''
225+
1) 86f7e437faa5a7fce15d1ddcb9eaeaea377667b8 "iPhone Developer: Profile 1 (1111AAAA11)"
226+
2) da4b9237bacccdf19c0760cab7aec4a8359010b0 "iPhone Developer: Profile 2 (2222BBBB22)"
227+
3) 5bf1fd927dfb8679496a2e6cf00cbe50c1c87145 "iPhone Developer: Profile 3 (3333CCCC33)"
228+
3 valid identities found''',
229+
''
230+
));
231+
mockTerminalStdInStream =
232+
new Stream<String>.fromFuture(new Future<String>.error(new Exception('Cannot read from StdIn')));
233+
when(mockProcessManager.runSync(
234+
<String>['security', 'find-certificate', '-c', '1111AAAA11', '-p'],
235+
environment: any,
236+
workingDirectory: any,
237+
)).thenReturn(new ProcessResult(
238+
1, // pid
239+
0, // exitCode
240+
'This is a mock certificate',
241+
'',
242+
));
243+
244+
final MockProcess mockOpenSslProcess = new MockProcess();
245+
final MockStdIn mockOpenSslStdIn = new MockStdIn();
246+
final MockStream mockOpenSslStdErr = new MockStream();
247+
248+
when(mockProcessManager.start(
249+
argThat(contains('openssl')), environment: any, workingDirectory: any,
250+
)).thenReturn(new Future<Process>.value(mockOpenSslProcess));
251+
252+
when(mockOpenSslProcess.stdin).thenReturn(mockOpenSslStdIn);
253+
when(mockOpenSslProcess.stdout).thenReturn(new Stream<List<int>>.fromFuture(
254+
new Future<List<int>>.value(UTF8.encode(
255+
'subject= /CN=iPhone Developer: Profile 1 (1111AAAA11)/OU=5555EEEE55/O=My Team/C=US'
256+
)),
257+
));
258+
when(mockOpenSslProcess.stderr).thenReturn(mockOpenSslStdErr);
259+
when(mockOpenSslProcess.exitCode).thenReturn(0);
260+
261+
final String developmentTeam = await getCodeSigningIdentityDevelopmentTeam(iosApp: app, usesTerminalUi: false);
262+
263+
expect(
264+
testLogger.statusText,
265+
contains('Signing iOS app for device deployment using developer identity: "iPhone Developer: Profile 1 (1111AAAA11)"'),
266+
);
267+
expect(testLogger.errorText, isEmpty);
268+
verify(mockOpenSslStdIn.write('This is a mock certificate'));
269+
expect(developmentTeam, '5555EEEE55');
270+
},
271+
overrides: <Type, Generator>{
272+
ProcessManager: () => mockProcessManager,
273+
AnsiTerminal: () => testTerminal,
274+
});
275+
214276
testUsingContext('Test saved certificate used', () async {
215277
when(mockProcessManager.runSync(<String>['which', 'security']))
216278
.thenReturn(exitsHappy);
@@ -257,7 +319,7 @@ void main() {
257319
when(mockOpenSslProcess.exitCode).thenReturn(0);
258320
when(mockConfig.getValue('ios-signing-cert')).thenReturn('iPhone Developer: Profile 3 (3333CCCC33)');
259321

260-
final String developmentTeam = await getCodeSigningIdentityDevelopmentTeam(app);
322+
final String developmentTeam = await getCodeSigningIdentityDevelopmentTeam(iosApp: app);
261323

262324
expect(
263325
testLogger.statusText,

0 commit comments

Comments
 (0)