Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Validate bins on path in doctor #113106

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 38 additions & 3 deletions packages/flutter_tools/lib/src/doctor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -512,7 +512,10 @@ class FlutterValidator extends DoctorValidator {
versionChannel = version.channel;
frameworkVersion = version.frameworkVersion;

messages.add(_getFlutterVersionMessage(frameworkVersion, versionChannel));
final String flutterRoot = _flutterRoot();
messages.add(_getFlutterVersionMessage(frameworkVersion, versionChannel, flutterRoot));

_validateRequiredBinaries(flutterRoot).forEach(messages.add);
messages.add(_getFlutterUpstreamMessage(version));
if (gitUrl != null) {
messages.add(ValidationMessage(_userMessages.flutterGitUrl(gitUrl)));
Expand Down Expand Up @@ -575,8 +578,8 @@ class FlutterValidator extends DoctorValidator {
);
}

ValidationMessage _getFlutterVersionMessage(String frameworkVersion, String versionChannel) {
String flutterVersionMessage = _userMessages.flutterVersion(frameworkVersion, versionChannel, _flutterRoot());
ValidationMessage _getFlutterVersionMessage(String frameworkVersion, String versionChannel, String flutterRoot) {
String flutterVersionMessage = _userMessages.flutterVersion(frameworkVersion, versionChannel, flutterRoot);

// The tool sets the channel as "unknown", if the current branch is on a
// "detached HEAD" state or doesn't have an upstream, and sets the
Expand All @@ -594,6 +597,38 @@ class FlutterValidator extends DoctorValidator {
return ValidationMessage.hint(flutterVersionMessage);
}

List<ValidationMessage> _validateRequiredBinaries(String flutterRoot) {
final ValidationMessage? flutterWarning = _validateSdkBinary('flutter', flutterRoot);
final ValidationMessage? dartWarning = _validateSdkBinary('dart', flutterRoot);
return <ValidationMessage>[
if (flutterWarning != null) flutterWarning,
if (dartWarning != null) dartWarning,
];
}

/// Return a warning if the provided [binary] on the user's path does not
/// resolve within the Flutter SDK.
ValidationMessage? _validateSdkBinary(String binary, String flutterRoot) {
final String flutterBinDir = _fileSystem.path.join(flutterRoot, 'bin');

final File? flutterBin = _operatingSystemUtils.which(binary);
if (flutterBin == null) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should an extra info message be added if flutterBinDir points to a different flutterBin eg: if a user have 2 installation of flutter and runs /extra_install/flutter/bin/flutter doctor and flutter points to /first_install/flutter/bin/flutter?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the check on line 622 should cover that?

return ValidationMessage.hint(
'The $binary binary is not on your path. Consider adding '
'$flutterBinDir to your path.',
);
}
final String resolvedFlutterPath = flutterBin.resolveSymbolicLinksSync();
if (!resolvedFlutterPath.contains(flutterRoot)) {
final String hint = 'Warning: `$binary` on your path resolves to '
'$resolvedFlutterPath, which is not inside your current Flutter '
'SDK checkout at $flutterRoot. Consider adding $flutterBinDir to '
'the front of your path.';
return ValidationMessage.hint(hint);
}
return null;
}

ValidationMessage _getFlutterUpstreamMessage(FlutterVersion version) {
final String? repositoryUrl = version.repositoryUrl;
final VersionCheckError? upstreamValidationError = VersionUpstreamValidator(version: version, platform: _platform).run();
Expand Down
115 changes: 114 additions & 1 deletion packages/flutter_tools/test/general.shard/flutter_validator_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:file/file.dart';
import 'package:file/memory.dart';
import 'package:flutter_tools/src/artifacts.dart';
import 'package:flutter_tools/src/base/os.dart';
Expand Down Expand Up @@ -384,10 +385,122 @@ void main() {
),
));
});

