diff --git a/packages/shorebird_cli/lib/src/artifact_builder.dart b/packages/shorebird_cli/lib/src/artifact_builder.dart index 53fe2582..67784618 100644 --- a/packages/shorebird_cli/lib/src/artifact_builder.dart +++ b/packages/shorebird_cli/lib/src/artifact_builder.dart @@ -144,6 +144,36 @@ class ArtifactBuilder { } } + Future buildAar({ + required String buildNumber, + Iterable? targetPlatforms, + List argResultsRest = const [], + }) async { + return _runShorebirdBuildCommand(() async { + const executable = 'flutter'; + final targetPlatformArgs = targetPlatforms?.targetPlatformArg; + final arguments = [ + 'build', + 'aar', + '--no-debug', + '--no-profile', + '--build-number=$buildNumber', + if (targetPlatformArgs != null) '--target-platform=$targetPlatformArgs', + ...argResultsRest, + ]; + + final result = await process.run( + executable, + arguments, + runInShell: true, + ); + + if (result.exitCode != ExitCode.success.code) { + throw ArtifactBuildException('Failed to build: ${result.stderr}'); + } + }); + } + /// A wrapper around [command] (which runs a `flutter build` command with /// Shorebird's fork of Flutter) with a try/finally that runs /// `flutter pub get` with the system installation of Flutter to reset diff --git a/packages/shorebird_cli/lib/src/commands/build/build_aar_command.dart b/packages/shorebird_cli/lib/src/commands/build/build_aar_command.dart index 6ef82f15..a02dfeaf 100644 --- a/packages/shorebird_cli/lib/src/commands/build/build_aar_command.dart +++ b/packages/shorebird_cli/lib/src/commands/build/build_aar_command.dart @@ -1,11 +1,10 @@ import 'dart:async'; -import 'dart:io'; import 'package:mason_logger/mason_logger.dart'; import 'package:path/path.dart' as p; +import 'package:shorebird_cli/src/artifact_builder.dart'; import 'package:shorebird_cli/src/command.dart'; import 'package:shorebird_cli/src/logger.dart'; -import 'package:shorebird_cli/src/shorebird_build_mixin.dart'; import 'package:shorebird_cli/src/shorebird_env.dart'; import 'package:shorebird_cli/src/shorebird_validator.dart'; @@ -14,7 +13,7 @@ import 'package:shorebird_cli/src/shorebird_validator.dart'; /// `shorebird build aar` /// Build an Android aar file from your app. /// {@endtemplate} -class BuildAarCommand extends ShorebirdCommand with ShorebirdBuildMixin { +class BuildAarCommand extends ShorebirdCommand { BuildAarCommand() { // We would have a "target" option here, similar to what [BuildApkCommand] // and [BuildAabCommand] have, but target cannot currently be configured in @@ -54,8 +53,8 @@ class BuildAarCommand extends ShorebirdCommand with ShorebirdBuildMixin { final buildNumber = results['build-number'] as String; final buildProgress = logger.progress('Building aar'); try { - await buildAar(buildNumber: buildNumber); - } on ProcessException catch (error) { + await artifactBuilder.buildAar(buildNumber: buildNumber); + } on ArtifactBuildException catch (error) { buildProgress.fail('Failed to build: ${error.message}'); return ExitCode.software.code; } diff --git a/packages/shorebird_cli/test/src/artifact_builder_test.dart b/packages/shorebird_cli/test/src/artifact_builder_test.dart index f70baf03..597e4a3d 100644 --- a/packages/shorebird_cli/test/src/artifact_builder_test.dart +++ b/packages/shorebird_cli/test/src/artifact_builder_test.dart @@ -415,5 +415,88 @@ Either run `flutter pub get` manually, or follow the steps in ${link(uri: Uri.pa }); }); }); + + group('buildAar', () { + const buildNumber = '1.0'; + + test('invokes the correct flutter build command', () async { + await runWithOverrides( + () => builder.buildAar(buildNumber: buildNumber), + ); + + verify( + () => shorebirdProcess.run( + 'flutter', + [ + 'build', + 'aar', + '--no-debug', + '--no-profile', + '--build-number=1.0', + ], + runInShell: any(named: 'runInShell'), + environment: any(named: 'environment'), + ), + ).called(1); + }); + + test('forward arguments to flutter build', () async { + await runWithOverrides( + () => builder.buildAar( + buildNumber: buildNumber, + targetPlatforms: [Arch.arm64], + argResultsRest: ['--foo', 'bar'], + ), + ); + + verify( + () => shorebirdProcess.run( + 'flutter', + [ + 'build', + 'aar', + '--no-debug', + '--no-profile', + '--build-number=1.0', + '--target-platform=android-arm64', + '--foo', + 'bar', + ], + runInShell: any(named: 'runInShell'), + ), + ).called(1); + }); + + group('after a build', () { + group('when the build is successful', () { + setUp(() { + when(() => buildProcessResult.exitCode) + .thenReturn(ExitCode.success.code); + }); + + verifyCorrectFlutterPubGet( + () async => runWithOverrides( + () => builder.buildAar(buildNumber: buildNumber), + ), + ); + + group('when the build fails', () { + setUp(() { + when(() => buildProcessResult.exitCode) + .thenReturn(ExitCode.software.code); + }); + + verifyCorrectFlutterPubGet( + () async => expectLater( + () async => runWithOverrides( + () => builder.buildAar(buildNumber: buildNumber), + ), + throwsA(isA()), + ), + ); + }); + }); + }); + }); }); } diff --git a/packages/shorebird_cli/test/src/commands/build/build_aar_command_test.dart b/packages/shorebird_cli/test/src/commands/build/build_aar_command_test.dart index feec6220..634d8d4a 100644 --- a/packages/shorebird_cli/test/src/commands/build/build_aar_command_test.dart +++ b/packages/shorebird_cli/test/src/commands/build/build_aar_command_test.dart @@ -3,11 +3,10 @@ import 'package:mason_logger/mason_logger.dart'; import 'package:mocktail/mocktail.dart'; import 'package:path/path.dart' as p; import 'package:scoped/scoped.dart'; +import 'package:shorebird_cli/src/artifact_builder.dart'; import 'package:shorebird_cli/src/commands/build/build.dart'; import 'package:shorebird_cli/src/logger.dart'; -import 'package:shorebird_cli/src/os/operating_system_interface.dart'; import 'package:shorebird_cli/src/shorebird_env.dart'; -import 'package:shorebird_cli/src/shorebird_process.dart'; import 'package:shorebird_cli/src/shorebird_validator.dart'; import 'package:test/test.dart'; @@ -19,13 +18,10 @@ void main() { const androidPackageName = 'com.example.my_flutter_module'; late ArgResults argResults; + late ArtifactBuilder artifactBuilder; late Logger logger; - late OperatingSystemInterface operatingSystemInterface; late Progress progress; late ShorebirdEnv shorebirdEnv; - late ShorebirdProcess shorebirdProcess; - late ShorebirdProcessResult buildProcessResult; - late ShorebirdProcessResult flutterPubGetProcessResult; late ShorebirdValidator shorebirdValidator; late BuildAarCommand command; @@ -33,9 +29,8 @@ void main() { return runScoped( body, values: { + artifactBuilderRef.overrideWith(() => artifactBuilder), loggerRef.overrideWith(() => logger), - osInterfaceRef.overrideWith(() => operatingSystemInterface), - processRef.overrideWith(() => shorebirdProcess), shorebirdEnvRef.overrideWith(() => shorebirdEnv), shorebirdValidatorRef.overrideWith(() => shorebirdValidator), }, @@ -44,41 +39,21 @@ void main() { setUp(() { argResults = MockArgResults(); + artifactBuilder = MockArtifactBuilder(); logger = MockLogger(); - flutterPubGetProcessResult = MockProcessResult(); - buildProcessResult = MockProcessResult(); - operatingSystemInterface = MockOperatingSystemInterface(); progress = MockProgress(); shorebirdEnv = MockShorebirdEnv(); - shorebirdProcess = MockShorebirdProcess(); shorebirdValidator = MockShorebirdValidator(); when(() => argResults['build-number']).thenReturn(buildNumber); when(() => argResults.rest).thenReturn([]); when(() => logger.progress(any())).thenReturn(progress); - when( - () => shorebirdProcess.run( - 'flutter', - ['--no-version-check', 'pub', 'get', '--offline'], - runInShell: any(named: 'runInShell'), - useVendedFlutter: false, - ), - ).thenAnswer((_) async => flutterPubGetProcessResult); - when(() => flutterPubGetProcessResult.exitCode) - .thenReturn(ExitCode.success.code); + () => artifactBuilder.buildAar(buildNumber: any(named: 'buildNumber')), + ).thenAnswer((_) async => {}); when( - () => shorebirdProcess.run( - any(), - any(), - runInShell: any(named: 'runInShell'), - ), - ).thenAnswer((invocation) async { - return buildProcessResult; - }); - - when(() => operatingSystemInterface.which('flutter')) - .thenReturn('/path/to/flutter'); + () => shorebirdEnv.androidPackageName, + ).thenReturn(androidPackageName); when( () => shorebirdValidator.validatePreconditions( checkUserIsAuthenticated: any(named: 'checkUserIsAuthenticated'), @@ -86,11 +61,6 @@ void main() { ), ).thenAnswer((_) async {}); - when( - () => shorebirdEnv.androidPackageName, - ).thenReturn(androidPackageName); - when(() => shorebirdEnv.flutterRevision).thenReturn('1234'); - command = runWithOverrides(BuildAarCommand.new) ..testArgResults = argResults; }); @@ -126,49 +96,27 @@ void main() { }); test('exits with code 70 when building aar fails', () async { - when(() => buildProcessResult.exitCode).thenReturn(1); - when(() => buildProcessResult.stderr).thenReturn('oops'); + when( + () => artifactBuilder.buildAar(buildNumber: any(named: 'buildNumber')), + ).thenThrow(ArtifactBuildException('Failed to build: error')); final result = await runWithOverrides(command.run); expect(result, equals(ExitCode.software.code)); - verify( - () => shorebirdProcess.run( - 'flutter', - [ - 'build', - 'aar', - '--no-debug', - '--no-profile', - '--build-number=$buildNumber', - ], - runInShell: any(named: 'runInShell'), - ), - ).called(1); + verify(() => artifactBuilder.buildAar(buildNumber: buildNumber)) + .called(1); verify( () => progress.fail(any(that: contains('Failed to build'))), ).called(1); }); test('exits with code 0 when building aar succeeds', () async { - when(() => buildProcessResult.exitCode).thenReturn(ExitCode.success.code); final result = await runWithOverrides(command.run); expect(result, equals(ExitCode.success.code)); - verify( - () => shorebirdProcess.run( - 'flutter', - [ - 'build', - 'aar', - '--no-debug', - '--no-profile', - '--build-number=$buildNumber', - ], - runInShell: any(named: 'runInShell'), - ), - ).called(1); + verify(() => artifactBuilder.buildAar(buildNumber: buildNumber)) + .called(1); verify( () => logger.info( ''' @@ -190,20 +138,5 @@ ${lightCyan.wrap( ), ).called(1); }); - - test('runs flutter pub get with system flutter after successful build', - () async { - when(() => buildProcessResult.exitCode).thenReturn(ExitCode.success.code); - await runWithOverrides(command.run); - - verify( - () => shorebirdProcess.run( - 'flutter', - ['--no-version-check', 'pub', 'get', '--offline'], - runInShell: any(named: 'runInShell'), - useVendedFlutter: false, - ), - ).called(1); - }); }); }