Skip to content
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

feat: adding gradlew version to the doctor output #2255

Merged
merged 10 commits into from
Jun 18, 2024
9 changes: 8 additions & 1 deletion packages/shorebird_cli/lib/src/commands/doctor_command.dart
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,12 @@ Engine • revision ${shorebirdEnv.shorebirdEngineRevision}''',
.join('${Platform.lineTerminator} ');
}
}

String? gradlewVersion;
if (gradlew.exists(Directory.current.path)) {
gradlewVersion = await gradlew.version(Directory.current.path);
}

output.writeln('''

Logs: ${shorebirdEnv.logsDirectory.path}
Expand All @@ -82,7 +88,8 @@ Android Toolchain
• ADB: ${androidSdk.adbPath ?? notDetected}
• JAVA_HOME: ${java.home ?? notDetected}
• JAVA_EXECUTABLE: ${javaExe ?? notDetected}
• JAVA_VERSION: $javaVersion''');
• JAVA_VERSION: $javaVersion
• Gradle: ${gradlewVersion ?? notDetected}''');
}

logger.info(output.toString());
Expand Down
33 changes: 28 additions & 5 deletions packages/shorebird_cli/lib/src/executables/gradlew.dart
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,7 @@ Gradlew get gradlew => read(gradlewRef);
class Gradlew {
String get executable => platform.isWindows ? 'gradlew.bat' : 'gradlew';

/// Return the set of product flavors configured for the app at [projectPath].
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice refactor!

/// Returns an empty set for apps that do not use product flavors.
Future<Set<String>> productFlavors(String projectPath) async {
Future<ShorebirdProcessResult> _run(List<String> args, String projectPath) {
final javaHome = java.home;
final androidRoot = Directory(p.join(projectPath, 'android'));

Expand All @@ -75,15 +73,40 @@ class Gradlew {
}

final executablePath = executableFile.path;
final result = await process.run(
return process.run(
executablePath,
['app:tasks', '--all', '--console=auto'],
args,
runInShell: true,
workingDirectory: p.dirname(executablePath),
environment: {
if (!javaHome.isNullOrEmpty) 'JAVA_HOME': javaHome!,
},
);
}

/// Returns whether the gradle wrapper exists at [projectPath].
bool exists(String projectPath) {
final androidRoot = Directory(p.join(projectPath, 'android'));
return File(p.join(androidRoot.path, executable)).existsSync();
}
erickzanardo marked this conversation as resolved.
Show resolved Hide resolved

/// Return the version of the gradle wrapper at [projectPath].
erickzanardo marked this conversation as resolved.
Show resolved Hide resolved
Future<String> version(String projectPath) async {
final result = await _run(['--version'], projectPath);

final versionPattern = RegExp(r'Gradle (\d+\.\d+\.\d+)');
erickzanardo marked this conversation as resolved.
Show resolved Hide resolved
final match = versionPattern.firstMatch(result.stdout.toString());

return match?.group(1) ?? 'unknown';
}

/// Return the set of product flavors configured for the app at [projectPath].
/// Returns an empty set for apps that do not use product flavors.
Future<Set<String>> productFlavors(String projectPath) async {
final result = await _run(
['app:tasks', '--all', '--console=auto'],
projectPath,
);

if (result.exitCode != 0) {
throw Exception('${result.stdout}\n${result.stderr}');
Expand Down
61 changes: 61 additions & 0 deletions packages/shorebird_cli/test/src/commands/doctor_command_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ void main() {
late AndroidSdk androidSdk;
late Directory logsDirectory;
late Doctor doctor;
late Gradlew gradlew;
late Java java;
late ShorebirdLogger logger;
late ShorebirdEnv shorebirdEnv;
Expand All @@ -42,6 +43,7 @@ void main() {
androidStudioRef.overrideWith(() => androidStudio),
androidSdkRef.overrideWith(() => androidSdk),
doctorRef.overrideWith(() => doctor),
gradlewRef.overrideWith(() => gradlew),
javaRef.overrideWith(() => java),
loggerRef.overrideWith(() => logger),
shorebirdEnvRef.overrideWith(() => shorebirdEnv),
Expand All @@ -55,6 +57,7 @@ void main() {
androidStudio = MockAndroidStudio();
androidSdk = MockAndroidSdk();
doctor = MockDoctor();
gradlew = MockGradlew();
logsDirectory = Directory.systemTemp.createTempSync('shorebird_logs');
java = MockJava();
logger = MockShorebirdLogger();
Expand All @@ -67,6 +70,7 @@ void main() {
when(() => androidStudio.path).thenReturn(null);
when(() => androidSdk.path).thenReturn(null);
when(() => androidSdk.adbPath).thenReturn(null);
when(() => gradlew.exists(any())).thenReturn(false);
when(() => java.home).thenReturn(null);
when(
() => shorebirdEnv.shorebirdEngineRevision,
Expand Down Expand Up @@ -142,6 +146,7 @@ Android Toolchain
• JAVA_HOME: $notDetectedText
• JAVA_EXECUTABLE: $notDetectedText
• JAVA_VERSION: $notDetectedText
• Gradle: $notDetectedText
'''),
);
});
Expand All @@ -166,6 +171,7 @@ OpenJDK 64-Bit Server VM (build 17.0.9+0-17.0.9b1087.7-11185874, mixed mode)'''
final msg =
verify(() => logger.info(captureAny())).captured.first as String;

final notDetectedText = red.wrap('not detected');
expect(
msg.replaceAll(
Platform.lineTerminator,
Expand All @@ -187,10 +193,65 @@ Android Toolchain
• JAVA_VERSION: openjdk version "17.0.9" 2023-10-17
OpenJDK Runtime Environment (build 17.0.9+0-17.0.9b1087.7-11185874)
OpenJDK 64-Bit Server VM (build 17.0.9+0-17.0.9b1087.7-11185874, mixed mode)
• Gradle: $notDetectedText
''',
),
);
});

group('when a gradlew executable exists', () {
setUp(() {
when(() => gradlew.exists(any())).thenReturn(true);
when(() => gradlew.version(any())).thenAnswer((_) async => '7.6.3');
});

test('prints the gradle version', () async {
when(() => argResults['verbose']).thenReturn(true);
when(() => androidStudio.path).thenReturn('test-studio-path');
when(() => androidSdk.path).thenReturn('test-sdk-path');
when(() => androidSdk.adbPath).thenReturn('test-adb-path');
when(() => java.home).thenReturn('test-java-home');
when(() => java.executable).thenReturn('test-java-executable');

when(() => java.version).thenReturn(
'''
openjdk version "17.0.9" 2023-10-17
OpenJDK Runtime Environment (build 17.0.9+0-17.0.9b1087.7-11185874)
OpenJDK 64-Bit Server VM (build 17.0.9+0-17.0.9b1087.7-11185874, mixed mode)'''
.replaceAll('\n', Platform.lineTerminator),
);
erickzanardo marked this conversation as resolved.
Show resolved Hide resolved
await runWithOverrides(command.run);

final msg =
verify(() => logger.info(captureAny())).captured.first as String;

expect(
msg.replaceAll(
Platform.lineTerminator,
'\n',
),
equals(
'''
Shorebird $packageVersion • git@github.com:shorebirdtech/shorebird.git
Flutter • revision ${shorebirdEnv.flutterRevision}
Engine • revision $shorebirdEngineRevision

Logs: ${logsDirectory.path}
Android Toolchain
• Android Studio: test-studio-path
• Android SDK: test-sdk-path
• ADB: test-adb-path
• JAVA_HOME: test-java-home
• JAVA_EXECUTABLE: test-java-executable
• JAVA_VERSION: openjdk version "17.0.9" 2023-10-17
OpenJDK Runtime Environment (build 17.0.9+0-17.0.9b1087.7-11185874)
OpenJDK 64-Bit Server VM (build 17.0.9+0-17.0.9b1087.7-11185874, mixed mode)
• Gradle: 7.6.3
''',
),
);
});
});
});

test('runs validators without applying fixes if no fix flag exists',
Expand Down
108 changes: 102 additions & 6 deletions packages/shorebird_cli/test/src/executables/gradlew_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,13 @@ Make sure you have run "flutter build apk" at least once.''',
});
});

group('productFlavors', () {
Directory setUpAppTempDir() {
final tempDir = Directory.systemTemp.createTempSync();
Directory(p.join(tempDir.path, 'android')).createSync(recursive: true);
return tempDir;
}
Directory setUpAppTempDir() {
final tempDir = Directory.systemTemp.createTempSync();
Directory(p.join(tempDir.path, 'android')).createSync(recursive: true);
return tempDir;
}

group('productFlavors', () {
test(
'throws MissingAndroidProjectException '
'when android root does not exist', () async {
Expand Down Expand Up @@ -329,5 +329,101 @@ Make sure you have run "flutter build apk" at least once.''',
},
);
});

group('exists', () {
late Directory tempDir;
setUp(() {
tempDir = setUpAppTempDir();
});

group('when gradlew does not exist', () {
test('returns false', () {
expect(gradlew.exists(tempDir.path), isFalse);
});
});

group('when gradlew exists', () {
group(
'when on mac and linux',
() {
test('returns true', () {
File(
p.join(tempDir.path, 'android', 'gradlew'),
).createSync(recursive: true);
erickzanardo marked this conversation as resolved.
Show resolved Hide resolved
expect(gradlew.exists(tempDir.path), isTrue);
});
},
onPlatform: {'windows': const Skip()},
);

group(
'when on windows',
() {
test('returns true', () {
File(
p.join(tempDir.path, 'android', 'gradlew.bat'),
).createSync(recursive: true);
erickzanardo marked this conversation as resolved.
Show resolved Hide resolved
expect(gradlew.exists(tempDir.path), isTrue);
});
},
onPlatform: {
'mac-os': const Skip(),
'linux': const Skip(),
},
);
});
});

group('version', () {
late Directory tempDir;

setUp(() {
when(() => platform.isLinux).thenReturn(true);
when(() => platform.isMacOS).thenReturn(false);
when(() => platform.isWindows).thenReturn(false);
erickzanardo marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here re: testOn

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This one I am not sure if it is worth to run in all platforms, since all is being tested is the parsing of the output, which shouldn't change (at least the relevant part for us), no matter which platform we are.

And if I change to testOn, then I will need to write one test for each platform (or at least of for unix based and one for windows), because we need to mock the executable (which you can see a couple of lines above) path differently.

Lmk if this makes sense

Copy link
Contributor

Choose a reason for hiding this comment

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

You don't need to write one for each platform. I'm just suggesting that we use a consistent method of targeting a platform for a test.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

right, changed to use testOn on all tests.

tempDir = setUpAppTempDir();
File(
p.join(tempDir.path, 'android', 'gradlew'),
).createSync(recursive: true);
const javaHome = 'test_java_home';
when(() => platform.environment).thenReturn({'JAVA_HOME': javaHome});

when(() => result.stdout).thenReturn('''

------------------------------------------------------------
Gradle 7.6.3
------------------------------------------------------------

Build time: 2023-10-04 15:59:47 UTC
Revision: 1694251d59e0d4752d547e1fd5b5020b798a7e71

Kotlin: 1.7.10
Groovy: 3.0.13
Ant: Apache Ant(TM) version 1.10.11 compiled on July 10 2021
JVM: 11.0.23 (Azul Systems, Inc. 11.0.23+9-LTS)
OS: Mac OS X 14.4.1 aarch64
''');
});

test('returns the correct version', () async {
final version = await runWithOverrides(
() => gradlew.version(tempDir.path),
);
expect(version, '7.6.3');
});

group('when the output cannot be parsed', () {
setUp(() {
when(() => result.stdout).thenReturn('not a real version');
});

test('returns unknown', () async {
final version = await runWithOverrides(
() => gradlew.version(tempDir.path),
);
expect(version, 'unknown');
});
});
});
});
}
Loading