Skip to content

Commit 47b4060

Browse files
authored
[flutter_tools] Have FlutterValidator fail on non-ideal git config (#103259)
1 parent 672859a commit 47b4060

File tree

5 files changed

+153
-30
lines changed

5 files changed

+153
-30
lines changed

packages/flutter_tools/lib/src/base/user_messages.dart

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,13 @@ class UserMessages {
1616
// Messages used in FlutterValidator
1717
String flutterStatusInfo(String? channel, String? version, String os, String locale) =>
1818
'Channel ${channel ?? 'unknown'}, ${version ?? 'Unknown'}, on $os, locale $locale';
19-
String flutterVersion(String version, String flutterRoot) =>
20-
'Flutter version $version at $flutterRoot';
19+
String flutterVersion(String version, String channel, String flutterRoot) =>
20+
'Flutter version $version on channel $channel at $flutterRoot';
2121
String flutterRevision(String revision, String age, String date) =>
2222
'Framework revision $revision ($age), $date';
2323
String flutterUpstreamRepositoryUrl(String url) => 'Upstream repository $url';
24+
String flutterUpstreamRepositoryUrlEnvMismatch(String url) => 'Upstream repository $url is not the same as FLUTTER_GIT_URL';
25+
String flutterUpstreamRepositoryUrlNonStandard(String url) => 'Upstream repository $url is not a standard remote';
2426
String flutterGitUrl(String url) => 'FLUTTER_GIT_URL = $url';
2527
String engineRevision(String revision) => 'Engine revision $revision';
2628
String dartRevision(String revision) => 'Dart version $revision';

packages/flutter_tools/lib/src/doctor.dart

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,7 @@ class Doctor {
381381
}
382382

383383
for (final ValidationMessage message in result.messages) {
384-
if (message.type != ValidationMessageType.information || verbose == true) {
384+
if (!message.isInformation || verbose == true) {
385385
int hangingIndent = 2;
386386
int indent = 4;
387387
final String indicator = showColor ? message.coloredIndicator : message.indicator;
@@ -467,20 +467,17 @@ class FlutterValidator extends DoctorValidator {
467467
@override
468468
Future<ValidationResult> validate() async {
469469
final List<ValidationMessage> messages = <ValidationMessage>[];
470-
ValidationType valid = ValidationType.installed;
471470
String? versionChannel;
472471
String? frameworkVersion;
473472

474473
try {
475474
final FlutterVersion version = _flutterVersion();
475+
final String? gitUrl = _platform.environment['FLUTTER_GIT_URL'];
476476
versionChannel = version.channel;
477477
frameworkVersion = version.frameworkVersion;
478-
messages.add(ValidationMessage(_userMessages.flutterVersion(
479-
frameworkVersion,
480-
_flutterRoot(),
481-
)));
482-
messages.add(ValidationMessage(_userMessages.flutterUpstreamRepositoryUrl(version.repositoryUrl ?? 'unknown')));
483-
final String? gitUrl = _platform.environment['FLUTTER_GIT_URL'];
478+
479+
messages.add(_getFlutterVersionMessage(frameworkVersion, versionChannel));
480+
messages.add(_getFlutterUpstreamMessage(version));
484481
if (gitUrl != null) {
485482
messages.add(ValidationMessage(_userMessages.flutterGitUrl(gitUrl)));
486483
}
@@ -502,7 +499,6 @@ class FlutterValidator extends DoctorValidator {
502499
}
503500
} on VersionCheckError catch (e) {
504501
messages.add(ValidationMessage.error(e.message));
505-
valid = ValidationType.partial;
506502
}
507503

508504
// Check that the binaries we downloaded for this platform actually run on it.
@@ -516,9 +512,12 @@ class FlutterValidator extends DoctorValidator {
516512
buffer.writeln(_userMessages.flutterBinariesLinuxRepairCommands);
517513
}
518514
messages.add(ValidationMessage.error(buffer.toString()));
519-
valid = ValidationType.partial;
520515
}
521516

517+
final ValidationType valid = messages.every((ValidationMessage message) => message.isInformation)
518+
? ValidationType.installed
519+
: ValidationType.partial;
520+
522521
return ValidationResult(
523522
valid,
524523
messages,
@@ -531,6 +530,40 @@ class FlutterValidator extends DoctorValidator {
531530
);
532531
}
533532

533+
ValidationMessage _getFlutterVersionMessage(String frameworkVersion, String versionChannel) {
534+
final String flutterVersionMessage = _userMessages.flutterVersion(frameworkVersion, versionChannel, _flutterRoot());
535+
536+
// The tool sets the channel as "unknown", if the current branch is on a
537+
// "detached HEAD" state or doesn't have an upstream, and sets the
538+
// frameworkVersion as "0.0.0-unknown" if "git describe" on HEAD doesn't
539+
// produce an expected format to be parsed for the frameworkVersion.
540+
if (versionChannel == 'unknown' || frameworkVersion == '0.0.0-unknown') {
541+
return ValidationMessage.hint(flutterVersionMessage);
542+
}
543+
return ValidationMessage(flutterVersionMessage);
544+
}
545+
546+
ValidationMessage _getFlutterUpstreamMessage(FlutterVersion version) {
547+
final String? repositoryUrl = version.repositoryUrl;
548+
final VersionCheckError? upstreamValidationError = VersionUpstreamValidator(version: version, platform: _platform).run();
549+
550+
// VersionUpstreamValidator can produce an error if repositoryUrl is null
551+
if (upstreamValidationError != null) {
552+
final String errorMessage = upstreamValidationError.message;
553+
if (errorMessage.contains('could not determine the remote upstream which is being tracked')) {
554+
return ValidationMessage.hint(_userMessages.flutterUpstreamRepositoryUrl('unknown'));
555+
}
556+
// At this point, repositoryUrl must not be null
557+
if (errorMessage.contains('Flutter SDK is tracking a non-standard remote')) {
558+
return ValidationMessage.hint(_userMessages.flutterUpstreamRepositoryUrlNonStandard(repositoryUrl!));
559+
}
560+
if (errorMessage.contains('Either remove "FLUTTER_GIT_URL" from the environment or set it to')){
561+
return ValidationMessage.hint(_userMessages.flutterUpstreamRepositoryUrlEnvMismatch(repositoryUrl!));
562+
}
563+
}
564+
return ValidationMessage(_userMessages.flutterUpstreamRepositoryUrl(repositoryUrl!));
565+
}
566+
534567
bool _genSnapshotRuns(String genSnapshotPath) {
535568
const int kExpectedExitCode = 255;
536569
try {

packages/flutter_tools/lib/src/doctor_validator.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,8 @@ class ValidationMessage {
252252

253253
bool get isHint => type == ValidationMessageType.hint;
254254

255+
bool get isInformation => type == ValidationMessageType.information;
256+
255257
String get indicator {
256258
switch (type) {
257259
case ValidationMessageType.error:

packages/flutter_tools/test/general.shard/flutter_validator_test.dart

Lines changed: 102 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ void main() {
3333
'downloaded and exits with code 1', () async {
3434
final FakeFlutterVersion flutterVersion = FakeFlutterVersion(
3535
frameworkVersion: '1.0.0',
36+
channel: 'beta',
3637
);
3738
final MemoryFileSystem fileSystem = MemoryFileSystem.test();
3839
final Artifacts artifacts = Artifacts.test();
@@ -60,7 +61,7 @@ void main() {
6061

6162
expect(await flutterValidator.validate(), _matchDoctorValidation(
6263
validationType: ValidationType.partial,
63-
statusInfo: 'Channel unknown, 1.0.0, on Linux, locale en_US.UTF-8',
64+
statusInfo: 'Channel beta, 1.0.0, on Linux, locale en_US.UTF-8',
6465
messages: containsAll(const <ValidationMessage>[
6566
ValidationMessage.error(
6667
'Downloaded executables cannot execute on host.\n'
@@ -76,6 +77,7 @@ void main() {
7677
testWithoutContext('FlutterValidator does not run gen_snapshot binary check if it is not already downloaded', () async {
7778
final FakeFlutterVersion flutterVersion = FakeFlutterVersion(
7879
frameworkVersion: '1.0.0',
80+
channel: 'beta',
7981
);
8082
final FlutterValidator flutterValidator = FlutterValidator(
8183
platform: FakePlatform(
@@ -97,7 +99,7 @@ void main() {
9799
// fail if the gen_snapshot binary is not present.
98100
expect(await flutterValidator.validate(), _matchDoctorValidation(
99101
validationType: ValidationType.installed,
100-
statusInfo: 'Channel unknown, 1.0.0, on Windows, locale en_US.UTF-8',
102+
statusInfo: 'Channel beta, 1.0.0, on Windows, locale en_US.UTF-8',
101103
messages: anything,
102104
));
103105
});
@@ -117,9 +119,9 @@ void main() {
117119

118120
expect(await flutterValidator.validate(), _matchDoctorValidation(
119121
validationType: ValidationType.partial,
120-
statusInfo: 'Channel unknown, 0.0.0, on Windows, locale en_US.UTF-8',
122+
statusInfo: 'Channel beta, 0.0.0, on Windows, locale en_US.UTF-8',
121123
messages: containsAll(const <ValidationMessage>[
122-
ValidationMessage('Flutter version 0.0.0 at sdk/flutter'),
124+
ValidationMessage('Flutter version 0.0.0 on channel beta at sdk/flutter'),
123125
ValidationMessage.error('version error'),
124126
]),
125127
));
@@ -128,6 +130,7 @@ void main() {
128130
testWithoutContext('FlutterValidator shows mirrors on pub and flutter cloud storage', () async {
129131
final FakeFlutterVersion flutterVersion = FakeFlutterVersion(
130132
frameworkVersion: '1.0.0',
133+
channel: 'beta',
131134
);
132135
final Platform platform = FakePlatform(
133136
operatingSystem: 'windows',
@@ -153,23 +156,26 @@ void main() {
153156

154157
expect(await flutterValidator.validate(), _matchDoctorValidation(
155158
validationType: ValidationType.installed,
156-
statusInfo: 'Channel unknown, 1.0.0, on Windows, locale en_US.UTF-8',
159+
statusInfo: 'Channel beta, 1.0.0, on Windows, locale en_US.UTF-8',
157160
messages: containsAll(const <ValidationMessage>[
158161
ValidationMessage('Pub download mirror https://example.com/pub'),
159162
ValidationMessage('Flutter download mirror https://example.com/flutter'),
160163
])
161164
));
162165
});
163166

164-
testWithoutContext('FlutterValidator shows FLUTTER_GIT_URL environment variable when set', () async {
167+
testWithoutContext('FlutterValidator shows FLUTTER_GIT_URL when set and fails if upstream is not the same', () async {
165168
final FlutterValidator flutterValidator = FlutterValidator(
166169
platform: FakePlatform(
167170
localeName: 'en_US.UTF-8',
168171
environment: <String, String> {
169172
'FLUTTER_GIT_URL': 'https://githubmirror.com/flutter.git',
170173
},
171174
),
172-
flutterVersion: () => FakeFlutterVersion(frameworkVersion: '1.0.0'),
175+
flutterVersion: () => FakeFlutterVersion(
176+
frameworkVersion: '1.0.0',
177+
channel: 'beta'
178+
),
173179
devToolsVersion: () => '2.8.0',
174180
userMessages: UserMessages(),
175181
artifacts: Artifacts.test(),
@@ -180,17 +186,69 @@ void main() {
180186
);
181187

182188
expect(await flutterValidator.validate(), _matchDoctorValidation(
183-
validationType: ValidationType.installed,
189+
validationType: ValidationType.partial,
190+
statusInfo: 'Channel beta, 1.0.0, on Linux, locale en_US.UTF-8',
191+
messages: containsAll(const <ValidationMessage>[
192+
ValidationMessage.hint('Upstream repository https://github.com/flutter/flutter.git is not the same as FLUTTER_GIT_URL'),
193+
ValidationMessage('FLUTTER_GIT_URL = https://githubmirror.com/flutter.git'),
194+
]),
195+
));
196+
});
197+
198+
testWithoutContext('FlutterValidator fails when channel is unknown', () async {
199+
final FlutterValidator flutterValidator = FlutterValidator(
200+
platform: FakePlatform(localeName: 'en_US.UTF-8'),
201+
flutterVersion: () => FakeFlutterVersion(
202+
frameworkVersion: '1.0.0',
203+
// channel is unknown by default
204+
),
205+
devToolsVersion: () => '2.8.0',
206+
userMessages: UserMessages(),
207+
artifacts: Artifacts.test(),
208+
fileSystem: MemoryFileSystem.test(),
209+
processManager: FakeProcessManager.any(),
210+
operatingSystemUtils: FakeOperatingSystemUtils(name: 'Linux'),
211+
flutterRoot: () => 'sdk/flutter',
212+
);
213+
214+
expect(await flutterValidator.validate(), _matchDoctorValidation(
215+
validationType: ValidationType.partial,
184216
statusInfo: 'Channel unknown, 1.0.0, on Linux, locale en_US.UTF-8',
185-
messages: contains(const ValidationMessage('FLUTTER_GIT_URL = https://githubmirror.com/flutter.git')),
217+
messages: contains(const ValidationMessage.hint('Flutter version 1.0.0 on channel unknown at sdk/flutter')),
218+
));
219+
});
220+
221+
testWithoutContext('FlutterValidator fails when framework version is unknown', () async {
222+
final FlutterValidator flutterValidator = FlutterValidator(
223+
platform: FakePlatform(localeName: 'en_US.UTF-8'),
224+
flutterVersion: () => FakeFlutterVersion(
225+
frameworkVersion: '0.0.0-unknown',
226+
channel: 'beta',
227+
),
228+
devToolsVersion: () => '2.8.0',
229+
userMessages: UserMessages(),
230+
artifacts: Artifacts.test(),
231+
fileSystem: MemoryFileSystem.test(),
232+
processManager: FakeProcessManager.any(),
233+
operatingSystemUtils: FakeOperatingSystemUtils(name: 'Linux'),
234+
flutterRoot: () => 'sdk/flutter',
235+
);
236+
237+
expect(await flutterValidator.validate(), _matchDoctorValidation(
238+
validationType: ValidationType.partial,
239+
statusInfo: 'Channel beta, 0.0.0-unknown, on Linux, locale en_US.UTF-8',
240+
messages: contains(const ValidationMessage.hint('Flutter version 0.0.0-unknown on channel beta at sdk/flutter')),
186241
));
187242
});
188243

189244
group('FlutterValidator shows flutter upstream remote', () {
190-
testWithoutContext('default url', () async {
245+
testWithoutContext('standard url', () async {
191246
final FlutterValidator flutterValidator = FlutterValidator(
192247
platform: FakePlatform(localeName: 'en_US.UTF-8'),
193-
flutterVersion: () => FakeFlutterVersion(frameworkVersion: '1.0.0'),
248+
flutterVersion: () => FakeFlutterVersion(
249+
frameworkVersion: '1.0.0',
250+
channel: 'beta'
251+
),
194252
devToolsVersion: () => '2.8.0',
195253
userMessages: UserMessages(),
196254
artifacts: Artifacts.test(),
@@ -202,16 +260,41 @@ void main() {
202260

203261
expect(await flutterValidator.validate(), _matchDoctorValidation(
204262
validationType: ValidationType.installed,
205-
statusInfo: 'Channel unknown, 1.0.0, on Linux, locale en_US.UTF-8',
263+
statusInfo: 'Channel beta, 1.0.0, on Linux, locale en_US.UTF-8',
206264
messages: contains(const ValidationMessage('Upstream repository https://github.com/flutter/flutter.git')),
207265
));
208266
});
209267

210-
testWithoutContext('unknown url if upstream is null', () async {
268+
testWithoutContext('non-standard url', () async {
211269
final FlutterValidator flutterValidator = FlutterValidator(
212270
platform: FakePlatform(localeName: 'en_US.UTF-8'),
213271
flutterVersion: () => FakeFlutterVersion(
214272
frameworkVersion: '1.0.0',
273+
channel: 'beta',
274+
repositoryUrl: 'https://githubmirror.com/flutter.git'
275+
),
276+
devToolsVersion: () => '2.8.0',
277+
userMessages: UserMessages(),
278+
artifacts: Artifacts.test(),
279+
fileSystem: MemoryFileSystem.test(),
280+
processManager: FakeProcessManager.any(),
281+
operatingSystemUtils: FakeOperatingSystemUtils(name: 'Linux'),
282+
flutterRoot: () => 'sdk/flutter',
283+
);
284+
285+
expect(await flutterValidator.validate(), _matchDoctorValidation(
286+
validationType: ValidationType.partial,
287+
statusInfo: 'Channel beta, 1.0.0, on Linux, locale en_US.UTF-8',
288+
messages: contains(const ValidationMessage.hint('Upstream repository https://githubmirror.com/flutter.git is not a standard remote')),
289+
));
290+
});
291+
292+
testWithoutContext('as unknown if upstream is null', () async {
293+
final FlutterValidator flutterValidator = FlutterValidator(
294+
platform: FakePlatform(localeName: 'en_US.UTF-8'),
295+
flutterVersion: () => FakeFlutterVersion(
296+
frameworkVersion: '1.0.0',
297+
channel: 'beta',
215298
repositoryUrl: null,
216299
),
217300
devToolsVersion: () => '2.8.0',
@@ -224,9 +307,9 @@ void main() {
224307
);
225308

226309
expect(await flutterValidator.validate(), _matchDoctorValidation(
227-
validationType: ValidationType.installed,
228-
statusInfo: 'Channel unknown, 1.0.0, on Linux, locale en_US.UTF-8',
229-
messages: contains(const ValidationMessage('Upstream repository unknown')),
310+
validationType: ValidationType.partial,
311+
statusInfo: 'Channel beta, 1.0.0, on Linux, locale en_US.UTF-8',
312+
messages: contains(const ValidationMessage.hint('Upstream repository unknown')),
230313
));
231314
});
232315
});
@@ -240,6 +323,9 @@ class FakeOperatingSystemUtils extends Fake implements OperatingSystemUtils {
240323
}
241324

242325
class FakeThrowingFlutterVersion extends FakeFlutterVersion {
326+
@override
327+
String get channel => 'beta';
328+
243329
@override
244330
String get frameworkCommitDate {
245331
throw VersionCheckError('version error');

packages/flutter_tools/test/general.shard/runner/runner_test.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,13 +191,13 @@ void main() {
191191
expect(logContents, contains('flutter crash'));
192192
expect(logContents, contains('Exception: an exception % --'));
193193
expect(logContents, contains('CrashingFlutterCommand.runCommand'));
194-
expect(logContents, contains('[] Flutter'));
194+
expect(logContents, contains('[!] Flutter'));
195195

196196
final CrashDetails sentDetails = (globals.crashReporter as WaitingCrashReporter)._details;
197197
expect(sentDetails.command, 'flutter crash');
198198
expect(sentDetails.error.toString(), 'Exception: an exception % --');
199199
expect(sentDetails.stackTrace.toString(), contains('CrashingFlutterCommand.runCommand'));
200-
expect(await sentDetails.doctorText.text, contains('[] Flutter'));
200+
expect(await sentDetails.doctorText.text, contains('[!] Flutter'));
201201
}, overrides: <Type, Generator>{
202202
Platform: () => FakePlatform(
203203
environment: <String, String>{

0 commit comments

Comments
 (0)