[codesign] function to Upload to notary server (#2111)
diff --git a/codesign/lib/src/file_codesign_visitor.dart b/codesign/lib/src/file_codesign_visitor.dart
index 7ebc23d..56f7d1e 100644
--- a/codesign/lib/src/file_codesign_visitor.dart
+++ b/codesign/lib/src/file_codesign_visitor.dart
@@ -92,6 +92,7 @@
''';
static const Duration _notarizationTimerDuration = Duration(seconds: 45);
static final RegExp _notarytoolStatusCheckPattern = RegExp(r'[ ]*status: ([a-zA-z ]+)');
+ static final RegExp _notarytoolRequestPattern = RegExp(r'id: ([a-z0-9-]+)');
static const String fixItInstructions = '''
Codesign test failed.
@@ -273,10 +274,6 @@
await completer.future;
}
- String uploadZipToNotary(File localFile, [int retryCount = 3]) {
- throw UnimplementedError('will implement later');
- }
-
/// Make a request to the notary service to see if the notary job is finished.
///
/// A return value of true means that notarization finished successfully,
@@ -319,4 +316,52 @@
}
throw CodesignException('Notarization failed with: $status\n$combinedOutput');
}
+
+ /// Upload artifact to Apple notary service.
+ String uploadZipToNotary(File localFile, [int retryCount = 3, int sleepTime = 1]) {
+ while (retryCount > 0) {
+ final List<String> args = <String>[
+ 'xcrun',
+ 'notarytool',
+ 'submit',
+ localFile.absolute.path,
+ '--apple-id',
+ codesignAppstoreId,
+ '--password',
+ appSpecificPassword,
+ '--team-id',
+ codesignTeamId,
+ ];
+
+ log.info('uploading ${args.join(' ')}');
+ final io.ProcessResult result = processManager.runSync(args);
+ if (result.exitCode != 0) {
+ throw CodesignException(
+ 'Command "${args.join(' ')}" failed with exit code ${result.exitCode}\nStdout: ${result.stdout}\nStderr: ${result.stderr}');
+ }
+
+ final String combinedOutput = (result.stdout as String) + (result.stderr as String);
+ final RegExpMatch? match;
+ match = _notarytoolRequestPattern.firstMatch(combinedOutput);
+
+ if (match == null) {
+ log.warning('Failed to upload to the notary service with args: ${args.join(' ')}');
+ log.warning('{combinedOutput.trim()}');
+ retryCount -= 1;
+ log.warning('Trying again $retryCount more time${retryCount > 1 ? 's' : ''}...');
+ io.sleep(Duration(seconds: sleepTime));
+ continue;
+ }
+
+ final String requestUuid = match.group(1)!;
+ log.info('RequestUUID for ${localFile.path} is: $requestUuid');
+
+ return requestUuid;
+ }
+ log.warning('The upload to notary service failed after retries, and'
+ ' the output format does not match the current notary tool version.'
+ ' If after inspecting the output, you believe the process finished '
+ 'successfully but was not detected, please contact flutter release engineers');
+ throw CodesignException('Failed to upload ${localFile.path} to the notary service');
+ }
}
diff --git a/codesign/test/file_codesign_visitor_test.dart b/codesign/test/file_codesign_visitor_test.dart
index 4890a24..1b2bdb4 100644
--- a/codesign/test/file_codesign_visitor_test.dart
+++ b/codesign/test/file_codesign_visitor_test.dart
@@ -599,5 +599,180 @@
),
);
});
+
+ test('upload notary retries upon failure', () async {
+ fileSystem.file('${tempDir.absolute.path}/temp').createSync();
+ processManager.addCommands(<FakeCommand>[
+ FakeCommand(
+ command: <String>[
+ 'xcrun',
+ 'notarytool',
+ 'submit',
+ '${tempDir.absolute.path}/temp',
+ '--apple-id',
+ randomString,
+ '--password',
+ randomString,
+ '--team-id',
+ randomString,
+ ],
+ stdout: '''Error uploading file.
+ Id: something that causes failure
+ path: /Users/flutter/Desktop/OvernightTextEditor_11.6.8.zip''',
+ ),
+ FakeCommand(
+ command: <String>[
+ 'xcrun',
+ 'notarytool',
+ 'submit',
+ '${tempDir.absolute.path}/temp',
+ '--apple-id',
+ randomString,
+ '--password',
+ randomString,
+ '--team-id',
+ randomString,
+ ],
+ stdout: '''Successfully uploaded file.
+ id: 2efe2717-52ef-43a5-96dc-0797e4ca1041
+ path: /Users/flutter/Desktop/OvernightTextEditor_11.6.8.zip''',
+ ),
+ ]);
+
+ final String uuid = codesignVisitor.uploadZipToNotary(
+ fileSystem.file('${tempDir.absolute.path}/temp'),
+ 3,
+ 0,
+ );
+ expect(uuid, '2efe2717-52ef-43a5-96dc-0797e4ca1041');
+ final List<String> messages = records
+ .where((LogRecord record) => record.level == Level.WARNING)
+ .map((LogRecord record) => record.message)
+ .toList();
+ expect(
+ messages,
+ contains('Failed to upload to the notary service with args: '
+ 'xcrun notarytool submit ${tempDir.absolute.path}/temp '
+ '--apple-id abcd1234 --password abcd1234 --team-id abcd1234'),
+ );
+ expect(
+ messages,
+ contains('Trying again 2 more times...'),
+ );
+ });
+
+ test('upload notary throws exception if exit code is unnormal', () async {
+ fileSystem.file('${tempDir.absolute.path}/temp').createSync();
+ processManager.addCommands(<FakeCommand>[
+ FakeCommand(
+ command: <String>[
+ 'xcrun',
+ 'notarytool',
+ 'submit',
+ '${tempDir.absolute.path}/temp',
+ '--apple-id',
+ randomString,
+ '--password',
+ randomString,
+ '--team-id',
+ randomString,
+ ],
+ stdout: '''Error uploading file.
+ Id: something that causes failure
+ path: /Users/flutter/Desktop/OvernightTextEditor_11.6.8.zip''',
+ exitCode: -1,
+ ),
+ ]);
+
+ expect(
+ () => codesignVisitor.uploadZipToNotary(
+ fileSystem.file('${tempDir.absolute.path}/temp'),
+ 1,
+ 0,
+ ),
+ throwsA(
+ isA<CodesignException>(),
+ ),
+ );
+ });
+
+ test('upload notary throws exception after 3 default tries', () async {
+ fileSystem.file('${tempDir.absolute.path}/temp').createSync();
+ processManager.addCommands(<FakeCommand>[
+ FakeCommand(
+ command: <String>[
+ 'xcrun',
+ 'notarytool',
+ 'submit',
+ '${tempDir.absolute.path}/temp',
+ '--apple-id',
+ randomString,
+ '--password',
+ randomString,
+ '--team-id',
+ randomString,
+ ],
+ stdout: '''Error uploading file.
+ Id: something that causes failure
+ path: /Users/flutter/Desktop/OvernightTextEditor_11.6.8.zip''',
+ ),
+ FakeCommand(
+ command: <String>[
+ 'xcrun',
+ 'notarytool',
+ 'submit',
+ '${tempDir.absolute.path}/temp',
+ '--apple-id',
+ randomString,
+ '--password',
+ randomString,
+ '--team-id',
+ randomString,
+ ],
+ stdout: '''Error uploading file.
+ Id: something that causes failure
+ path: /Users/flutter/Desktop/OvernightTextEditor_11.6.8.zip''',
+ ),
+ FakeCommand(
+ command: <String>[
+ 'xcrun',
+ 'notarytool',
+ 'submit',
+ '${tempDir.absolute.path}/temp',
+ '--apple-id',
+ randomString,
+ '--password',
+ randomString,
+ '--team-id',
+ randomString,
+ ],
+ stdout: '''Error uploading file.
+ Id: something that causes failure
+ path: /Users/flutter/Desktop/OvernightTextEditor_11.6.8.zip''',
+ ),
+ ]);
+
+ expect(
+ () => codesignVisitor.uploadZipToNotary(
+ fileSystem.file('${tempDir.absolute.path}/temp'),
+ 3,
+ 0,
+ ),
+ throwsA(
+ isA<CodesignException>(),
+ ),
+ );
+ final List<String> messages = records
+ .where((LogRecord record) => record.level == Level.WARNING)
+ .map((LogRecord record) => record.message)
+ .toList();
+ expect(
+ messages,
+ contains('The upload to notary service failed after retries, and'
+ ' the output format does not match the current notary tool version.'
+ ' If after inspecting the output, you believe the process finished '
+ 'successfully but was not detected, please contact flutter release engineers'),
+ );
+ });
});
}