testWithoutContext('detects no flutter and dart on path', () async {
final FlutterValidator flutterValidator = FlutterValidator(
platform: FakePlatform(localeName: 'en_US.UTF-8'),
flutterVersion: () => FakeFlutterVersion(
frameworkVersion: '1.0.0',
channel: 'beta'
),
devToolsVersion: () => '2.8.0',
userMessages: UserMessages(),
artifacts: Artifacts.test(),
fileSystem: MemoryFileSystem.test(),
processManager: FakeProcessManager.any(),
operatingSystemUtils: FakeOperatingSystemUtils(
name: 'Linux',
whichLookup: const <String, File>{},
),
flutterRoot: () => 'sdk/flutter',
);

expect(await flutterValidator.validate(), _matchDoctorValidation(
validationType: ValidationType.partial,
statusInfo: 'Channel beta, 1.0.0, on Linux, locale en_US.UTF-8',
messages: contains(const ValidationMessage.hint(
'The flutter binary is not on your path. Consider adding sdk/flutter/bin to your path.',
)),
));
});

testWithoutContext('detects flutter and dart from outside flutter sdk', () async {
final FileSystem fs = MemoryFileSystem.test();
final FlutterValidator flutterValidator = FlutterValidator(
platform: FakePlatform(localeName: 'en_US.UTF-8'),
flutterVersion: () => FakeFlutterVersion(
frameworkVersion: '1.0.0',
channel: 'beta'
),
devToolsVersion: () => '2.8.0',
userMessages: UserMessages(),
artifacts: Artifacts.test(),
fileSystem: fs,
processManager: FakeProcessManager.any(),
operatingSystemUtils: FakeOperatingSystemUtils(
name: 'Linux',
whichLookup: <String, File>{
'flutter': fs.file('/usr/bin/flutter')..createSync(recursive: true),
'dart': fs.file('/usr/bin/dart')..createSync(recursive: true),
},
),
flutterRoot: () => 'sdk/flutter',
);

expect(await flutterValidator.validate(), _matchDoctorValidation(
validationType: ValidationType.partial,
statusInfo: 'Channel beta, 1.0.0, on Linux, locale en_US.UTF-8',
messages: contains(const ValidationMessage.hint(
'Warning: `flutter` on your path resolves to /usr/bin/flutter, which '
'is not inside your current Flutter SDK checkout at sdk/flutter. '
'Consider adding sdk/flutter/bin to the front of your path.',
)),
));
});

testWithoutContext('no warnings if flutter & dart binaries are inside the Flutter SDK', () async {
final FileSystem fs = MemoryFileSystem.test();
final FlutterValidator flutterValidator = FlutterValidator(
platform: FakePlatform(localeName: 'en_US.UTF-8'),
flutterVersion: () => FakeFlutterVersion(
frameworkVersion: '1.0.0',
channel: 'beta'
),
devToolsVersion: () => '2.8.0',
userMessages: UserMessages(),
artifacts: Artifacts.test(),
fileSystem: fs,
processManager: FakeProcessManager.any(),
operatingSystemUtils: FakeOperatingSystemUtils(
name: 'Linux',
whichLookup: <String, File>{
'flutter': fs.file('/sdk/flutter/bin/flutter')..createSync(recursive: true),
'dart': fs.file('/sdk/flutter/bin/dart')..createSync(recursive: true),
},
),
flutterRoot: () => '/sdk/flutter',
);

expect(await flutterValidator.validate(), _matchDoctorValidation(
validationType: ValidationType.installed,
statusInfo: 'Channel beta, 1.0.0, on Linux, locale en_US.UTF-8',
messages: isNot(contains(isA<ValidationMessage>().having(
(ValidationMessage message) => message.message,
'message',
contains('Consider adding /sdk/flutter/bin to the front of your path'),
))),
));
});
}

class FakeOperatingSystemUtils extends Fake implements OperatingSystemUtils {
FakeOperatingSystemUtils({required this.name});
FakeOperatingSystemUtils({
required this.name,
this.whichLookup,
FileSystem? fs,
}) {
fs ??= MemoryFileSystem.test();
whichLookup ??= <String, File>{
'flutter': fs.file('/sdk/flutter/bin/flutter')..createSync(recursive: true),
'dart': fs.file('/sdk/flutter/bin/dart')..createSync(recursive: true),
};
}

/// A map of [File]s that calls to [which] will return.
Map<String, File>? whichLookup;

@override
File? which(String execName) => whichLookup![execName];

@override
final String name;
Expand Down