Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Commit 0916375

Browse files
authored
[tools]Build IPA validation UI Polish (#116744)
* [tools]some ui polish for build ipa validation * do not print out a few success validations * rename installed type to success for more general usage * forgot nit after reverting custom validation types and re-use doctor types
1 parent c98978a commit 0916375

27 files changed

+221
-134
lines changed

dev/devicelab/bin/tasks/ios_content_validation_test.dart

+17-15
Original file line numberDiff line numberDiff line change
@@ -42,21 +42,23 @@ Future<void> main() async {
4242
throw TaskResult.failure('Usage archive event not sent');
4343
}
4444

45-
if (!output.contains('Warning: App icon is using the wrong size (e.g. [email protected]).')) {
46-
throw TaskResult.failure('Must validate incorrect app icon image size.');
47-
}
48-
49-
// The project is still using Flutter template icon and launch image.
50-
if (!output.contains('Warning: App icon is set to the default placeholder icon. Replace with unique icons.')) {
51-
throw TaskResult.failure('Must validate template app icon.');
52-
}
53-
if (!output.contains('Warning: Launch image is set to the default placeholder. Replace with unique launch images.')) {
54-
throw TaskResult.failure('Must validate template launch image.');
55-
}
56-
57-
// The project is still using com.example as bundle identifier prefix.
58-
if (!output.contains('Warning: Your application still contains the default "com.example" bundle identifier.')) {
59-
throw TaskResult.failure('Must validate the default bundle identifier prefix');
45+
// The output contains extra time related prefix, so cannot use a single string.
46+
const List<String> expectedValidationMessages = <String>[
47+
'[!] App Settings Validation\n',
48+
' • Version Number: 1.0.0\n',
49+
' • Build Number: 1\n',
50+
' • Display Name: Hello\n',
51+
' • Deployment Target: 11.0\n',
52+
' • Bundle Identifier: com.example.hello\n',
53+
' ! Your application still contains the default "com.example" bundle identifier.\n',
54+
'[!] App Icon and Launch Image Assets Validation\n',
55+
' ! App icon is set to the default placeholder icon. Replace with unique icons.\n',
56+
' ! App icon is using the incorrect size (e.g. [email protected]).\n',
57+
' ! Launch image is set to the default placeholder icon. Replace with unique launch image.\n',
58+
'To update the settings, please refer to https://docs.flutter.dev/deployment/ios\n',
59+
];
60+
if (expectedValidationMessages.any((String message) => !output.contains(message))) {
61+
throw TaskResult.failure('Must have the expected validation message');
6062
}
6163
});
6264

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ class AndroidStudioValidator extends DoctorValidator {
7373
if (_studio.isValid) {
7474
type = _hasIssues(messages)
7575
? ValidationType.partial
76-
: ValidationType.installed;
76+
: ValidationType.success;
7777
messages.addAll(_studio.validationMessages.map<ValidationMessage>(
7878
(String m) => ValidationMessage(m),
7979
));

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ class AndroidValidator extends DoctorValidator {
258258
}
259259

260260
// Success.
261-
return ValidationResult(ValidationType.installed, messages, statusInfo: sdkVersionText);
261+
return ValidationResult(ValidationType.success, messages, statusInfo: sdkVersionText);
262262
}
263263
}
264264

@@ -327,7 +327,7 @@ class AndroidLicenseValidator extends DoctorValidator {
327327
messages.add(ValidationMessage.error(_userMessages.androidLicensesUnknown(_platform)));
328328
return ValidationResult(ValidationType.partial, messages, statusInfo: sdkVersionText);
329329
}
330-
return ValidationResult(ValidationType.installed, messages, statusInfo: sdkVersionText);
330+
return ValidationResult(ValidationType.success, messages, statusInfo: sdkVersionText);
331331
}
332332

