diff --git a/packages/shorebird_cli/lib/src/executables/java.dart b/packages/shorebird_cli/lib/src/executables/java.dart index dd00d8c5..10a7c5f5 100644 --- a/packages/shorebird_cli/lib/src/executables/java.dart +++ b/packages/shorebird_cli/lib/src/executables/java.dart @@ -8,6 +8,7 @@ import 'package:shorebird_cli/src/android_studio.dart'; import 'package:shorebird_cli/src/extensions/string.dart'; import 'package:shorebird_cli/src/os/os.dart'; import 'package:shorebird_cli/src/platform.dart'; +import 'package:shorebird_cli/src/shorebird_flutter.dart'; import 'package:shorebird_cli/src/shorebird_process.dart'; /// A reference to a [Java] instance. @@ -37,9 +38,13 @@ class Java { /// Returns a path to the user's JDK. If one is not found, returns `null`. /// - /// This first looks for the Java bundled with Android Studio, then the - /// JAVA_HOME environment variable. + /// This first looks a jdk-dir override, then for the Java bundled with + /// Android Studio, then the JAVA_HOME environment variable. String? get home { + const jdkDir = 'jdk-dir'; + final config = shorebirdFlutter.getConfig(); + if (config.containsKey(jdkDir)) return config[jdkDir] as String; + if (!_androidStudioJavaPath.isNullOrEmpty) { return _androidStudioJavaPath; } diff --git a/packages/shorebird_cli/lib/src/shorebird_flutter.dart b/packages/shorebird_cli/lib/src/shorebird_flutter.dart index 8797101e..80b65b96 100644 --- a/packages/shorebird_cli/lib/src/shorebird_flutter.dart +++ b/packages/shorebird_cli/lib/src/shorebird_flutter.dart @@ -121,6 +121,32 @@ class ShorebirdFlutter { return match?.group(1); } + /// Executes `flutter config --list` and returns the output as a map. + Map getConfig() { + final args = ['config', '--list']; + final result = process.runSync(executable, args); + if (result.exitCode != ExitCode.success.code) { + throw ProcessException( + executable, + args, + '${result.stderr}', + result.exitCode, + ); + } + final output = '${result.stdout}'; + final config = {}; + final lines = LineSplitter.split(output).toList(); + for (final line in lines.skip(1)) { + final parts = line.split(':'); + if (parts.length == 2) { + final key = parts[0].trim(); + final value = parts[1].trim(); + config[key] = value; + } + } + return config; + } + /// Converts a full git revision to a short revision string. String shortRevisionString(String revision) => revision.substring(0, 10); diff --git a/packages/shorebird_cli/test/src/executables/java_test.dart b/packages/shorebird_cli/test/src/executables/java_test.dart index 7c21d21f..d02dcc40 100644 --- a/packages/shorebird_cli/test/src/executables/java_test.dart +++ b/packages/shorebird_cli/test/src/executables/java_test.dart @@ -8,6 +8,7 @@ import 'package:shorebird_cli/src/android_studio.dart'; import 'package:shorebird_cli/src/executables/executables.dart'; import 'package:shorebird_cli/src/os/os.dart'; import 'package:shorebird_cli/src/platform.dart'; +import 'package:shorebird_cli/src/shorebird_flutter.dart'; import 'package:shorebird_cli/src/shorebird_process.dart'; import 'package:test/test.dart'; @@ -18,6 +19,7 @@ void main() { late AndroidStudio androidStudio; late OperatingSystemInterface osInterface; late Platform platform; + late ShorebirdFlutter shorebirdFlutter; late ShorebirdProcess shorebirdProcess; late Java java; @@ -29,6 +31,7 @@ void main() { osInterfaceRef.overrideWith(() => osInterface), processRef.overrideWith(() => shorebirdProcess), platformRef.overrideWith(() => platform), + shorebirdFlutterRef.overrideWith(() => shorebirdFlutter), }, ); } @@ -43,6 +46,7 @@ void main() { androidStudio = MockAndroidStudio(); osInterface = MockOperatingSystemInterface(); platform = MockPlatform(); + shorebirdFlutter = MockShorebirdFlutter(); shorebirdProcess = MockShorebirdProcess(); java = Java(); @@ -52,6 +56,8 @@ void main() { when(() => platform.isLinux).thenReturn(false); when(() => osInterface.which(any())).thenReturn(null); + + when(shorebirdFlutter.getConfig).thenReturn({}); }); group('version', () { @@ -146,7 +152,7 @@ void main() { group('when Android Studio is installed', () { late Directory jbrDir; - group('when on macOS', () { + group('on macOS', () { setUp(() { when(() => platform.isMacOS).thenReturn(true); @@ -169,6 +175,19 @@ void main() { when(() => platform.environment).thenReturn({'HOME': tempDir.path}); }); + group('when flutter config contains jdk override', () { + const jdkDirOverride = '/jdk'; + setUp(() { + when( + shorebirdFlutter.getConfig, + ).thenReturn({'jdk-dir': jdkDirOverride}); + }); + + test('returns value of jdk-dir', () { + expect(runWithOverrides(() => java.home), equals(jdkDirOverride)); + }); + }); + test('returns correct path', () async { await expectLater( runWithOverrides(() => java.home), @@ -184,7 +203,7 @@ void main() { }); }); - group('when on Windows', () { + group('on Windows', () { late Directory jbrDir; setUp(() { @@ -221,7 +240,7 @@ void main() { }); }); - group('when on Linux', () { + group('on Linux', () { setUp(() { when(() => platform.isLinux).thenReturn(true); @@ -257,6 +276,19 @@ void main() { ).thenReturn({'JAVA_HOME': javaHome}); }); + group('when flutter config contains jdk override', () { + const jdkDirOverride = '/jdk'; + setUp(() { + when( + shorebirdFlutter.getConfig, + ).thenReturn({'jdk-dir': jdkDirOverride}); + }); + + test('returns value of jdk-dir', () { + expect(runWithOverrides(() => java.home), equals(jdkDirOverride)); + }); + }); + test('returns value of JAVA_HOME', () { expect(runWithOverrides(() => java.home), equals(javaHome)); }); diff --git a/packages/shorebird_cli/test/src/shorebird_flutter_test.dart b/packages/shorebird_cli/test/src/shorebird_flutter_test.dart index f491de73..cd00c6d9 100644 --- a/packages/shorebird_cli/test/src/shorebird_flutter_test.dart +++ b/packages/shorebird_cli/test/src/shorebird_flutter_test.dart @@ -137,6 +137,83 @@ void main() { }); }); + group('getConfig', () { + late ShorebirdProcessResult configProcessResult; + + setUp(() { + configProcessResult = MockProcessResult(); + when( + () => process.runSync(any(), any()), + ).thenReturn(configProcessResult); + }); + + group('when process exists with non-zero code', () { + setUp(() { + when( + () => configProcessResult.exitCode, + ).thenReturn(ExitCode.software.code); + when(() => configProcessResult.stderr).thenReturn('oops'); + }); + + test('throws ProcessException', () { + expect( + () => runWithOverrides(shorebirdFlutter.getConfig), + throwsA(isA()), + ); + verify( + () => process.runSync('flutter', ['config', '--list']), + ).called(1); + }); + }); + + group('when process completes successfully', () { + setUp(() { + when(() => configProcessResult.stdout).thenReturn(''' +All Settings: + enable-web: (Not set) + enable-linux-desktop: (Not set) + enable-macos-desktop: (Not set) + enable-windows-desktop: (Not set) + enable-android: (Not set) + enable-ios: (Not set) + enable-fuchsia: (Not set) (Unavailable) + enable-custom-devices: (Not set) + cli-animations: (Not set) + enable-native-assets: (Not set) (Unavailable) + enable-flutter-preview: (Not set) (Unavailable) + enable-swift-package-manager: (Not set) + explicit-package-dependencies: (Not set) + jdk-dir: ./jdk/dir/override + '''); + when( + () => configProcessResult.exitCode, + ).thenReturn(ExitCode.success.code); + }); + + test('returns correct config map', () { + expect( + runWithOverrides(shorebirdFlutter.getConfig), + equals({ + 'enable-web': '(Not set)', + 'enable-linux-desktop': '(Not set)', + 'enable-macos-desktop': '(Not set)', + 'enable-windows-desktop': '(Not set)', + 'enable-android': '(Not set)', + 'enable-ios': '(Not set)', + 'enable-fuchsia': '(Not set) (Unavailable)', + 'enable-custom-devices': '(Not set)', + 'cli-animations': '(Not set)', + 'enable-native-assets': '(Not set) (Unavailable)', + 'enable-flutter-preview': '(Not set) (Unavailable)', + 'enable-swift-package-manager': '(Not set)', + 'explicit-package-dependencies': '(Not set)', + 'jdk-dir': './jdk/dir/override', + }), + ); + }); + }); + }); + group('getSystemVersion', () { test( 'throws ProcessException when process exits with non-zero code',