diff --git a/packages/shorebird_cli/README.md b/packages/shorebird_cli/README.md index 9a466693..f10525b0 100644 --- a/packages/shorebird_cli/README.md +++ b/packages/shorebird_cli/README.md @@ -202,32 +202,30 @@ shorebird release **Sample** ``` -shorebird release -āœ“ Building release (17.9s) -āœ“ Fetching apps (0.1s) +$ shorebird release +āœ“ Building release (5.1s) +āœ“ Fetching apps (0.2s) -What is the version of this release? (0.1.0) 0.1.0 +What is the version of this release? (1.0.0) 1.0.0 šŸš€ Ready to create a new release! -šŸ“± App: My App (61fc9c16-3c4a-4825-a155-9765993614aa) -šŸ“¦ Release Version: 0.1.0 -āš™ļø Architecture: aarch64 -šŸ•¹ļø Platform: android -#ļøāƒ£ Hash: cfe26dddf8aff17131042f9dfad409c83eb130c5a9f2fd6f77325b2388062265 - -Your next step is to upload the release artifact to the Play Store. -./build/app/outputs/bundle/release/app-release.aab - -See the following link for more information: -https://support.google.com/googleplay/android-developer/answer/9859152?hl=en +šŸ“± App: My App (30370f27-dbf1-4673-8b20-fb096e38dffa) +šŸ“¦ Release Version: 1.0.0 +šŸ•¹ļø Platform: android (arm64, arm32, x86) Would you like to continue? (y/N) Yes āœ“ Fetching releases (55ms) āœ“ Creating release (45ms) -āœ“ Creating artifact (1.8s) +āœ“ Creating artifacts (4.6s) āœ… Published Release! + +Your next step is to upload the app bundle to the Play Store. +./build/app/outputs/bundle/release/app-release.aab + +See the following link for more information: +https://support.google.com/googleplay/android-developer/answer/9859152?hl=en ``` ### Patch diff --git a/packages/shorebird_cli/lib/src/commands/release_command.dart b/packages/shorebird_cli/lib/src/commands/release_command.dart index 4e5dc814..e04edfab 100644 --- a/packages/shorebird_cli/lib/src/commands/release_command.dart +++ b/packages/shorebird_cli/lib/src/commands/release_command.dart @@ -41,13 +41,6 @@ class ReleaseCommand extends ShorebirdCommand allowed: ['android'], allowedHelp: {'android': 'The Android platform.'}, defaultsTo: 'android', - ) - ..addOption( - 'arch', - help: 'The architecture of the release (e.g. "aarch64").', - allowed: ['aarch64'], - allowedHelp: {'aarch64': 'The 64-bit ARM architecture.'}, - defaultsTo: 'aarch64', ); } @@ -88,27 +81,6 @@ make smaller updates to your app. return ExitCode.software.code; } - final artifactPath = p.join( - Directory.current.path, - 'build', - 'app', - 'intermediates', - 'stripped_native_libs', - 'release', - 'out', - 'lib', - 'arm64-v8a', - 'libapp.so', - ); - - final artifact = File(artifactPath); - - if (!artifact.existsSync()) { - logger.err('Artifact not found: "${artifact.path}"'); - return ExitCode.software.code; - } - - final hash = _hashFn(await artifact.readAsBytes()); final pubspecYaml = getPubspecYaml()!; final shorebirdYaml = getShorebirdYaml()!; final codePushClient = buildCodePushClient( @@ -152,8 +124,11 @@ Did you forget to run "shorebird init"?''', 'What is the version of this release?', defaultValue: pubspecVersionString, ); - final arch = results['arch'] as String; + final platform = results['platform'] as String; + final archNames = ShorebirdBuildMixin.architectures.keys.map( + (arch) => arch.name, + ); logger.info(''' @@ -161,9 +136,7 @@ ${styleBold.wrap(lightGreen.wrap('šŸš€ Ready to create a new release!'))} šŸ“± App: ${lightCyan.wrap(app.displayName)} ${lightCyan.wrap('(${app.id})')} šŸ“¦ Release Version: ${lightCyan.wrap(releaseVersion)} -āš™ļø Architecture: ${lightCyan.wrap(arch)} -šŸ•¹ļø Platform: ${lightCyan.wrap(platform)} -#ļøāƒ£ Hash: ${lightCyan.wrap(hash)} +šŸ•¹ļø Platform: ${lightCyan.wrap(platform)} ${lightCyan.wrap('(${archNames.join(', ')})')} '''); final confirm = logger.confirm('Would you like to continue?'); @@ -198,26 +171,44 @@ ${styleBold.wrap(lightGreen.wrap('šŸš€ Ready to create a new release!'))} } } - final createArtifactProgress = logger.progress('Creating artifact'); - try { - await codePushClient.createReleaseArtifact( - releaseId: release.id, - artifactPath: artifact.path, - arch: arch, - platform: platform, - hash: hash, + final createArtifactProgress = logger.progress('Creating artifacts'); + for (final archMetadata in ShorebirdBuildMixin.architectures.values) { + final artifactPath = p.join( + Directory.current.path, + 'build', + 'app', + 'intermediates', + 'stripped_native_libs', + 'release', + 'out', + 'lib', + archMetadata.path, + 'libapp.so', ); - createArtifactProgress.complete(); - } catch (error) { - createArtifactProgress.fail('$error'); - return ExitCode.software.code; + final artifact = File(artifactPath); + final hash = _hashFn(await artifact.readAsBytes()); + + try { + await codePushClient.createReleaseArtifact( + releaseId: release.id, + artifactPath: artifact.path, + arch: archMetadata.arch, + platform: platform, + hash: hash, + ); + } catch (error) { + createArtifactProgress.fail('$error'); + return ExitCode.software.code; + } } + createArtifactProgress.complete(); + logger ..success('\nāœ… Published Release!') ..info(''' -Your next step is to upload the release artifact to the Play Store. +Your next step is to upload the app bundle to the Play Store. ${lightCyan.wrap("./build/app/outputs/bundle/release/app-release.aab")} See the following link for more information: diff --git a/packages/shorebird_cli/lib/src/shorebird_build_mixin.dart b/packages/shorebird_cli/lib/src/shorebird_build_mixin.dart index cdd6e55d..8af7d7b6 100644 --- a/packages/shorebird_cli/lib/src/shorebird_build_mixin.dart +++ b/packages/shorebird_cli/lib/src/shorebird_build_mixin.dart @@ -3,7 +3,36 @@ import 'dart:io'; import 'package:mason_logger/mason_logger.dart'; import 'package:shorebird_cli/src/command.dart'; +enum Arch { + arm64, + arm32, + x86, +} + +class ArchMetadata { + const ArchMetadata({required this.path, required this.arch}); + + final String path; + final String arch; +} + mixin ShorebirdBuildMixin on ShorebirdCommand { + // TODO(felangel): extend to other platforms. + static const architectures = { + Arch.arm64: ArchMetadata( + path: 'arm64-v8a', + arch: 'aarch64', + ), + Arch.arm32: ArchMetadata( + path: 'armeabi-v7a', + arch: 'arm', + ), + Arch.x86: ArchMetadata( + path: 'x86_64', + arch: 'x86_64', + ), + }; + Future buildRelease() async { const executable = 'flutter'; final arguments = [ diff --git a/packages/shorebird_cli/test/src/commands/release_command_test.dart b/packages/shorebird_cli/test/src/commands/release_command_test.dart index e4f26dd0..4820e832 100644 --- a/packages/shorebird_cli/test/src/commands/release_command_test.dart +++ b/packages/shorebird_cli/test/src/commands/release_command_test.dart @@ -8,6 +8,7 @@ import 'package:path/path.dart' as p; import 'package:shorebird_cli/src/auth/auth.dart'; import 'package:shorebird_cli/src/commands/commands.dart'; import 'package:shorebird_cli/src/config/config.dart'; +import 'package:shorebird_cli/src/shorebird_build_mixin.dart'; import 'package:shorebird_cli/src/validators/validators.dart'; import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; import 'package:test/test.dart'; @@ -86,6 +87,24 @@ flutter: return tempDir; } + void setUpTempArtifacts(Directory dir) { + for (final archMetadata in ShorebirdBuildMixin.architectures.values) { + final artifactPath = p.join( + dir.path, + 'build', + 'app', + 'intermediates', + 'stripped_native_libs', + 'release', + 'out', + 'lib', + archMetadata.path, + 'libapp.so', + ); + File(artifactPath).createSync(recursive: true); + } + } + setUp(() { argResults = _MockArgResults(); applicationConfigHome = Directory.systemTemp.createTempSync(); @@ -192,36 +211,12 @@ flutter: expect(exitCode, equals(ExitCode.software.code)); }); - test('throws software error when artifact is not found (default).', - () async { - final tempDir = setUpTempDir(); - final exitCode = await IOOverrides.runZoned( - command.run, - getCurrentDirectory: () => tempDir, - ); - verify( - () => logger.err(any(that: contains('Artifact not found:'))), - ).called(1); - expect(exitCode, ExitCode.software.code); - }); - test('throws error when fetching apps fails.', () async { const error = 'something went wrong'; when(() => codePushClient.getApps()).thenThrow(error); final tempDir = setUpTempDir(); - final artifactPath = p.join( - tempDir.path, - 'build', - 'app', - 'intermediates', - 'stripped_native_libs', - 'release', - 'out', - 'lib', - 'arm64-v8a', - 'libapp.so', - ); - File(artifactPath).createSync(recursive: true); + setUpTempArtifacts(tempDir); + final exitCode = await IOOverrides.runZoned( command.run, getCurrentDirectory: () => tempDir, @@ -236,19 +231,7 @@ flutter: ).thenReturn(appDisplayName); when(() => codePushClient.getApps()).thenAnswer((_) async => []); final tempDir = setUpTempDir(); - final artifactPath = p.join( - tempDir.path, - 'build', - 'app', - 'intermediates', - 'stripped_native_libs', - 'release', - 'out', - 'lib', - 'arm64-v8a', - 'libapp.so', - ); - File(artifactPath).createSync(recursive: true); + setUpTempArtifacts(tempDir); final exitCode = await IOOverrides.runZoned( command.run, getCurrentDirectory: () => tempDir, @@ -269,19 +252,7 @@ Did you forget to run "shorebird init"?''', () => logger.prompt(any(), defaultValue: any(named: 'defaultValue')), ).thenReturn(appDisplayName); final tempDir = setUpTempDir(); - final artifactPath = p.join( - tempDir.path, - 'build', - 'app', - 'intermediates', - 'stripped_native_libs', - 'release', - 'out', - 'lib', - 'arm64-v8a', - 'libapp.so', - ); - File(artifactPath).createSync(recursive: true); + setUpTempArtifacts(tempDir); final exitCode = await IOOverrides.runZoned( command.run, getCurrentDirectory: () => tempDir, @@ -296,19 +267,7 @@ Did you forget to run "shorebird init"?''', () => codePushClient.getReleases(appId: any(named: 'appId')), ).thenThrow(error); final tempDir = setUpTempDir(); - final artifactPath = p.join( - tempDir.path, - 'build', - 'app', - 'intermediates', - 'stripped_native_libs', - 'release', - 'out', - 'lib', - 'arm64-v8a', - 'libapp.so', - ); - File(artifactPath).createSync(recursive: true); + setUpTempArtifacts(tempDir); final exitCode = await IOOverrides.runZoned( command.run, getCurrentDirectory: () => tempDir, @@ -330,19 +289,7 @@ Did you forget to run "shorebird init"?''', ), ).thenThrow(error); final tempDir = setUpTempDir(); - final artifactPath = p.join( - tempDir.path, - 'build', - 'app', - 'intermediates', - 'stripped_native_libs', - 'release', - 'out', - 'lib', - 'arm64-v8a', - 'libapp.so', - ); - File(artifactPath).createSync(recursive: true); + setUpTempArtifacts(tempDir); final exitCode = await IOOverrides.runZoned( command.run, getCurrentDirectory: () => tempDir, @@ -366,19 +313,7 @@ Did you forget to run "shorebird init"?''', ), ).thenThrow(error); final tempDir = setUpTempDir(); - final artifactPath = p.join( - tempDir.path, - 'build', - 'app', - 'intermediates', - 'stripped_native_libs', - 'release', - 'out', - 'lib', - 'arm64-v8a', - 'libapp.so', - ); - File(artifactPath).createSync(recursive: true); + setUpTempArtifacts(tempDir); final exitCode = await IOOverrides.runZoned( command.run, getCurrentDirectory: () => tempDir, @@ -389,19 +324,7 @@ Did you forget to run "shorebird init"?''', test('succeeds when release is successful', () async { final tempDir = setUpTempDir(); - final artifactPath = p.join( - tempDir.path, - 'build', - 'app', - 'intermediates', - 'stripped_native_libs', - 'release', - 'out', - 'lib', - 'arm64-v8a', - 'libapp.so', - ); - File(artifactPath).createSync(recursive: true); + setUpTempArtifacts(tempDir); final exitCode = await IOOverrides.runZoned( command.run, getCurrentDirectory: () => tempDir, @@ -425,19 +348,7 @@ Did you forget to run "shorebird init"?''', ], ); final tempDir = setUpTempDir(); - final artifactPath = p.join( - tempDir.path, - 'build', - 'app', - 'intermediates', - 'stripped_native_libs', - 'release', - 'out', - 'lib', - 'arm64-v8a', - 'libapp.so', - ); - File(artifactPath).createSync(recursive: true); + setUpTempArtifacts(tempDir); final exitCode = await IOOverrides.runZoned( command.run, getCurrentDirectory: () => tempDir,