Add -runFirstLaunch command before opening Xcode during iOS symbol recovery (#2431)

* Add -runFirstLaunch command before opening Xcode during iOS symbol recovery

* Copy headBranch fix

* Revert lock
diff --git a/app_dart/test/src/utilities/entity_generators.dart b/app_dart/test/src/utilities/entity_generators.dart
index c3dba7e..52f43d9 100644
--- a/app_dart/test/src/utilities/entity_generators.dart
+++ b/app_dart/test/src/utilities/entity_generators.dart
@@ -195,12 +195,14 @@
 
 github.CheckSuite generateCheckSuite(
   int i, {
+  String headBranch = 'main',
   String headSha = 'abc',
   github.CheckRunConclusion conclusion = github.CheckRunConclusion.success,
   List<github.PullRequest> pullRequests = const <github.PullRequest>[],
 }) {
   return github.CheckSuite(
     id: i,
+    headBranch: headBranch,
     headSha: headSha,
     conclusion: conclusion,
     pullRequests: pullRequests,
diff --git a/device_doctor/lib/src/ios_debug_symbol_doctor.dart b/device_doctor/lib/src/ios_debug_symbol_doctor.dart
index f495dda..172573e 100644
--- a/device_doctor/lib/src/ios_debug_symbol_doctor.dart
+++ b/device_doctor/lib/src/ios_debug_symbol_doctor.dart
@@ -109,6 +109,28 @@
     if (timeoutSeconds == null) {
       throw ArgumentError('Could not parse an integer from the option --timeout="${argResults!['timeout']}"');
     }
+
+    // Prompt Xcode to first setup without opening the app.
+    // This will return very quickly if there is no work to do.
+    logger.info('Running Xcode first launch...');
+    final io.ProcessResult runFirstLaunchResult = await processManager.run(<String>[
+      'xcrun',
+      'xcodebuild',
+      '-runFirstLaunch',
+    ]);
+    final String runFirstLaunchStdout = runFirstLaunchResult.stdout.trim();
+    if (runFirstLaunchStdout.isNotEmpty) {
+      logger.info('stdout from `xcodebuild -runFirstLaunch`:\n$runFirstLaunchStdout\n');
+    }
+    final String runFirstLaunchStderr = runFirstLaunchResult.stderr.trim();
+    if (runFirstLaunchStderr.isNotEmpty) {
+      logger.info('stderr from `xcodebuild -runFirstLaunch`:\n$runFirstLaunchStderr\n');
+    }
+    final int runFirstLaunchCode = runFirstLaunchResult.exitCode;
+    if (runFirstLaunchCode != 0) {
+      logger.info('Failed running `xcodebuild -runFirstLaunch` with code $runFirstLaunchCode!');
+    }
+
     final Duration timeout = Duration(seconds: timeoutSeconds);
     logger.info('Launching Xcode...');
     final Future<io.ProcessResult> xcodeFuture = processManager.run(<String>[
diff --git a/device_doctor/test/src/ios_debug_symbol_doctor_test.dart b/device_doctor/test/src/ios_debug_symbol_doctor_test.dart
index 3dedecc..cd21955 100644
--- a/device_doctor/test/src/ios_debug_symbol_doctor_test.dart
+++ b/device_doctor/test/src/ios_debug_symbol_doctor_test.dart
@@ -80,6 +80,11 @@
 
     test('recover opens Xcode, waits, then kills it', () async {
       when(
+        processManager.run(<String>['xcrun', 'xcodebuild', '-runFirstLaunch']),
+      ).thenAnswer((_) async {
+        return ProcessResult(0, 0, '', '');
+      });
+      when(
         processManager.run(<String>['open', '-n', '-F', '-W', xcworkspacePath]),
       ).thenAnswer((_) async {
         return ProcessResult(1, 0, '', '');
@@ -114,6 +119,7 @@
       expect(
         logger.logs[Level.INFO],
         containsAllInOrder(<String>[
+          'Running Xcode first launch...',
           'Launching Xcode...',
           'Waiting for 300 seconds',
           'Waited for 300 seconds, now killing Xcode',