333333
Future<bool> _checkJavaVersionNoOutput() async {

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

+88-21
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import '../base/process.dart';
1515
import '../base/utils.dart';
1616
import '../build_info.dart';
1717
import '../convert.dart';
18+
import '../doctor_validator.dart';
1819
import '../globals.dart' as globals;
1920
import '../ios/application_package.dart';
2021
import '../ios/mac.dart';
@@ -277,7 +278,27 @@ class BuildIOSArchiveCommand extends _BuildIOSSubCommand {
277278
.toList();
278279
}
279280

280-
Future<void> _validateIconAssetsAfterArchive(StringBuffer messageBuffer) async {
281+
ValidationResult? _createValidationResult(String title, List<ValidationMessage> messages) {
282+
if (messages.isEmpty) {
283+
return null;
284+
}
285+
final bool anyInvalid = messages.any((ValidationMessage message) => message.type != ValidationMessageType.information);
286+
return ValidationResult(
287+
anyInvalid ? ValidationType.partial : ValidationType.success,
288+
messages,
289+
statusInfo: title,
290+
);
291+
}
292+
293+
ValidationMessage _createValidationMessage({
294+
required bool isValid,
295+
required String message,
296+
}) {
297+
// Use "information" type for valid message, and "hint" type for invalid message.
298+
return isValid ? ValidationMessage(message) : ValidationMessage.hint(message);
299+
}
300+
301+
Future<List<ValidationMessage>> _validateIconAssetsAfterArchive() async {
281302
final BuildableIOSApp app = await buildableIOSApp;
282303

283304
final Map<_ImageAssetFileKey, String> templateInfoMap = _parseImageAssetContentsJson(
@@ -287,24 +308,35 @@ class BuildIOSArchiveCommand extends _BuildIOSSubCommand {
287308
app.projectAppIconDirName,
288309
requiresSize: true);
289310

311+
final List<ValidationMessage> validationMessages = <ValidationMessage>[];
312+
290313
final bool usesTemplate = _isAssetStillUsingTemplateFiles(
291314
templateImageInfoMap: templateInfoMap,
292315
projectImageInfoMap: projectInfoMap,
293316
templateImageDirName: await app.templateAppIconDirNameForImages,
294317
projectImageDirName: app.projectAppIconDirName);
318+
295319
if (usesTemplate) {
296-
messageBuffer.writeln('\nWarning: App icon is set to the default placeholder icon. Replace with unique icons.');
320+
validationMessages.add(_createValidationMessage(
321+
isValid: false,
322+
message: 'App icon is set to the default placeholder icon. Replace with unique icons.',
323+
));
297324
}
298325

299326
final List<String> filesWithWrongSize = _imageFilesWithWrongSize(
300327
imageInfoMap: projectInfoMap,
301328
imageDirName: app.projectAppIconDirName);
329+
302330
if (filesWithWrongSize.isNotEmpty) {
303-
messageBuffer.writeln('\nWarning: App icon is using the wrong size (e.g. ${filesWithWrongSize.first}).');
331+
validationMessages.add(_createValidationMessage(
332+
isValid: false,
333+
message: 'App icon is using the incorrect size (e.g. ${filesWithWrongSize.first}).',
334+
));
304335
}
336+
return validationMessages;
305337
}
306338

307-
Future<void> _validateLaunchImageAssetsAfterArchive(StringBuffer messageBuffer) async {
339+
Future<List<ValidationMessage>> _validateLaunchImageAssetsAfterArchive() async {
308340
final BuildableIOSApp app = await buildableIOSApp;
309341

310342
final Map<_ImageAssetFileKey, String> templateInfoMap = _parseImageAssetContentsJson(
@@ -314,25 +346,32 @@ class BuildIOSArchiveCommand extends _BuildIOSSubCommand {
314346
app.projectLaunchImageDirName,
315347
requiresSize: false);
316348

349+
final List<ValidationMessage> validationMessages = <ValidationMessage>[];
350+
317351
final bool usesTemplate = _isAssetStillUsingTemplateFiles(
318352
templateImageInfoMap: templateInfoMap,
319353
projectImageInfoMap: projectInfoMap,
320354
templateImageDirName: await app.templateLaunchImageDirNameForImages,
321355
projectImageDirName: app.projectLaunchImageDirName);
322356

323357
if (usesTemplate) {
324-
messageBuffer.writeln('\nWarning: Launch image is set to the default placeholder. Replace with unique launch images.');
358+
validationMessages.add(_createValidationMessage(
359+
isValid: false,
360+
message: 'Launch image is set to the default placeholder icon. Replace with unique launch image.',
361+
));
325362
}
363+
364+
return validationMessages;
326365
}
327366

328-
Future<void> _validateXcodeBuildSettingsAfterArchive(StringBuffer messageBuffer) async {
367+
Future<List<ValidationMessage>> _validateXcodeBuildSettingsAfterArchive() async {
329368
final BuildableIOSApp app = await buildableIOSApp;
330369

331370
final String plistPath = app.builtInfoPlistPathAfterArchive;
332371

333372
if (!globals.fs.file(plistPath).existsSync()) {
334373
globals.printError('Invalid iOS archive. Does not contain Info.plist.');
335-
return;
374+
return <ValidationMessage>[];
336375
}
337376

338377
final Map<String, String?> xcodeProjectSettingsMap = <String, String?>{};
@@ -343,17 +382,32 @@ class BuildIOSArchiveCommand extends _BuildIOSSubCommand {
343382
xcodeProjectSettingsMap['Deployment Target'] = globals.plistParser.getStringValueFromFile(plistPath, PlistParser.kMinimumOSVersionKey);
344383
xcodeProjectSettingsMap['Bundle Identifier'] = globals.plistParser.getStringValueFromFile(plistPath, PlistParser.kCFBundleIdentifierKey);
345384

346-
xcodeProjectSettingsMap.forEach((String title, String? info) {
347-
messageBuffer.writeln('$title: ${info ?? "Missing"}');
348-
});
385+
final List<ValidationMessage> validationMessages = xcodeProjectSettingsMap.entries.map((MapEntry<String, String?> entry) {
386+
final String title = entry.key;
387+
final String? info = entry.value;
388+
return _createValidationMessage(
389+
isValid: info != null,
390+
message: '$title: ${info ?? "Missing"}',
391+
);
392+
}).toList();
349393

350-
if (xcodeProjectSettingsMap.values.any((String? element) => element == null)) {
351-
messageBuffer.writeln('\nYou must set up the missing settings.');
394+
final bool hasMissingSettings = xcodeProjectSettingsMap.values.any((String? element) => element == null);
395+
if (hasMissingSettings) {
396+
validationMessages.add(_createValidationMessage(
397+
isValid: false,
398+
message: 'You must set up the missing app settings.'),
399+
);
352400
}
353401

354-
if (xcodeProjectSettingsMap['Bundle Identifier']?.startsWith('com.example') ?? false) {
355-
messageBuffer.writeln('\nWarning: Your application still contains the default "com.example" bundle identifier.');
402+
final bool usesDefaultBundleIdentifier = xcodeProjectSettingsMap['Bundle Identifier']?.startsWith('com.example') ?? false;
403+
if (usesDefaultBundleIdentifier) {
404+
validationMessages.add(_createValidationMessage(
405+
isValid: false,
406+
message: 'Your application still contains the default "com.example" bundle identifier.'),
407+
);
356408
}
409+
410+
return validationMessages;
357411
}
358412

359413
@override
@@ -362,13 +416,26 @@ class BuildIOSArchiveCommand extends _BuildIOSSubCommand {
362416
displayNullSafetyMode(buildInfo);
363417
final FlutterCommandResult xcarchiveResult = await super.runCommand();
364418

365-
final StringBuffer validationMessageBuffer = StringBuffer();
366-
await _validateXcodeBuildSettingsAfterArchive(validationMessageBuffer);
367-
await _validateIconAssetsAfterArchive(validationMessageBuffer);
368-
await _validateLaunchImageAssetsAfterArchive(validationMessageBuffer);
369-
370-
validationMessageBuffer.write('\nTo update the settings, please refer to https://docs.flutter.dev/deployment/ios');
371-
globals.printBox(validationMessageBuffer.toString(), title: 'App Settings');
419+
final List<ValidationResult?> validationResults = <ValidationResult?>[];
420+
validationResults.add(_createValidationResult(
421+
'App Settings Validation',
422+
await _validateXcodeBuildSettingsAfterArchive(),
423+
));
424+
validationResults.add(_createValidationResult(
425+
'App Icon and Launch Image Assets Validation',
426+
await _validateIconAssetsAfterArchive() + await _validateLaunchImageAssetsAfterArchive(),
427+
));
428+
429+
for (final ValidationResult result in validationResults.whereType<ValidationResult>()) {
430+
globals.printStatus('\n${result.coloredLeadingBox} ${result.statusInfo}');
431+
for (final ValidationMessage message in result.messages) {
432+
globals.printStatus(
433+
'${message.coloredIndicator} ${message.message}',
434+
indent: result.leadingBox.length + 1,
435+
);
436+
}
437+
}
438+
globals.printStatus('\nTo update the settings, please refer to https://docs.flutter.dev/deployment/ios\n');
372439

373440
// xcarchive failed or not at expected location.
374441
if (xcarchiveResult.exitStatus != ExitStatus.success) {

packages/flutter_tools/lib/src/doctor.dart

+6-6
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ class Doctor {
303303
case ValidationType.notAvailable:
304304
lineBuffer.write('is not available.');
305305
break;
306-
case ValidationType.installed:
306+
case ValidationType.success:
307307
lineBuffer.write('is fully installed.');
308308
break;
309309
}
@@ -320,7 +320,7 @@ class Doctor {
320320
));
321321
buffer.writeln();
322322

323-
if (result.type != ValidationType.installed) {
323+
if (result.type != ValidationType.success) {
324324
missingComponent = true;
325325
}
326326
}
@@ -400,7 +400,7 @@ class Doctor {
400400
case ValidationType.notAvailable:
401401
issues += 1;
402402
break;
403-
case ValidationType.installed:
403+
case ValidationType.success:
404404
break;
405405
}
406406
if (sendEvent) {
@@ -558,7 +558,7 @@ class FlutterValidator extends DoctorValidator {
558558

559559
ValidationType valid;
560560
if (messages.every((ValidationMessage message) => message.isInformation)) {
561-
valid = ValidationType.installed;
561+
valid = ValidationType.success;
562562
} else {
563563
// The issues for this validator stem from broken git configuration of the local install;
564564
// in that case, make it clear that it is fine to continue, but freshness check/upgrades
@@ -707,13 +707,13 @@ class DeviceValidator extends DoctorValidator {
707707
} else if (diagnostics.isNotEmpty) {
708708
installedMessages.addAll(diagnosticMessages);
709709
return ValidationResult(
710-
ValidationType.installed,
710+
ValidationType.success,
711711
installedMessages,
712712
statusInfo: _userMessages.devicesAvailable(devices.length)
713713
);
714714
} else {
715715
return ValidationResult(
716-
ValidationType.installed,
716+
ValidationType.success,
717717
installedMessages,
718718
statusInfo: _userMessages.devicesAvailable(devices.length)
719719
);

packages/flutter_tools/lib/src/doctor_validator.dart

+7-7
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ enum ValidationType {
3636
missing,
3737
partial,
3838
notAvailable,
39-
installed,
39+
success,
4040
}
4141

4242
enum ValidationMessageType {
@@ -116,7 +116,7 @@ class GroupedValidator extends DoctorValidator {
116116
for (final ValidationResult result in results) {
117117
statusInfo ??= result.statusInfo;
118118
switch (result.type) {
119-
case ValidationType.installed:
119+
case ValidationType.success:
120120
if (mergedType == ValidationType.missing) {
121121
mergedType = ValidationType.partial;
122122
}
@@ -127,7 +127,7 @@ class GroupedValidator extends DoctorValidator {
127127
break;
128128
case ValidationType.crash:
129129
case ValidationType.missing:
130-
if (mergedType == ValidationType.installed) {
130+
if (mergedType == ValidationType.success) {
131131
mergedType = ValidationType.partial;
132132
}
133133
break;
@@ -142,7 +142,7 @@ class GroupedValidator extends DoctorValidator {
142142

143143
@immutable
144144
class ValidationResult {
145-
/// [ValidationResult.type] should only equal [ValidationResult.installed]
145+
/// [ValidationResult.type] should only equal [ValidationResult.success]
146146
/// if no [messages] are hints or errors.
147147
const ValidationResult(this.type, this.messages, { this.statusInfo });
148148

@@ -171,7 +171,7 @@ class ValidationResult {
171171
return '[☠]';
172172
case ValidationType.missing:
173173
return '[✗]';
174-
case ValidationType.installed:
174+
case ValidationType.success:
175175
return '[✓]';
176176
case ValidationType.notAvailable:
177177
case ValidationType.partial:
@@ -186,7 +186,7 @@ class ValidationResult {
186186
return globals.terminal.color(leadingBox, TerminalColor.red);
187187
case ValidationType.missing:
188188
return globals.terminal.color(leadingBox, TerminalColor.red);
189-
case ValidationType.installed:
189+
case ValidationType.success:
190190
return globals.terminal.color(leadingBox, TerminalColor.green);
191191
case ValidationType.notAvailable:
192192
case ValidationType.partial:
@@ -202,7 +202,7 @@ class ValidationResult {
202202
return 'crash';
203203
case ValidationType.missing:
204204
return 'missing';
205-
case ValidationType.installed:
205+
case ValidationType.success:
206206
return 'installed';
207207
case ValidationType.notAvailable:
208208
return 'notAvailable';

packages/flutter_tools/lib/src/http_host_validator.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ class HttpHostValidator extends DoctorValidator {
110110

111111
if (availabilityResults.every((_HostValidationResult result) => result.available)) {
112112
return ValidationResult(
113-
ValidationType.installed,
113+
ValidationType.success,
114114
messages..add(const ValidationMessage('All required HTTP hosts are available')),
115115
);
116116
}

packages/flutter_tools/lib/src/intellij/intellij_validator.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ abstract class IntelliJValidator extends DoctorValidator {
114114
}
115115

116116
return ValidationResult(
117-
_hasIssues(messages) ? ValidationType.partial : ValidationType.installed,
117+
_hasIssues(messages) ? ValidationType.partial : ValidationType.success,
118118
messages,
119119
statusInfo: _userMessages.intellijStatusInfo(version),
120120
);

packages/flutter_tools/lib/src/linux/linux_doctor.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ class LinuxDoctorValidator extends DoctorValidator {
5959

6060
@override
6161
Future<ValidationResult> validate() async {
62-
ValidationType validationType = ValidationType.installed;
62+
ValidationType validationType = ValidationType.success;
6363
final List<ValidationMessage> messages = <ValidationMessage>[];
6464

6565
final Map<String, _VersionInfo?> installedVersions = <String, _VersionInfo?>{

0 commit comments

Comments
 (0)