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
42 changes: 32 additions & 10 deletions packages/shorebird_cli/lib/src/executables/gradlew.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ import 'package:shorebird_cli/src/shorebird_process.dart';
/// {@endtemplate}
class MissingAndroidProjectException implements Exception {
/// {@macro missing_android_project_exception}
const MissingAndroidProjectException(this.projectPath);
const MissingAndroidProjectException(this.projectRoot);

/// Expected path for the Android project.
final String projectPath;
final String projectRoot;

@override
String toString() {
return '''
Could not find an android project in $projectPath.
Could not find an android project in $projectRoot.
To add android, run "flutter create . --platforms android"''';
}
}
Expand Down Expand Up @@ -58,14 +58,12 @@ 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 projectRoot) {
final javaHome = java.home;
final androidRoot = Directory(p.join(projectPath, 'android'));
final androidRoot = Directory(p.join(projectRoot, 'android'));

if (!androidRoot.existsSync()) {
throw MissingAndroidProjectException(projectPath);
throw MissingAndroidProjectException(projectRoot);
}

final executableFile = File(p.join(androidRoot.path, executable));
Expand All @@ -75,15 +73,39 @@ 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 [projectRoot].
bool exists(String projectRoot) =>
File(p.join(projectRoot, 'android', executable)).existsSync();

/// Return the version of the gradle wrapper at [projectRoot].
Future<String> version(String projectRoot) async {
final result = await _run(['--version'], projectRoot);

// Tries to match version string in the output (e.g. "Gradle 7.6.3")
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 [projectRoot].
/// Returns an empty set for apps that do not use product flavors.
Future<Set<String>> productFlavors(String projectRoot) async {
final result = await _run(
['app:tasks', '--all', '--console=auto'],
projectRoot,
);

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');
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
});

test('prints the gradle version', () async {
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
117 changes: 111 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,110 @@ 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 unix based OSs',
() {
setUp(() {
File(
p.join(tempDir.path, 'android', 'gradlew'),
).createSync(recursive: true);
erickzanardo marked this conversation as resolved.
Show resolved Hide resolved
});

test(
'returns true',
() {
expect(gradlew.exists(tempDir.path), isTrue);
},
testOn: 'linux || mac-os',
Copy link
Contributor

Choose a reason for hiding this comment

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

Neat, I didn't know OS1 || OS2 was supported syntax

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah, me neither, I just learned. Apparently testOn uses this package: https://pub.dev/packages/boolean_selector

);
},
);

group(
'when on windows',
() {
setUp(() {
File(
p.join(tempDir.path, 'android', 'gradlew.bat'),
).createSync(recursive: true);
erickzanardo marked this conversation as resolved.
Show resolved Hide resolved
});

test(
'returns true',
() {
expect(gradlew.exists(tempDir.path), isTrue);
},
testOn: 'windows',
);
},
);
});
});

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