diff --git a/packages/shorebird_cli/bin/shorebird.dart b/packages/shorebird_cli/bin/shorebird.dart index 8fcd0a5c..e5b38ec4 100644 --- a/packages/shorebird_cli/bin/shorebird.dart +++ b/packages/shorebird_cli/bin/shorebird.dart @@ -16,6 +16,7 @@ import 'package:shorebird_cli/src/java.dart'; import 'package:shorebird_cli/src/logger.dart'; import 'package:shorebird_cli/src/platform.dart'; import 'package:shorebird_cli/src/process.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; import 'package:shorebird_cli/src/shorebird_validator.dart'; import 'package:shorebird_cli/src/shorebird_version_manager.dart'; import 'package:shorebird_cli/src/xcodebuild.dart'; @@ -40,6 +41,7 @@ Future main(List args) async { loggerRef, platformRef, processRef, + shorebirdEnvRef, shorebirdValidatorRef, shorebirdVersionManagerRef, xcodeBuildRef, diff --git a/packages/shorebird_cli/lib/src/cache.dart b/packages/shorebird_cli/lib/src/cache.dart index 01cbed03..223ce60f 100644 --- a/packages/shorebird_cli/lib/src/cache.dart +++ b/packages/shorebird_cli/lib/src/cache.dart @@ -7,7 +7,8 @@ import 'package:path/path.dart' as p; import 'package:platform/platform.dart'; import 'package:scoped/scoped.dart'; import 'package:shorebird_cli/src/http_client/http_client.dart'; -import 'package:shorebird_cli/src/shorebird_environment.dart'; +import 'package:shorebird_cli/src/platform.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; typedef ArchiveExtracter = Future Function( String archivePath, @@ -32,7 +33,6 @@ class Cache { Cache({ http.Client? httpClient, this.extractArchive = _defaultArchiveExtractor, - Platform platform = const LocalPlatform(), }) : httpClient = httpClient ?? retryingHttpClient(http.Client()) { registerArtifact(PatchArtifact(cache: this, platform: platform)); registerArtifact(BundleToolArtifact(cache: this, platform: platform)); @@ -72,7 +72,7 @@ class Cache { /// The Shorebird cache directory. static Directory get shorebirdCacheDirectory { return Directory( - p.join(ShorebirdEnvironment.shorebirdRoot.path, 'bin', 'cache'), + p.join(shorebirdEnv.shorebirdRoot.path, 'bin', 'cache'), ); } @@ -174,7 +174,7 @@ class PatchArtifact extends CachedArtifact { artifactName += 'windows-x64.zip'; } - return '${cache.storageBaseUrl}/${cache.storageBucket}/shorebird/${ShorebirdEnvironment.shorebirdEngineRevision}/$artifactName'; + return '${cache.storageBaseUrl}/${cache.storageBucket}/shorebird/${shorebirdEnv.shorebirdEngineRevision}/$artifactName'; } } diff --git a/packages/shorebird_cli/lib/src/code_push_client_wrapper.dart b/packages/shorebird_cli/lib/src/code_push_client_wrapper.dart index 2b0d1cf7..b630aff5 100644 --- a/packages/shorebird_cli/lib/src/code_push_client_wrapper.dart +++ b/packages/shorebird_cli/lib/src/code_push_client_wrapper.dart @@ -10,7 +10,7 @@ import 'package:scoped/scoped.dart'; import 'package:shorebird_cli/src/auth/auth.dart'; import 'package:shorebird_cli/src/logger.dart'; import 'package:shorebird_cli/src/shorebird_build_mixin.dart'; -import 'package:shorebird_cli/src/shorebird_environment.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; import 'package:shorebird_cli/src/third_party/flutter_tools/lib/flutter_tools.dart'; import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; @@ -44,7 +44,7 @@ ScopedRef codePushClientWrapperRef = create(() { return CodePushClientWrapper( codePushClient: CodePushClient( httpClient: auth.client, - hostedUri: ShorebirdEnvironment.hostedUri, + hostedUri: shorebirdEnv.hostedUri, ), ); }); @@ -63,6 +63,25 @@ class CodePushClientWrapper { final CodePushClient codePushClient; + Future createApp({String? appName}) async { + late final String displayName; + if (appName == null) { + String? defaultAppName; + try { + defaultAppName = shorebirdEnv.getPubspecYaml()?.name; + } catch (_) {} + + displayName = logger.prompt( + '${lightGreen.wrap('?')} How should we refer to this app?', + defaultValue: defaultAppName, + ); + } else { + displayName = appName; + } + + return codePushClient.createApp(displayName: displayName); + } + Future> getApps() async { final fetchAppsProgress = logger.progress('Fetching apps'); try { diff --git a/packages/shorebird_cli/lib/src/command.dart b/packages/shorebird_cli/lib/src/command.dart index 9d93869e..50195d6b 100644 --- a/packages/shorebird_cli/lib/src/command.dart +++ b/packages/shorebird_cli/lib/src/command.dart @@ -25,12 +25,6 @@ typedef StartProcess = Future Function( }); abstract class ShorebirdCommand extends Command { - ShorebirdCommand({ - CodePushClientBuilder? buildCodePushClient, - }) : buildCodePushClient = buildCodePushClient ?? CodePushClient.new; - - final CodePushClientBuilder buildCodePushClient; - // We don't currently have a test involving both a CommandRunner // and a Command, so we can't test this getter. // coverage:ignore-start diff --git a/packages/shorebird_cli/lib/src/command_runner.dart b/packages/shorebird_cli/lib/src/command_runner.dart index d9241546..6a6f3568 100644 --- a/packages/shorebird_cli/lib/src/command_runner.dart +++ b/packages/shorebird_cli/lib/src/command_runner.dart @@ -8,7 +8,7 @@ import 'package:scoped/scoped.dart'; import 'package:shorebird_cli/src/commands/commands.dart'; import 'package:shorebird_cli/src/logger.dart'; import 'package:shorebird_cli/src/process.dart'; -import 'package:shorebird_cli/src/shorebird_environment.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; import 'package:shorebird_cli/src/version.dart'; const executableName = 'shorebird'; @@ -144,7 +144,7 @@ ${lightCyan.wrap('shorebird release android -- --no-pub lib/main.dart')}''', logger.info( ''' Shorebird $packageVersion -Shorebird Engine • revision ${ShorebirdEnvironment.shorebirdEngineRevision}''', +Shorebird Engine • revision ${shorebirdEnv.shorebirdEngineRevision}''', ); exitCode = ExitCode.success.code; } else { diff --git a/packages/shorebird_cli/lib/src/commands/account/downgrade_account_command.dart b/packages/shorebird_cli/lib/src/commands/account/downgrade_account_command.dart index 537baba8..002c78f9 100644 --- a/packages/shorebird_cli/lib/src/commands/account/downgrade_account_command.dart +++ b/packages/shorebird_cli/lib/src/commands/account/downgrade_account_command.dart @@ -2,15 +2,14 @@ import 'dart:async'; import 'package:intl/intl.dart'; import 'package:mason_logger/mason_logger.dart'; -import 'package:shorebird_cli/src/auth/auth.dart'; +import 'package:shorebird_cli/src/code_push_client_wrapper.dart'; import 'package:shorebird_cli/src/command.dart'; import 'package:shorebird_cli/src/logger.dart'; -import 'package:shorebird_cli/src/shorebird_environment.dart'; import 'package:shorebird_cli/src/shorebird_validator.dart'; import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; class DowngradeAccountCommand extends ShorebirdCommand { - DowngradeAccountCommand({super.buildCodePushClient}); + DowngradeAccountCommand(); @override String get name => 'downgrade'; @@ -28,14 +27,10 @@ class DowngradeAccountCommand extends ShorebirdCommand { return e.exitCode.code; } - final client = buildCodePushClient( - httpClient: auth.client, - hostedUri: ShorebirdEnvironment.hostedUri, - ); - final User user; try { - final currentUser = await client.getCurrentUser(); + final currentUser = + await codePushClientWrapper.codePushClient.getCurrentUser(); if (currentUser == null) { throw Exception('Failed to retrieve user information.'); } @@ -65,7 +60,8 @@ class DowngradeAccountCommand extends ShorebirdCommand { final DateTime cancellationDate; try { - cancellationDate = await client.cancelSubscription(); + cancellationDate = + await codePushClientWrapper.codePushClient.cancelSubscription(); } catch (error) { progress.fail('Failed to downgrade plan. Error: $error'); return ExitCode.software.code; diff --git a/packages/shorebird_cli/lib/src/commands/account/upgrade_account_command.dart b/packages/shorebird_cli/lib/src/commands/account/upgrade_account_command.dart index 63e9fd22..de118f7e 100644 --- a/packages/shorebird_cli/lib/src/commands/account/upgrade_account_command.dart +++ b/packages/shorebird_cli/lib/src/commands/account/upgrade_account_command.dart @@ -1,10 +1,9 @@ import 'dart:async'; import 'package:mason_logger/mason_logger.dart'; -import 'package:shorebird_cli/src/auth/auth.dart'; +import 'package:shorebird_cli/src/code_push_client_wrapper.dart'; import 'package:shorebird_cli/src/command.dart'; import 'package:shorebird_cli/src/logger.dart'; -import 'package:shorebird_cli/src/shorebird_environment.dart'; import 'package:shorebird_cli/src/shorebird_validator.dart'; import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; @@ -13,7 +12,7 @@ import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; /// {@endtemplate} class UpgradeAccountCommand extends ShorebirdCommand { /// {@macro upgrade_account_command} - UpgradeAccountCommand({super.buildCodePushClient}); + UpgradeAccountCommand(); @override String get name => 'upgrade'; @@ -38,16 +37,11 @@ Please use $consoleLink instead.''', return e.exitCode.code; } - final client = buildCodePushClient( - httpClient: auth.client, - hostedUri: ShorebirdEnvironment.hostedUri, - ); - final progress = logger.progress('Retrieving account information'); final User? user; try { - user = await client.getCurrentUser(); + user = await codePushClientWrapper.codePushClient.getCurrentUser(); if (user == null) { progress.fail(''' We're having trouble retrieving your account information. @@ -69,7 +63,8 @@ Please try logging out using ${lightCyan.wrap('shorebird logout')} and logging b final Uri paymentLink; try { - paymentLink = await client.createPaymentLink(); + paymentLink = + await codePushClientWrapper.codePushClient.createPaymentLink(); } catch (error) { progress.fail(error.toString()); return ExitCode.software.code; diff --git a/packages/shorebird_cli/lib/src/commands/apps/create_apps_command.dart b/packages/shorebird_cli/lib/src/commands/apps/create_apps_command.dart index f6539494..8347ba9a 100644 --- a/packages/shorebird_cli/lib/src/commands/apps/create_apps_command.dart +++ b/packages/shorebird_cli/lib/src/commands/apps/create_apps_command.dart @@ -1,10 +1,9 @@ import 'dart:async'; import 'package:mason_logger/mason_logger.dart'; +import 'package:shorebird_cli/src/code_push_client_wrapper.dart'; import 'package:shorebird_cli/src/command.dart'; import 'package:shorebird_cli/src/logger.dart'; -import 'package:shorebird_cli/src/shorebird_config_mixin.dart'; -import 'package:shorebird_cli/src/shorebird_create_app_mixin.dart'; import 'package:shorebird_cli/src/shorebird_validator.dart'; import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; @@ -13,10 +12,9 @@ import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; /// `shorebird apps create` /// Create a new app on Shorebird. /// {@endtemplate} -class CreateAppCommand extends ShorebirdCommand - with ShorebirdConfigMixin, ShorebirdCreateAppMixin { +class CreateAppCommand extends ShorebirdCommand { /// {@macro create_app_command} - CreateAppCommand({super.buildCodePushClient}) { + CreateAppCommand() { argParser.addOption( 'app-name', help: ''' @@ -44,7 +42,7 @@ Defaults to the name in "pubspec.yaml".''', final appName = results['app-name'] as String?; late final App app; try { - app = await createApp(appName: appName); + app = await codePushClientWrapper.createApp(appName: appName); } catch (error) { logger.err('$error'); return ExitCode.software.code; diff --git a/packages/shorebird_cli/lib/src/commands/apps/delete_apps_command.dart b/packages/shorebird_cli/lib/src/commands/apps/delete_apps_command.dart index 2dad04dc..0780c75e 100644 --- a/packages/shorebird_cli/lib/src/commands/apps/delete_apps_command.dart +++ b/packages/shorebird_cli/lib/src/commands/apps/delete_apps_command.dart @@ -1,10 +1,10 @@ import 'dart:async'; import 'package:mason_logger/mason_logger.dart'; -import 'package:shorebird_cli/src/auth/auth.dart'; +import 'package:shorebird_cli/src/code_push_client_wrapper.dart'; import 'package:shorebird_cli/src/command.dart'; import 'package:shorebird_cli/src/logger.dart'; -import 'package:shorebird_cli/src/shorebird_environment.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; import 'package:shorebird_cli/src/shorebird_validator.dart'; /// {@template delete_app_command} @@ -14,7 +14,7 @@ import 'package:shorebird_cli/src/shorebird_validator.dart'; /// {@endtemplate} class DeleteAppCommand extends ShorebirdCommand { /// {@macro delete_app_command} - DeleteAppCommand({super.buildCodePushClient}) { + DeleteAppCommand() { argParser ..addOption( 'app-id', @@ -60,7 +60,7 @@ Please use $consoleLink instead.''', if (appIdArg == null) { String? defaultAppId; try { - defaultAppId = ShorebirdEnvironment.getShorebirdYaml()?.appId; + defaultAppId = shorebirdEnv.getShorebirdYaml()?.appId; } catch (_) {} appId = logger.prompt( @@ -71,11 +71,6 @@ Please use $consoleLink instead.''', appId = appIdArg; } - final client = buildCodePushClient( - httpClient: auth.client, - hostedUri: ShorebirdEnvironment.hostedUri, - ); - final shouldProceed = force || logger.confirm('Deleting an app is permanent. Continue?'); if (!shouldProceed) { @@ -84,7 +79,7 @@ Please use $consoleLink instead.''', } try { - await client.deleteApp(appId: appId); + await codePushClientWrapper.codePushClient.deleteApp(appId: appId); } catch (error) { logger.err('$error'); return ExitCode.software.code; 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 3b5bc0e0..6ef82f15 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 @@ -6,7 +6,7 @@ import 'package:path/path.dart' as p; 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_config_mixin.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; import 'package:shorebird_cli/src/shorebird_validator.dart'; /// {@template build_aar_command} @@ -14,8 +14,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 ShorebirdConfigMixin, ShorebirdBuildMixin { +class BuildAarCommand extends ShorebirdCommand with ShorebirdBuildMixin { BuildAarCommand() { // We would have a "target" option here, similar to what [BuildApkCommand] // and [BuildAabCommand] have, but target cannot currently be configured in @@ -47,7 +46,7 @@ class BuildAarCommand extends ShorebirdCommand return e.exitCode.code; } - if (androidPackageName == null) { + if (shorebirdEnv.androidPackageName == null) { logger.err('Could not find androidPackage in pubspec.yaml.'); return ExitCode.config.code; } @@ -68,7 +67,7 @@ class BuildAarCommand extends ShorebirdCommand 'host', 'outputs', 'repo', - ...androidPackageName!.split('.'), + ...shorebirdEnv.androidPackageName!.split('.'), 'flutter_release', buildNumber, 'flutter_release-$buildNumber.aar', diff --git a/packages/shorebird_cli/lib/src/commands/build/build_apk_command.dart b/packages/shorebird_cli/lib/src/commands/build/build_apk_command.dart index 9b8aa0cf..13508e26 100644 --- a/packages/shorebird_cli/lib/src/commands/build/build_apk_command.dart +++ b/packages/shorebird_cli/lib/src/commands/build/build_apk_command.dart @@ -6,7 +6,6 @@ import 'package:shorebird_cli/src/command.dart'; import 'package:shorebird_cli/src/doctor.dart'; import 'package:shorebird_cli/src/logger.dart'; import 'package:shorebird_cli/src/shorebird_build_mixin.dart'; -import 'package:shorebird_cli/src/shorebird_config_mixin.dart'; import 'package:shorebird_cli/src/shorebird_validator.dart'; /// {@template build_apk_command} @@ -14,8 +13,7 @@ import 'package:shorebird_cli/src/shorebird_validator.dart'; /// `shorebird build apk` /// Build an Android APK file from your app. /// {@endtemplate} -class BuildApkCommand extends ShorebirdCommand - with ShorebirdConfigMixin, ShorebirdBuildMixin { +class BuildApkCommand extends ShorebirdCommand with ShorebirdBuildMixin { /// {@macro build_apk_command} BuildApkCommand() { argParser diff --git a/packages/shorebird_cli/lib/src/commands/build/build_app_bundle_command.dart b/packages/shorebird_cli/lib/src/commands/build/build_app_bundle_command.dart index b67a9125..ee60daa0 100644 --- a/packages/shorebird_cli/lib/src/commands/build/build_app_bundle_command.dart +++ b/packages/shorebird_cli/lib/src/commands/build/build_app_bundle_command.dart @@ -6,7 +6,6 @@ import 'package:shorebird_cli/src/command.dart'; import 'package:shorebird_cli/src/doctor.dart'; import 'package:shorebird_cli/src/logger.dart'; import 'package:shorebird_cli/src/shorebird_build_mixin.dart'; -import 'package:shorebird_cli/src/shorebird_config_mixin.dart'; import 'package:shorebird_cli/src/shorebird_validator.dart'; /// {@template build_app_bundle_command} @@ -14,8 +13,7 @@ import 'package:shorebird_cli/src/shorebird_validator.dart'; /// `shorebird build appbundle` /// Build an Android App Bundle file from your app. /// {@endtemplate} -class BuildAppBundleCommand extends ShorebirdCommand - with ShorebirdConfigMixin, ShorebirdBuildMixin { +class BuildAppBundleCommand extends ShorebirdCommand with ShorebirdBuildMixin { /// {@macro build_app_bundle_command} BuildAppBundleCommand() { argParser diff --git a/packages/shorebird_cli/lib/src/commands/build/build_ipa_command.dart b/packages/shorebird_cli/lib/src/commands/build/build_ipa_command.dart index d3282c10..0e152bde 100644 --- a/packages/shorebird_cli/lib/src/commands/build/build_ipa_command.dart +++ b/packages/shorebird_cli/lib/src/commands/build/build_ipa_command.dart @@ -6,7 +6,6 @@ import 'package:shorebird_cli/src/command.dart'; import 'package:shorebird_cli/src/doctor.dart'; import 'package:shorebird_cli/src/logger.dart'; import 'package:shorebird_cli/src/shorebird_build_mixin.dart'; -import 'package:shorebird_cli/src/shorebird_config_mixin.dart'; import 'package:shorebird_cli/src/shorebird_validator.dart'; /// {@template build_ipa_command} @@ -14,8 +13,7 @@ import 'package:shorebird_cli/src/shorebird_validator.dart'; /// Builds an .xcarchive and optionally .ipa for an iOS app to be generated for /// App Store submission. /// {@endtemplate} -class BuildIpaCommand extends ShorebirdCommand - with ShorebirdConfigMixin, ShorebirdBuildMixin { +class BuildIpaCommand extends ShorebirdCommand with ShorebirdBuildMixin { /// {@macro build_ipa_command} BuildIpaCommand() { argParser diff --git a/packages/shorebird_cli/lib/src/commands/cache/clean_cache_command.dart b/packages/shorebird_cli/lib/src/commands/cache/clean_cache_command.dart index 09d5beae..fc320d16 100644 --- a/packages/shorebird_cli/lib/src/commands/cache/clean_cache_command.dart +++ b/packages/shorebird_cli/lib/src/commands/cache/clean_cache_command.dart @@ -6,13 +6,12 @@ import 'package:shorebird_cli/src/cache.dart'; import 'package:shorebird_cli/src/command.dart'; import 'package:shorebird_cli/src/logger.dart'; import 'package:shorebird_cli/src/platform.dart'; -import 'package:shorebird_cli/src/shorebird_config_mixin.dart'; /// {@template clean_cache_command} /// `shorebird cache clean` /// Clears the Shorebird cache directory. /// {@endtemplate} -class CleanCacheCommand extends ShorebirdCommand with ShorebirdConfigMixin { +class CleanCacheCommand extends ShorebirdCommand { /// {@macro clean_cache_command} CleanCacheCommand(); diff --git a/packages/shorebird_cli/lib/src/commands/collaborators/add_collaborators_command.dart b/packages/shorebird_cli/lib/src/commands/collaborators/add_collaborators_command.dart index c6ba436c..b5e7f5e1 100644 --- a/packages/shorebird_cli/lib/src/commands/collaborators/add_collaborators_command.dart +++ b/packages/shorebird_cli/lib/src/commands/collaborators/add_collaborators_command.dart @@ -1,10 +1,10 @@ import 'dart:async'; import 'package:mason_logger/mason_logger.dart'; -import 'package:shorebird_cli/src/auth/auth.dart'; +import 'package:shorebird_cli/src/code_push_client_wrapper.dart'; import 'package:shorebird_cli/src/command.dart'; import 'package:shorebird_cli/src/logger.dart'; -import 'package:shorebird_cli/src/shorebird_environment.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; import 'package:shorebird_cli/src/shorebird_validator.dart'; /// {@template add_collaborators_command} @@ -13,7 +13,7 @@ import 'package:shorebird_cli/src/shorebird_validator.dart'; /// {@endtemplate} class AddCollaboratorsCommand extends ShorebirdCommand { /// {@macro add_collaborators_command} - AddCollaboratorsCommand({super.buildCodePushClient}) { + AddCollaboratorsCommand() { argParser ..addOption( _appIdOption, @@ -44,13 +44,8 @@ class AddCollaboratorsCommand extends ShorebirdCommand { return e.exitCode.code; } - final client = buildCodePushClient( - httpClient: auth.client, - hostedUri: ShorebirdEnvironment.hostedUri, - ); - final appId = results[_appIdOption] as String? ?? - ShorebirdEnvironment.getShorebirdYaml()?.appId; + shorebirdEnv.getShorebirdYaml()?.appId; if (appId == null) { logger.err( ''' @@ -82,7 +77,10 @@ ${styleBold.wrap(lightGreen.wrap('šŸš€ Ready to add a new collaborator!'))} final progress = logger.progress('Adding collaborator'); try { - await client.createCollaborator(appId: appId, email: collaborator); + await codePushClientWrapper.codePushClient.createCollaborator( + appId: appId, + email: collaborator, + ); progress.complete(); } catch (error) { progress.fail(); diff --git a/packages/shorebird_cli/lib/src/commands/collaborators/delete_collaborators_command.dart b/packages/shorebird_cli/lib/src/commands/collaborators/delete_collaborators_command.dart index 5b46aa08..4c621ebb 100644 --- a/packages/shorebird_cli/lib/src/commands/collaborators/delete_collaborators_command.dart +++ b/packages/shorebird_cli/lib/src/commands/collaborators/delete_collaborators_command.dart @@ -2,10 +2,10 @@ import 'dart:async'; import 'package:collection/collection.dart'; import 'package:mason_logger/mason_logger.dart'; -import 'package:shorebird_cli/src/auth/auth.dart'; +import 'package:shorebird_cli/src/code_push_client_wrapper.dart'; import 'package:shorebird_cli/src/command.dart'; import 'package:shorebird_cli/src/logger.dart'; -import 'package:shorebird_cli/src/shorebird_environment.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; import 'package:shorebird_cli/src/shorebird_validator.dart'; import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; @@ -15,7 +15,7 @@ import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; /// {@endtemplate} class DeleteCollaboratorsCommand extends ShorebirdCommand { /// {@macro delete_collaborators_command} - DeleteCollaboratorsCommand({super.buildCodePushClient}) { + DeleteCollaboratorsCommand() { argParser ..addOption( _appIdOption, @@ -47,13 +47,8 @@ class DeleteCollaboratorsCommand extends ShorebirdCommand { return e.exitCode.code; } - final client = buildCodePushClient( - httpClient: auth.client, - hostedUri: ShorebirdEnvironment.hostedUri, - ); - final appId = results[_appIdOption] as String? ?? - ShorebirdEnvironment.getShorebirdYaml()?.appId; + shorebirdEnv.getShorebirdYaml()?.appId; if (appId == null) { logger.err( ''' @@ -71,7 +66,8 @@ You must either specify an app id via the "--$_appIdOption" flag or run this com final getCollaboratorsProgress = logger.progress('Fetching collaborators'); final List collaborators; try { - collaborators = await client.getCollaborators(appId: appId); + collaborators = await codePushClientWrapper.codePushClient + .getCollaborators(appId: appId); getCollaboratorsProgress.complete(); } catch (error) { getCollaboratorsProgress.fail(); @@ -109,7 +105,7 @@ ${styleBold.wrap(lightGreen.wrap('šŸ—‘ļø Ready to delete an existing collabora final progress = logger.progress('Deleting collaborator'); try { - await client.deleteCollaborator( + await codePushClientWrapper.codePushClient.deleteCollaborator( appId: appId, userId: collaborator.userId, ); diff --git a/packages/shorebird_cli/lib/src/commands/collaborators/list_collaborators_command.dart b/packages/shorebird_cli/lib/src/commands/collaborators/list_collaborators_command.dart index db391a90..23e1a073 100644 --- a/packages/shorebird_cli/lib/src/commands/collaborators/list_collaborators_command.dart +++ b/packages/shorebird_cli/lib/src/commands/collaborators/list_collaborators_command.dart @@ -2,10 +2,10 @@ import 'dart:async'; import 'package:barbecue/barbecue.dart'; import 'package:mason_logger/mason_logger.dart'; -import 'package:shorebird_cli/src/auth/auth.dart'; +import 'package:shorebird_cli/src/code_push_client_wrapper.dart'; import 'package:shorebird_cli/src/command.dart'; import 'package:shorebird_cli/src/logger.dart'; -import 'package:shorebird_cli/src/shorebird_environment.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; import 'package:shorebird_cli/src/shorebird_validator.dart'; import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; @@ -15,7 +15,7 @@ import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; /// {@endtemplate} class ListCollaboratorsCommand extends ShorebirdCommand { /// {@macro list_collaborators_command} - ListCollaboratorsCommand({super.buildCodePushClient}) { + ListCollaboratorsCommand() { argParser.addOption( _appIdOption, help: 'The app id to list collaborators for.', @@ -43,13 +43,8 @@ class ListCollaboratorsCommand extends ShorebirdCommand { return e.exitCode.code; } - final client = buildCodePushClient( - httpClient: auth.client, - hostedUri: ShorebirdEnvironment.hostedUri, - ); - final appId = results[_appIdOption] as String? ?? - ShorebirdEnvironment.getShorebirdYaml()?.appId; + shorebirdEnv.getShorebirdYaml()?.appId; if (appId == null) { logger.err( ''' @@ -61,7 +56,8 @@ You must either specify an app id via the "--$_appIdOption" flag or run this com final List collaborators; try { - collaborators = await client.getCollaborators(appId: appId); + collaborators = await codePushClientWrapper.codePushClient + .getCollaborators(appId: appId); } catch (error) { logger.err('$error'); return ExitCode.software.code; diff --git a/packages/shorebird_cli/lib/src/commands/doctor_command.dart b/packages/shorebird_cli/lib/src/commands/doctor_command.dart index 7aaa4e8d..432b371e 100644 --- a/packages/shorebird_cli/lib/src/commands/doctor_command.dart +++ b/packages/shorebird_cli/lib/src/commands/doctor_command.dart @@ -2,7 +2,7 @@ import 'package:mason_logger/mason_logger.dart'; import 'package:shorebird_cli/src/command.dart'; import 'package:shorebird_cli/src/doctor.dart'; import 'package:shorebird_cli/src/logger.dart'; -import 'package:shorebird_cli/src/shorebird_environment.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; import 'package:shorebird_cli/src/version.dart'; /// {@template doctor_command} @@ -34,7 +34,7 @@ class DoctorCommand extends ShorebirdCommand { logger.info(''' Shorebird v$packageVersion -Shorebird Engine • revision ${ShorebirdEnvironment.shorebirdEngineRevision} +Shorebird Engine • revision ${shorebirdEnv.shorebirdEngineRevision} '''); await doctor.runValidators(doctor.allValidators, applyFixes: shouldFix); diff --git a/packages/shorebird_cli/lib/src/commands/init_command.dart b/packages/shorebird_cli/lib/src/commands/init_command.dart index 09bebe5e..3cbf7b3f 100644 --- a/packages/shorebird_cli/lib/src/commands/init_command.dart +++ b/packages/shorebird_cli/lib/src/commands/init_command.dart @@ -3,26 +3,27 @@ import 'dart:io'; import 'package:collection/collection.dart'; import 'package:mason_logger/mason_logger.dart'; import 'package:path/path.dart' as p; +import 'package:shorebird_cli/src/code_push_client_wrapper.dart'; import 'package:shorebird_cli/src/command.dart'; +import 'package:shorebird_cli/src/config/config.dart'; import 'package:shorebird_cli/src/doctor.dart'; import 'package:shorebird_cli/src/gradlew.dart'; import 'package:shorebird_cli/src/logger.dart'; import 'package:shorebird_cli/src/platform.dart'; -import 'package:shorebird_cli/src/shorebird_config_mixin.dart'; -import 'package:shorebird_cli/src/shorebird_create_app_mixin.dart'; -import 'package:shorebird_cli/src/shorebird_environment.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; import 'package:shorebird_cli/src/shorebird_validator.dart'; import 'package:shorebird_cli/src/xcodebuild.dart'; +import 'package:yaml/yaml.dart'; +import 'package:yaml_edit/yaml_edit.dart'; /// {@template init_command} /// /// `shorebird init` /// Initialize Shorebird. /// {@endtemplate} -class InitCommand extends ShorebirdCommand - with ShorebirdConfigMixin, ShorebirdCreateAppMixin { +class InitCommand extends ShorebirdCommand { /// {@macro init_command} - InitCommand({super.buildCodePushClient}) { + InitCommand() { argParser.addFlag( 'force', abbr: 'f', @@ -48,7 +49,7 @@ class InitCommand extends ShorebirdCommand } try { - if (!ShorebirdEnvironment.hasPubspecYaml) { + if (!shorebirdEnv.hasPubspecYaml) { logger.err(''' Could not find a "pubspec.yaml". Please make sure you are running "shorebird init" from the root of your Flutter project. @@ -61,11 +62,8 @@ Please make sure you are running "shorebird init" from the root of your Flutter } final force = results['force'] == true; - if (force && ShorebirdEnvironment.hasShorebirdYaml) { - ShorebirdEnvironment.getShorebirdYamlFile().deleteSync(); - } - if (ShorebirdEnvironment.hasShorebirdYaml) { + if (!force && shorebirdEnv.hasShorebirdYaml) { logger.err(''' A "shorebird.yaml" already exists. If you want to reinitialize Shorebird, please run "shorebird init --force".'''); @@ -99,7 +97,7 @@ If you want to reinitialize Shorebird, please run "shorebird init --force".'''); try { final displayName = logger.prompt( '${lightGreen.wrap('?')} How should we refer to this app?', - defaultValue: ShorebirdEnvironment.getPubspecYaml()?.name, + defaultValue: shorebirdEnv.getPubspecYaml()?.name, ); final hasNoFlavors = productFlavors.isEmpty; final hasSomeFlavors = productFlavors.isNotEmpty && @@ -109,15 +107,19 @@ If you want to reinitialize Shorebird, please run "shorebird init --force".'''); if (hasNoFlavors) { // No platforms have any flavors so we just create a single app // and assign it as the default. - appId = (await createApp(appName: displayName)).id; + final app = await codePushClientWrapper.createApp(appName: displayName); + appId = app.id; } else if (hasSomeFlavors) { // Some platforms have flavors and some do not so we create an app // for the default (no flavor) and then create an app per flavor. - appId = (await createApp(appName: displayName)).id; + final app = await codePushClientWrapper.createApp(appName: displayName); + appId = app.id; final values = {}; for (final flavor in productFlavors) { - values[flavor] = - (await createApp(appName: '$displayName ($flavor)')).id; + final app = await codePushClientWrapper.createApp( + appName: '$displayName ($flavor)', + ); + values[flavor] = app.id; } flavors = values; } else { @@ -125,8 +127,10 @@ If you want to reinitialize Shorebird, please run "shorebird init --force".'''); // and assign the default to the first flavor. final values = {}; for (final flavor in productFlavors) { - values[flavor] = - (await createApp(appName: '$displayName ($flavor)')).id; + final app = await codePushClientWrapper.createApp( + appName: '$displayName ($flavor)', + ); + values[flavor] = app.id; } flavors = values; appId = flavors.values.first; @@ -136,10 +140,10 @@ If you want to reinitialize Shorebird, please run "shorebird init --force".'''); return ExitCode.software.code; } - addShorebirdYamlToProject(appId, flavors: flavors); + _addShorebirdYamlToProject(appId, flavors: flavors); - if (!ShorebirdEnvironment.pubspecContainsShorebirdYaml) { - addShorebirdYamlToPubspecAssets(); + if (!shorebirdEnv.pubspecContainsShorebirdYaml) { + _addShorebirdYamlToPubspecAssets(); } await doctor.runValidators(doctor.allValidators, applyFixes: true); @@ -207,4 +211,56 @@ For more information about Shorebird, visit ${link(uri: Uri.parse('https://shore .toSet(); } } + + ShorebirdYaml _addShorebirdYamlToProject( + String appId, { + Map? flavors, + }) { + const content = ''' +# This file is used to configure the Shorebird updater used by your application. +# Learn more at https://shorebird.dev +# This file should be checked into version control. + +# This is the unique identifier assigned to your app. +# It is used by your app to request the correct patches from Shorebird servers. +app_id: +'''; + + final editor = YamlEditor(content)..update(['app_id'], appId); + + if (flavors != null) editor.update(['flavors'], flavors); + + shorebirdEnv.getShorebirdYamlFile().writeAsStringSync(editor.toString()); + + return ShorebirdYaml(appId: appId); + } + + void _addShorebirdYamlToPubspecAssets() { + final pubspecFile = shorebirdEnv.getPubspecYamlFile(); + final pubspecContents = pubspecFile.readAsStringSync(); + final yaml = loadYaml(pubspecContents, sourceUrl: pubspecFile.uri) as Map; + final editor = YamlEditor(pubspecContents); + + if (!yaml.containsKey('flutter')) { + editor.update( + ['flutter'], + { + 'assets': ['shorebird.yaml'] + }, + ); + } else { + if (!(yaml['flutter'] as Map).containsKey('assets')) { + editor.update(['flutter', 'assets'], ['shorebird.yaml']); + } else { + final assets = (yaml['flutter'] as Map)['assets'] as List; + if (!assets.contains('shorebird.yaml')) { + editor.update(['flutter', 'assets'], [...assets, 'shorebird.yaml']); + } + } + } + + if (editor.edits.isEmpty) return; + + pubspecFile.writeAsStringSync(editor.toString()); + } } diff --git a/packages/shorebird_cli/lib/src/commands/patch/patch_aar_command.dart b/packages/shorebird_cli/lib/src/commands/patch/patch_aar_command.dart index 95832c82..caed2082 100644 --- a/packages/shorebird_cli/lib/src/commands/patch/patch_aar_command.dart +++ b/packages/shorebird_cli/lib/src/commands/patch/patch_aar_command.dart @@ -15,9 +15,9 @@ import 'package:shorebird_cli/src/formatters/file_size_formatter.dart'; import 'package:shorebird_cli/src/logger.dart'; import 'package:shorebird_cli/src/shorebird_artifact_mixin.dart'; import 'package:shorebird_cli/src/shorebird_build_mixin.dart'; -import 'package:shorebird_cli/src/shorebird_config_mixin.dart'; -import 'package:shorebird_cli/src/shorebird_environment.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; import 'package:shorebird_cli/src/shorebird_validator.dart'; +import 'package:shorebird_cli/src/shorebird_version_manager.dart'; import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; /// {@template patch_aar_command} @@ -25,7 +25,7 @@ import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; /// Create a patch for an Android archive release. /// {@endtemplate} class PatchAarCommand extends ShorebirdCommand - with ShorebirdConfigMixin, ShorebirdBuildMixin, ShorebirdArtifactMixin { + with ShorebirdBuildMixin, ShorebirdArtifactMixin { /// {@macro patch_aar_command} PatchAarCommand({ HashFunction? hashFn, @@ -105,7 +105,7 @@ of the Android app that is using this module.''', await cache.updateAll(); - if (androidPackageName == null) { + if (shorebirdEnv.androidPackageName == null) { logger.err('Could not find androidPackage in pubspec.yaml.'); return ExitCode.config.code; } @@ -113,7 +113,7 @@ of the Android app that is using this module.''', final buildNumber = results['build-number'] as String; final releaseVersion = results['release-version'] as String; - final shorebirdYaml = ShorebirdEnvironment.getShorebirdYaml()!; + final shorebirdYaml = shorebirdEnv.getShorebirdYaml()!; final appId = shorebirdYaml.getAppId(); final app = await codePushClientWrapper.getApp(appId: appId); @@ -148,7 +148,8 @@ Please re-run the release command for this version or create a new release.'''); ); final String shorebirdFlutterRevision; try { - shorebirdFlutterRevision = await getShorebirdFlutterRevision(); + shorebirdFlutterRevision = + await shorebirdVersionManager.fetchCurrentGitHash(); flutterRevisionProgress.complete(); } catch (error) { flutterRevisionProgress.fail('$error'); @@ -171,7 +172,7 @@ Either create a new release using: ${lightCyan.wrap('shorebird release aar')} Or downgrade your Flutter version and try again using: - ${lightCyan.wrap('cd ${ShorebirdEnvironment.flutterDirectory.path}')} + ${lightCyan.wrap('cd ${shorebirdEnv.flutterDirectory.path}')} ${lightCyan.wrap('git checkout ${release.flutterRevision}')} Shorebird plans to support this automatically, let us know if it's important to you: @@ -217,7 +218,7 @@ https://github.com/shorebirdtech/shorebird/issues/472 final contentDiffs = _aarDiffer.changedFiles( releaseAarPath, aarArtifactPath( - packageName: androidPackageName!, + packageName: shorebirdEnv.androidPackageName!, buildNumber: buildNumber, ), ); @@ -247,7 +248,7 @@ https://github.com/shorebirdtech/shorebird/issues/472 } final extractedAarDir = await extractAar( - packageName: androidPackageName!, + packageName: shorebirdEnv.androidPackageName!, buildNumber: buildNumber, unzipFn: _unzipFn, ); diff --git a/packages/shorebird_cli/lib/src/commands/patch/patch_android_command.dart b/packages/shorebird_cli/lib/src/commands/patch/patch_android_command.dart index 35fda9f3..49a9fd79 100644 --- a/packages/shorebird_cli/lib/src/commands/patch/patch_android_command.dart +++ b/packages/shorebird_cli/lib/src/commands/patch/patch_android_command.dart @@ -14,10 +14,10 @@ import 'package:shorebird_cli/src/doctor.dart'; import 'package:shorebird_cli/src/formatters/formatters.dart'; import 'package:shorebird_cli/src/logger.dart'; import 'package:shorebird_cli/src/shorebird_build_mixin.dart'; -import 'package:shorebird_cli/src/shorebird_config_mixin.dart'; -import 'package:shorebird_cli/src/shorebird_environment.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; import 'package:shorebird_cli/src/shorebird_release_version_mixin.dart'; import 'package:shorebird_cli/src/shorebird_validator.dart'; +import 'package:shorebird_cli/src/shorebird_version_manager.dart'; import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; /// {@template patch_android_command} @@ -26,10 +26,7 @@ import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; /// push server. /// {@endtemplate} class PatchAndroidCommand extends ShorebirdCommand - with - ShorebirdConfigMixin, - ShorebirdBuildMixin, - ShorebirdReleaseVersionMixin { + with ShorebirdBuildMixin, ShorebirdReleaseVersionMixin { /// {@macro patch_android_command} PatchAndroidCommand({ HashFunction? hashFn, @@ -112,7 +109,7 @@ class PatchAndroidCommand extends ShorebirdCommand return ExitCode.software.code; } - final shorebirdYaml = ShorebirdEnvironment.getShorebirdYaml()!; + final shorebirdYaml = shorebirdEnv.getShorebirdYaml()!; final appId = shorebirdYaml.getAppId(flavor: flavor); final app = await codePushClientWrapper.getApp(appId: appId); @@ -156,7 +153,8 @@ Please re-run the release command for this version or create a new release.'''); ); final String shorebirdFlutterRevision; try { - shorebirdFlutterRevision = await getShorebirdFlutterRevision(); + shorebirdFlutterRevision = + await shorebirdVersionManager.fetchCurrentGitHash(); flutterRevisionProgress.complete(); } catch (error) { flutterRevisionProgress.fail('$error'); @@ -179,7 +177,7 @@ Either create a new release using: ${lightCyan.wrap('shorebird release')} Or downgrade your Flutter version and try again using: - ${lightCyan.wrap('cd ${ShorebirdEnvironment.flutterDirectory.path}')} + ${lightCyan.wrap('cd ${shorebirdEnv.flutterDirectory.path}')} ${lightCyan.wrap('git checkout ${release.flutterRevision}')} Shorebird plans to support this automatically, let us know if it's important to you: diff --git a/packages/shorebird_cli/lib/src/commands/patch/patch_ios_command.dart b/packages/shorebird_cli/lib/src/commands/patch/patch_ios_command.dart index a011d31c..80639029 100644 --- a/packages/shorebird_cli/lib/src/commands/patch/patch_ios_command.dart +++ b/packages/shorebird_cli/lib/src/commands/patch/patch_ios_command.dart @@ -14,16 +14,16 @@ import 'package:shorebird_cli/src/ios.dart'; import 'package:shorebird_cli/src/logger.dart'; import 'package:shorebird_cli/src/shorebird_artifact_mixin.dart'; import 'package:shorebird_cli/src/shorebird_build_mixin.dart'; -import 'package:shorebird_cli/src/shorebird_config_mixin.dart'; -import 'package:shorebird_cli/src/shorebird_environment.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; import 'package:shorebird_cli/src/shorebird_validator.dart'; +import 'package:shorebird_cli/src/shorebird_version_manager.dart'; import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; /// {@template patch_ios_command} /// `shorebird patch ios-alpha` command. /// {@endtemplate} class PatchIosCommand extends ShorebirdCommand - with ShorebirdConfigMixin, ShorebirdBuildMixin, ShorebirdArtifactMixin { + with ShorebirdBuildMixin, ShorebirdArtifactMixin { /// {@macro patch_ios_command} PatchIosCommand({ HashFunction? hashFn, @@ -92,7 +92,7 @@ class PatchIosCommand extends ShorebirdCommand return ExitCode.usage.code; } - final shorebirdYaml = ShorebirdEnvironment.getShorebirdYaml()!; + final shorebirdYaml = shorebirdEnv.getShorebirdYaml()!; final appId = shorebirdYaml.getAppId(flavor: flavor); final app = await codePushClientWrapper.getApp(appId: appId); @@ -158,7 +158,8 @@ Please re-run the release command for this version or create a new release.'''); ); final String shorebirdFlutterRevision; try { - shorebirdFlutterRevision = await getShorebirdFlutterRevision(); + shorebirdFlutterRevision = + await shorebirdVersionManager.fetchCurrentGitHash(); flutterRevisionProgress.complete(); } catch (error) { flutterRevisionProgress.fail('$error'); @@ -181,7 +182,7 @@ Either create a new release using: ${lightCyan.wrap('shorebird release')} Or downgrade your Flutter version and try again using: - ${lightCyan.wrap('cd ${ShorebirdEnvironment.flutterDirectory.path}')} + ${lightCyan.wrap('cd ${shorebirdEnv.flutterDirectory.path}')} ${lightCyan.wrap('git checkout ${release.flutterRevision}')} Shorebird plans to support this automatically, let us know if it's important to you: diff --git a/packages/shorebird_cli/lib/src/commands/patch/patch_ios_framework_command.dart b/packages/shorebird_cli/lib/src/commands/patch/patch_ios_framework_command.dart index 4d5507cf..94a8d84b 100644 --- a/packages/shorebird_cli/lib/src/commands/patch/patch_ios_framework_command.dart +++ b/packages/shorebird_cli/lib/src/commands/patch/patch_ios_framework_command.dart @@ -12,13 +12,13 @@ import 'package:shorebird_cli/src/ios.dart'; import 'package:shorebird_cli/src/logger.dart'; import 'package:shorebird_cli/src/shorebird_artifact_mixin.dart'; import 'package:shorebird_cli/src/shorebird_build_mixin.dart'; -import 'package:shorebird_cli/src/shorebird_config_mixin.dart'; -import 'package:shorebird_cli/src/shorebird_environment.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; import 'package:shorebird_cli/src/shorebird_validator.dart'; +import 'package:shorebird_cli/src/shorebird_version_manager.dart'; import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; class PatchIosFrameworkCommand extends ShorebirdCommand - with ShorebirdConfigMixin, ShorebirdBuildMixin, ShorebirdArtifactMixin { + with ShorebirdBuildMixin, ShorebirdArtifactMixin { PatchIosFrameworkCommand({ HashFunction? hashFn, }) : _hashFn = hashFn ?? ((m) => sha256.convert(m).toString()) { @@ -73,7 +73,7 @@ of the iOS app that is using this module.''', const releasePlatform = ReleasePlatform.ios; final releaseVersion = results['release-version'] as String; final dryRun = results['dry-run'] == true; - final shorebirdYaml = ShorebirdEnvironment.getShorebirdYaml()!; + final shorebirdYaml = shorebirdEnv.getShorebirdYaml()!; final appId = shorebirdYaml.getAppId(); final app = await codePushClientWrapper.getApp(appId: appId); @@ -115,7 +115,8 @@ Please re-run the release command for this version or create a new release.'''); ); final String shorebirdFlutterRevision; try { - shorebirdFlutterRevision = await getShorebirdFlutterRevision(); + shorebirdFlutterRevision = + await shorebirdVersionManager.fetchCurrentGitHash(); flutterRevisionProgress.complete(); } catch (error) { flutterRevisionProgress.fail('$error'); @@ -138,7 +139,7 @@ Either create a new release using: ${lightCyan.wrap('shorebird release')} Or downgrade your Flutter version and try again using: - ${lightCyan.wrap('cd ${ShorebirdEnvironment.flutterDirectory.path}')} + ${lightCyan.wrap('cd ${shorebirdEnv.flutterDirectory.path}')} ${lightCyan.wrap('git checkout ${release.flutterRevision}')} Shorebird plans to support this automatically, let us know if it's important to you: diff --git a/packages/shorebird_cli/lib/src/commands/release/release_aar_command.dart b/packages/shorebird_cli/lib/src/commands/release/release_aar_command.dart index 0e76c02c..dde6c8b4 100644 --- a/packages/shorebird_cli/lib/src/commands/release/release_aar_command.dart +++ b/packages/shorebird_cli/lib/src/commands/release/release_aar_command.dart @@ -9,10 +9,10 @@ import 'package:shorebird_cli/src/config/config.dart'; import 'package:shorebird_cli/src/logger.dart'; import 'package:shorebird_cli/src/shorebird_artifact_mixin.dart'; import 'package:shorebird_cli/src/shorebird_build_mixin.dart'; -import 'package:shorebird_cli/src/shorebird_config_mixin.dart'; -import 'package:shorebird_cli/src/shorebird_environment.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; import 'package:shorebird_cli/src/shorebird_release_version_mixin.dart'; import 'package:shorebird_cli/src/shorebird_validator.dart'; +import 'package:shorebird_cli/src/shorebird_version_manager.dart'; import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; /// {@template release_aar_command} @@ -21,7 +21,6 @@ import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; /// {@endtemplate} class ReleaseAarCommand extends ShorebirdCommand with - ShorebirdConfigMixin, ShorebirdBuildMixin, ShorebirdReleaseVersionMixin, ShorebirdArtifactMixin { @@ -75,7 +74,7 @@ make smaller updates to your app. return e.exitCode.code; } - if (androidPackageName == null) { + if (shorebirdEnv.androidPackageName == null) { logger.err('Could not find androidPackage in pubspec.yaml.'); return ExitCode.config.code; } @@ -85,7 +84,7 @@ make smaller updates to your app. final releaseVersion = results['release-version'] as String; final buildProgress = logger.progress('Building aar'); - final shorebirdYaml = ShorebirdEnvironment.getShorebirdYaml()!; + final shorebirdYaml = shorebirdEnv.getShorebirdYaml()!; final appId = shorebirdYaml.getAppId(); final app = await codePushClientWrapper.getApp(appId: appId); @@ -141,7 +140,8 @@ ${summary.join('\n')} ); final String shorebirdFlutterRevision; try { - shorebirdFlutterRevision = await getShorebirdFlutterRevision(); + shorebirdFlutterRevision = + await shorebirdVersionManager.fetchCurrentGitHash(); flutterRevisionProgress.complete(); } catch (error) { flutterRevisionProgress.fail('$error'); @@ -168,7 +168,7 @@ ${summary.join('\n')} final extractAarProgress = logger.progress('Creating artifacts'); final extractedAarDir = await extractAar( - packageName: androidPackageName!, + packageName: shorebirdEnv.androidPackageName!, buildNumber: buildNumber, unzipFn: _unzipFn, ); @@ -179,7 +179,7 @@ ${summary.join('\n')} releaseId: release.id, platform: platform, aarPath: aarArtifactPath( - packageName: androidPackageName!, + packageName: shorebirdEnv.androidPackageName!, buildNumber: buildNumber, ), extractedAarDir: extractedAarDir, @@ -201,7 +201,7 @@ Your next step is to add this module as a dependency in your app's build.gradle: ${lightCyan.wrap(''' dependencies { // ... - releaseImplementation '$androidPackageName:flutter_release:$buildNumber' + releaseImplementation '${shorebirdEnv.androidPackageName}:flutter_release:$buildNumber' // ... }''')} '''); diff --git a/packages/shorebird_cli/lib/src/commands/release/release_android_command.dart b/packages/shorebird_cli/lib/src/commands/release/release_android_command.dart index f7ce211f..d337a81e 100644 --- a/packages/shorebird_cli/lib/src/commands/release/release_android_command.dart +++ b/packages/shorebird_cli/lib/src/commands/release/release_android_command.dart @@ -8,10 +8,10 @@ import 'package:shorebird_cli/src/config/shorebird_yaml.dart'; import 'package:shorebird_cli/src/doctor.dart'; import 'package:shorebird_cli/src/logger.dart'; import 'package:shorebird_cli/src/shorebird_build_mixin.dart'; -import 'package:shorebird_cli/src/shorebird_config_mixin.dart'; -import 'package:shorebird_cli/src/shorebird_environment.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; import 'package:shorebird_cli/src/shorebird_release_version_mixin.dart'; import 'package:shorebird_cli/src/shorebird_validator.dart'; +import 'package:shorebird_cli/src/shorebird_version_manager.dart'; import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; /// {@template release_android_command} @@ -19,10 +19,7 @@ import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; /// Create new app releases for Android. /// {@endtemplate} class ReleaseAndroidCommand extends ShorebirdCommand - with - ShorebirdConfigMixin, - ShorebirdBuildMixin, - ShorebirdReleaseVersionMixin { + with ShorebirdBuildMixin, ShorebirdReleaseVersionMixin { /// {@macro release_android_command} ReleaseAndroidCommand() { argParser @@ -89,8 +86,7 @@ make smaller updates to your app. return ExitCode.software.code; } - final shorebirdYaml = ShorebirdEnvironment.getShorebirdYaml()!; - + final shorebirdYaml = shorebirdEnv.getShorebirdYaml()!; final appId = shorebirdYaml.getAppId(flavor: flavor); final app = await codePushClientWrapper.getApp(appId: appId); @@ -158,7 +154,8 @@ ${summary.join('\n')} ); final String shorebirdFlutterRevision; try { - shorebirdFlutterRevision = await getShorebirdFlutterRevision(); + shorebirdFlutterRevision = + await shorebirdVersionManager.fetchCurrentGitHash(); flutterRevisionProgress.complete(); } catch (error) { flutterRevisionProgress.fail('$error'); diff --git a/packages/shorebird_cli/lib/src/commands/release/release_ios_command.dart b/packages/shorebird_cli/lib/src/commands/release/release_ios_command.dart index 86f2027e..38d1472f 100644 --- a/packages/shorebird_cli/lib/src/commands/release/release_ios_command.dart +++ b/packages/shorebird_cli/lib/src/commands/release/release_ios_command.dart @@ -12,9 +12,9 @@ import 'package:shorebird_cli/src/ios.dart'; import 'package:shorebird_cli/src/logger.dart'; import 'package:shorebird_cli/src/shorebird_artifact_mixin.dart'; import 'package:shorebird_cli/src/shorebird_build_mixin.dart'; -import 'package:shorebird_cli/src/shorebird_config_mixin.dart'; -import 'package:shorebird_cli/src/shorebird_environment.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; import 'package:shorebird_cli/src/shorebird_validator.dart'; +import 'package:shorebird_cli/src/shorebird_version_manager.dart'; import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; /// {@template release_ios_command} @@ -22,7 +22,7 @@ import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; /// Create new app releases for iOS. /// {@endtemplate} class ReleaseIosCommand extends ShorebirdCommand - with ShorebirdBuildMixin, ShorebirdConfigMixin, ShorebirdArtifactMixin { + with ShorebirdBuildMixin, ShorebirdArtifactMixin { /// {@macro release_ios_command} ReleaseIosCommand({ IpaReader? ipaReader, @@ -75,7 +75,7 @@ make smaller updates to your app. const releasePlatform = ReleasePlatform.ios; final flavor = results['flavor'] as String?; final target = results['target'] as String?; - final shorebirdYaml = ShorebirdEnvironment.getShorebirdYaml()!; + final shorebirdYaml = shorebirdEnv.getShorebirdYaml()!; final appId = shorebirdYaml.getAppId(flavor: flavor); final app = await codePushClientWrapper.getApp(appId: appId); @@ -165,7 +165,8 @@ ${summary.join('\n')} ); final String shorebirdFlutterRevision; try { - shorebirdFlutterRevision = await getShorebirdFlutterRevision(); + shorebirdFlutterRevision = + await shorebirdVersionManager.fetchCurrentGitHash(); flutterRevisionProgress.complete(); } catch (error) { flutterRevisionProgress.fail('$error'); diff --git a/packages/shorebird_cli/lib/src/commands/release/release_ios_framework_command.dart b/packages/shorebird_cli/lib/src/commands/release/release_ios_framework_command.dart index c6a414f0..32baa342 100644 --- a/packages/shorebird_cli/lib/src/commands/release/release_ios_framework_command.dart +++ b/packages/shorebird_cli/lib/src/commands/release/release_ios_framework_command.dart @@ -10,13 +10,13 @@ import 'package:shorebird_cli/src/doctor.dart'; import 'package:shorebird_cli/src/ios.dart'; import 'package:shorebird_cli/src/logger.dart'; import 'package:shorebird_cli/src/shorebird_build_mixin.dart'; -import 'package:shorebird_cli/src/shorebird_config_mixin.dart'; -import 'package:shorebird_cli/src/shorebird_environment.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; import 'package:shorebird_cli/src/shorebird_validator.dart'; +import 'package:shorebird_cli/src/shorebird_version_manager.dart'; import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; class ReleaseIosFrameworkCommand extends ShorebirdCommand - with ShorebirdConfigMixin, ShorebirdBuildMixin { + with ShorebirdBuildMixin { ReleaseIosFrameworkCommand() { argParser ..addOption( @@ -58,7 +58,7 @@ of the iOS app that is using this module.''', const releasePlatform = ReleasePlatform.ios; final releaseVersion = results['release-version'] as String; - final shorebirdYaml = ShorebirdEnvironment.getShorebirdYaml()!; + final shorebirdYaml = shorebirdEnv.getShorebirdYaml()!; final appId = shorebirdYaml.getAppId(); final app = await codePushClientWrapper.getApp(appId: appId); @@ -113,7 +113,8 @@ ${summary.join('\n')} ); final String shorebirdFlutterRevision; try { - shorebirdFlutterRevision = await getShorebirdFlutterRevision(); + shorebirdFlutterRevision = + await shorebirdVersionManager.fetchCurrentGitHash(); flutterRevisionProgress.complete(); } catch (error) { flutterRevisionProgress.fail('$error'); diff --git a/packages/shorebird_cli/lib/src/commands/releases/delete_releases_command.dart b/packages/shorebird_cli/lib/src/commands/releases/delete_releases_command.dart index f66f91b0..164919c3 100644 --- a/packages/shorebird_cli/lib/src/commands/releases/delete_releases_command.dart +++ b/packages/shorebird_cli/lib/src/commands/releases/delete_releases_command.dart @@ -2,11 +2,11 @@ import 'dart:async'; import 'package:collection/collection.dart'; import 'package:mason_logger/mason_logger.dart'; -import 'package:shorebird_cli/src/auth/auth.dart'; +import 'package:shorebird_cli/src/code_push_client_wrapper.dart'; import 'package:shorebird_cli/src/command.dart'; import 'package:shorebird_cli/src/config/shorebird_yaml.dart'; import 'package:shorebird_cli/src/logger.dart'; -import 'package:shorebird_cli/src/shorebird_environment.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; import 'package:shorebird_cli/src/shorebird_validator.dart'; import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; @@ -17,7 +17,7 @@ import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; /// {@endtemplate} class DeleteReleasesCommand extends ShorebirdCommand { /// {@macro delete_releases_command} - DeleteReleasesCommand({super.buildCodePushClient}) { + DeleteReleasesCommand() { argParser ..addOption( 'version', @@ -47,18 +47,14 @@ class DeleteReleasesCommand extends ShorebirdCommand { } final flavor = results['flavor'] as String?; - final appId = - ShorebirdEnvironment.getShorebirdYaml()!.getAppId(flavor: flavor); - - final codePushClient = buildCodePushClient( - httpClient: auth.client, - hostedUri: ShorebirdEnvironment.hostedUri, - ); + final appId = shorebirdEnv.getShorebirdYaml()!.getAppId(flavor: flavor); final List releases; var progress = logger.progress('Fetching releases'); try { - releases = await codePushClient.getReleases(appId: appId); + releases = await codePushClientWrapper.codePushClient.getReleases( + appId: appId, + ); progress.complete('Fetched releases.'); } catch (error) { progress.fail('$error'); @@ -89,7 +85,7 @@ class DeleteReleasesCommand extends ShorebirdCommand { progress = logger.progress('Deleting release'); try { - await codePushClient.deleteRelease( + await codePushClientWrapper.codePushClient.deleteRelease( appId: appId, releaseId: releaseToDelete.id, ); diff --git a/packages/shorebird_cli/lib/src/commands/releases/list_releases_command.dart b/packages/shorebird_cli/lib/src/commands/releases/list_releases_command.dart index a0084fc8..9cdf2cae 100644 --- a/packages/shorebird_cli/lib/src/commands/releases/list_releases_command.dart +++ b/packages/shorebird_cli/lib/src/commands/releases/list_releases_command.dart @@ -1,10 +1,10 @@ import 'package:barbecue/barbecue.dart'; import 'package:mason_logger/mason_logger.dart'; -import 'package:shorebird_cli/src/auth/auth.dart'; +import 'package:shorebird_cli/src/code_push_client_wrapper.dart'; import 'package:shorebird_cli/src/command.dart'; import 'package:shorebird_cli/src/config/shorebird_yaml.dart'; import 'package:shorebird_cli/src/logger.dart'; -import 'package:shorebird_cli/src/shorebird_environment.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; import 'package:shorebird_cli/src/shorebird_validator.dart'; import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; @@ -15,7 +15,7 @@ import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; /// {@endtemplate} class ListReleasesCommand extends ShorebirdCommand { /// {@macro list_releases_command} - ListReleasesCommand({super.buildCodePushClient}) { + ListReleasesCommand() { argParser.addOption( 'flavor', help: 'The product flavor to use when listing releases.', @@ -47,17 +47,13 @@ Please use $consoleLink instead.''', } final flavor = results['flavor'] as String?; - final appId = - ShorebirdEnvironment.getShorebirdYaml()!.getAppId(flavor: flavor); - - final codePushClient = buildCodePushClient( - httpClient: auth.client, - hostedUri: ShorebirdEnvironment.hostedUri, - ); + final appId = shorebirdEnv.getShorebirdYaml()!.getAppId(flavor: flavor); final List releases; try { - releases = await codePushClient.getReleases(appId: appId); + releases = await codePushClientWrapper.codePushClient.getReleases( + appId: appId, + ); } catch (error) { logger.err('$error'); return ExitCode.software.code; diff --git a/packages/shorebird_cli/lib/src/commands/run_command.dart b/packages/shorebird_cli/lib/src/commands/run_command.dart index fb6df6d2..1f50c9fc 100644 --- a/packages/shorebird_cli/lib/src/commands/run_command.dart +++ b/packages/shorebird_cli/lib/src/commands/run_command.dart @@ -14,7 +14,7 @@ import 'package:shorebird_cli/src/shorebird_validator.dart'; /// {@endtemplate} class RunCommand extends ShorebirdCommand { /// {@macro run_command} - RunCommand({super.buildCodePushClient}) { + RunCommand() { argParser ..addOption( 'device-id', diff --git a/packages/shorebird_cli/lib/src/commands/upgrade_command.dart b/packages/shorebird_cli/lib/src/commands/upgrade_command.dart index 5b5ecdbe..f9ce125a 100644 --- a/packages/shorebird_cli/lib/src/commands/upgrade_command.dart +++ b/packages/shorebird_cli/lib/src/commands/upgrade_command.dart @@ -4,7 +4,7 @@ import 'package:mason_logger/mason_logger.dart'; import 'package:shorebird_cli/src/command.dart'; import 'package:shorebird_cli/src/logger.dart'; import 'package:shorebird_cli/src/process.dart'; -import 'package:shorebird_cli/src/shorebird_environment.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; import 'package:shorebird_cli/src/shorebird_version_manager.dart'; /// {@template upgrade_command} @@ -91,7 +91,7 @@ class UpgradeCommand extends ShorebirdCommand { final result = await process.run( executable, args, - workingDirectory: ShorebirdEnvironment.flutterDirectory.path, + workingDirectory: shorebirdEnv.flutterDirectory.path, ); if (result.exitCode != 0) { diff --git a/packages/shorebird_cli/lib/src/ios_deploy.dart b/packages/shorebird_cli/lib/src/ios_deploy.dart index 7c60d7af..f071beab 100644 --- a/packages/shorebird_cli/lib/src/ios_deploy.dart +++ b/packages/shorebird_cli/lib/src/ios_deploy.dart @@ -8,7 +8,7 @@ import 'package:path/path.dart' as p; import 'package:scoped/scoped.dart'; import 'package:shorebird_cli/src/logger.dart'; import 'package:shorebird_cli/src/process.dart'; -import 'package:shorebird_cli/src/shorebird_environment.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; /// A reference to a [IOSDeploy] instance. final iosDeployRef = create(IOSDeploy.new); @@ -33,7 +33,7 @@ class IOSDeploy { @visibleForTesting static File get iosDeployExecutable => File( p.join( - ShorebirdEnvironment.flutterDirectory.path, + shorebirdEnv.flutterDirectory.path, 'bin', 'cache', 'artifacts', diff --git a/packages/shorebird_cli/lib/src/process.dart b/packages/shorebird_cli/lib/src/process.dart index ef27d428..c896bd76 100644 --- a/packages/shorebird_cli/lib/src/process.dart +++ b/packages/shorebird_cli/lib/src/process.dart @@ -3,7 +3,7 @@ import 'dart:io'; import 'package:mason_logger/mason_logger.dart'; import 'package:meta/meta.dart'; import 'package:scoped/scoped.dart'; -import 'package:shorebird_cli/src/shorebird_environment.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; // A reference to a [EngineConfig] instance. final engineConfigRef = create(() => const EngineConfig.empty()); @@ -111,10 +111,7 @@ class ShorebirdProcess { } String _resolveExecutable(String executable) { - if (executable == 'flutter') { - return ShorebirdEnvironment.flutterBinaryFile.path; - } - + if (executable == 'flutter') return shorebirdEnv.flutterBinaryFile.path; return executable; } diff --git a/packages/shorebird_cli/lib/src/shorebird_build_mixin.dart b/packages/shorebird_cli/lib/src/shorebird_build_mixin.dart index 30337e44..c250154f 100644 --- a/packages/shorebird_cli/lib/src/shorebird_build_mixin.dart +++ b/packages/shorebird_cli/lib/src/shorebird_build_mixin.dart @@ -6,7 +6,7 @@ import 'package:path/path.dart' as p; import 'package:shorebird_cli/src/cache.dart'; import 'package:shorebird_cli/src/command.dart'; import 'package:shorebird_cli/src/process.dart'; -import 'package:shorebird_cli/src/shorebird_environment.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; enum Arch { arm64, @@ -328,7 +328,7 @@ mixin ShorebirdBuildMixin on ShorebirdCommand { ]; final result = await process.run( - ShorebirdEnvironment.genSnapshotFile.path, + shorebirdEnv.genSnapshotFile.path, arguments, ); diff --git a/packages/shorebird_cli/lib/src/shorebird_config_mixin.dart b/packages/shorebird_cli/lib/src/shorebird_config_mixin.dart deleted file mode 100644 index 53842941..00000000 --- a/packages/shorebird_cli/lib/src/shorebird_config_mixin.dart +++ /dev/null @@ -1,84 +0,0 @@ -import 'dart:io'; - -import 'package:path/path.dart' as p; -import 'package:shorebird_cli/src/command.dart'; -import 'package:shorebird_cli/src/config/config.dart'; -import 'package:shorebird_cli/src/process.dart'; -import 'package:shorebird_cli/src/shorebird_environment.dart'; -import 'package:yaml/yaml.dart'; -import 'package:yaml_edit/yaml_edit.dart'; - -mixin ShorebirdConfigMixin on ShorebirdCommand { - /// Returns the Android package name from the pubspec.yaml file of a Flutter - /// module. - String? get androidPackageName { - final pubspec = ShorebirdEnvironment.getPubspecYaml(); - final module = pubspec?.flutter?['module'] as Map?; - return module?['androidPackage'] as String?; - } - - ShorebirdYaml addShorebirdYamlToProject( - String appId, { - Map? flavors, - }) { - const content = ''' -# This file is used to configure the Shorebird updater used by your application. -# Learn more at https://shorebird.dev -# This file should be checked into version control. - -# This is the unique identifier assigned to your app. -# It is used by your app to request the correct patches from Shorebird servers. -app_id: -'''; - - final editor = YamlEditor(content)..update(['app_id'], appId); - - if (flavors != null) editor.update(['flavors'], flavors); - - ShorebirdEnvironment.getShorebirdYamlFile() - .writeAsStringSync(editor.toString()); - - return ShorebirdYaml(appId: appId); - } - - void addShorebirdYamlToPubspecAssets() { - final pubspecFile = File(p.join(Directory.current.path, 'pubspec.yaml')); - final pubspecContents = pubspecFile.readAsStringSync(); - final yaml = loadYaml(pubspecContents, sourceUrl: pubspecFile.uri) as Map; - final editor = YamlEditor(pubspecContents); - - if (!yaml.containsKey('flutter')) { - editor.update( - ['flutter'], - { - 'assets': ['shorebird.yaml'] - }, - ); - } else { - if (!(yaml['flutter'] as Map).containsKey('assets')) { - editor.update(['flutter', 'assets'], ['shorebird.yaml']); - } else { - final assets = (yaml['flutter'] as Map)['assets'] as List; - if (!assets.contains('shorebird.yaml')) { - editor.update(['flutter', 'assets'], [...assets, 'shorebird.yaml']); - } - } - } - - if (editor.edits.isEmpty) return; - - pubspecFile.writeAsStringSync(editor.toString()); - } - - Future getShorebirdFlutterRevision() async { - final result = await process.run( - 'git', - ['rev-parse', 'HEAD'], - workingDirectory: ShorebirdEnvironment.flutterDirectory.path, - ); - if (result.exitCode != 0) { - throw Exception('Unable to determine flutter revision: ${result.stderr}'); - } - return (result.stdout as String).trim(); - } -} diff --git a/packages/shorebird_cli/lib/src/shorebird_create_app_mixin.dart b/packages/shorebird_cli/lib/src/shorebird_create_app_mixin.dart deleted file mode 100644 index 276f4031..00000000 --- a/packages/shorebird_cli/lib/src/shorebird_create_app_mixin.dart +++ /dev/null @@ -1,32 +0,0 @@ -import 'package:mason_logger/mason_logger.dart'; -import 'package:shorebird_cli/src/auth/auth.dart'; -import 'package:shorebird_cli/src/logger.dart'; -import 'package:shorebird_cli/src/shorebird_config_mixin.dart'; -import 'package:shorebird_cli/src/shorebird_environment.dart'; -import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; - -mixin ShorebirdCreateAppMixin on ShorebirdConfigMixin { - Future createApp({String? appName}) async { - late final String displayName; - if (appName == null) { - String? defaultAppName; - try { - defaultAppName = ShorebirdEnvironment.getPubspecYaml()?.name; - } catch (_) {} - - displayName = logger.prompt( - '${lightGreen.wrap('?')} How should we refer to this app?', - defaultValue: defaultAppName, - ); - } else { - displayName = appName; - } - - final client = buildCodePushClient( - httpClient: auth.client, - hostedUri: ShorebirdEnvironment.hostedUri, - ); - - return client.createApp(displayName: displayName); - } -} diff --git a/packages/shorebird_cli/lib/src/shorebird_env.dart b/packages/shorebird_cli/lib/src/shorebird_env.dart new file mode 100644 index 00000000..95511907 --- /dev/null +++ b/packages/shorebird_cli/lib/src/shorebird_env.dart @@ -0,0 +1,157 @@ +import 'dart:io' hide Platform; + +import 'package:checked_yaml/checked_yaml.dart'; +import 'package:path/path.dart' as p; +import 'package:pubspec_parse/pubspec_parse.dart'; +import 'package:scoped/scoped.dart'; +import 'package:shorebird_cli/src/config/shorebird_yaml.dart'; +import 'package:shorebird_cli/src/platform.dart'; +import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; +import 'package:yaml/yaml.dart'; + +/// A reference to a [ShorebirdEnv] instance. +final shorebirdEnvRef = create(ShorebirdEnv.new); + +/// The [ShorebirdEnv] instance available in the current zone. +ShorebirdEnv get shorebirdEnv => read(shorebirdEnvRef); + +/// {@template shorebird_env} +/// A class that provides access to shorebird environment metadata. +/// {@endtemplate} +class ShorebirdEnv { + /// {@macro shorebird_env} + const ShorebirdEnv(); + + /// The root directory of the Shorebird install. + /// + /// Assumes we are running from $ROOT/bin/cache. + Directory get shorebirdRoot { + return File(platform.script.toFilePath()).parent.parent.parent; + } + + String get shorebirdEngineRevision { + return File( + p.join(flutterDirectory.path, 'bin', 'internal', 'engine.version'), + ).readAsStringSync().trim(); + } + + String get flutterRevision { + return File( + p.join(shorebirdRoot.path, 'bin', 'internal', 'flutter.version'), + ).readAsStringSync().trim(); + } + + /// The root of the Shorebird-vended Flutter git checkout. + Directory get flutterDirectory { + return Directory( + p.join( + shorebirdRoot.path, + 'bin', + 'cache', + 'flutter', + ), + ); + } + + /// The Shorebird-vended Flutter binary. + File get flutterBinaryFile { + return File( + p.join( + flutterDirectory.path, + 'bin', + 'flutter', + ), + ); + } + + File get genSnapshotFile { + return File( + p.join( + flutterDirectory.path, + 'bin', + 'cache', + 'artifacts', + 'engine', + 'ios-release', + 'gen_snapshot_arm64', + ), + ); + } + + /// The `shorebird.yaml` file for this project. + File getShorebirdYamlFile() { + return File(p.join(Directory.current.path, 'shorebird.yaml')); + } + + /// The `pubspec.yaml` file for this project. + File getPubspecYamlFile() { + return File(p.join(Directory.current.path, 'pubspec.yaml')); + } + + /// The `shorebird.yaml` file for this project, parsed into a [ShorebirdYaml] + /// object. + /// + /// Returns `null` if the file does not exist. + /// Throws a [ParsedYamlException] if the file exists but is invalid. + ShorebirdYaml? getShorebirdYaml() { + final file = getShorebirdYamlFile(); + if (!file.existsSync()) return null; + final yaml = file.readAsStringSync(); + return checkedYamlDecode(yaml, (m) => ShorebirdYaml.fromJson(m!)); + } + + /// The `pubspec.yaml` file for this project, parsed into a [Pubspec] object. + /// + /// Returns `null` if the file does not exist. + /// Throws a [ParsedYamlException] if the file exists but is invalid. + Pubspec? getPubspecYaml() { + final file = getPubspecYamlFile(); + if (!file.existsSync()) return null; + final yaml = file.readAsStringSync(); + return Pubspec.parse(yaml); + } + + /// Whether `shorebird init` has been run in the current project. + bool get isShorebirdInitialized { + return hasShorebirdYaml && pubspecContainsShorebirdYaml; + } + + /// Whether the current project has a `shorebird.yaml` file. + bool get hasShorebirdYaml => getShorebirdYamlFile().existsSync(); + + /// Whether the current project has a `pubspec.yaml` file. + bool get hasPubspecYaml => getPubspecYaml() != null; + + /// Whether the current project's `pubspec.yaml` file contains a reference to + /// `shorebird.yaml` in its `assets` section. + bool get pubspecContainsShorebirdYaml { + final file = File(p.join(Directory.current.path, 'pubspec.yaml')); + final pubspecContents = file.readAsStringSync(); + final yaml = loadYaml(pubspecContents, sourceUrl: file.uri) as Map; + if (!yaml.containsKey('flutter')) return false; + if (!(yaml['flutter'] as Map).containsKey('assets')) return false; + final assets = (yaml['flutter'] as Map)['assets'] as List; + return assets.contains('shorebird.yaml'); + } + + /// Returns the Android package name from the pubspec.yaml file of a Flutter + /// module. + String? get androidPackageName { + final pubspec = getPubspecYaml(); + final module = pubspec?.flutter?['module'] as Map?; + return module?['androidPackage'] as String?; + } + + /// The base URL for the Shorebird code push server that overrides the default + /// used by [CodePushClient]. If none is provided, [CodePushClient] will use + /// its default. + Uri? get hostedUri { + try { + final baseUrl = platform.environment['SHOREBIRD_HOSTED_URL'] ?? + getShorebirdYaml()?.baseUrl; + return baseUrl == null ? null : Uri.tryParse(baseUrl); + } catch (_) { + return null; + } + } +} diff --git a/packages/shorebird_cli/lib/src/shorebird_environment.dart b/packages/shorebird_cli/lib/src/shorebird_environment.dart deleted file mode 100644 index e2f23a1a..00000000 --- a/packages/shorebird_cli/lib/src/shorebird_environment.dart +++ /dev/null @@ -1,144 +0,0 @@ -import 'dart:io' hide Platform; - -import 'package:checked_yaml/checked_yaml.dart'; -import 'package:meta/meta.dart'; -import 'package:path/path.dart' as p; -import 'package:platform/platform.dart'; -import 'package:pubspec_parse/pubspec_parse.dart'; -import 'package:shorebird_cli/src/config/shorebird_yaml.dart'; -import 'package:shorebird_cli/src/platform.dart'; -import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; -import 'package:yaml/yaml.dart'; - -abstract class ShorebirdEnvironment { - /// Environment variables from [Platform.environment]. - static Map get environment => platform.environment; - - /// The root directory of the Shorebird install. - /// - /// Assumes we are running from $ROOT/bin/cache. - static Directory get shorebirdRoot => - File(platform.script.toFilePath()).parent.parent.parent; - - static String get shorebirdEngineRevision { - return _shorebirdEngineRevision ?? - File(p.join(flutterDirectory.path, 'bin', 'internal', 'engine.version')) - .readAsStringSync() - .trim(); - } - - static String? _shorebirdEngineRevision; - - @visibleForTesting - static set shorebirdEngineRevision(String revision) { - _shorebirdEngineRevision = revision; - } - - static String? _flutterRevision; - - @visibleForTesting - static set flutterRevision(String revision) { - _flutterRevision = revision; - } - - static String get flutterRevision { - return _flutterRevision ?? - File( - p.join(shorebirdRoot.path, 'bin', 'internal', 'flutter.version'), - ).readAsStringSync().trim(); - } - - /// The root of the Shorebird-vended Flutter git checkout. - static Directory get flutterDirectory => Directory( - p.join( - shorebirdRoot.path, - 'bin', - 'cache', - 'flutter', - ), - ); - - /// The Shorebird-vended Flutter binary. - static File get flutterBinaryFile => File( - p.join( - flutterDirectory.path, - 'bin', - 'flutter', - ), - ); - - static File get genSnapshotFile => File( - p.join( - flutterDirectory.path, - 'bin', - 'cache', - 'artifacts', - 'engine', - 'ios-release', - 'gen_snapshot_arm64', - ), - ); - - /// The `shorebird.yaml` file for this project. - static File getShorebirdYamlFile() { - return File(p.join(Directory.current.path, 'shorebird.yaml')); - } - - /// The `shorebird.yaml` file for this project, parsed into a [ShorebirdYaml] - /// object. - /// - /// Returns `null` if the file does not exist. - /// Throws a [ParsedYamlException] if the file exists but is invalid. - static ShorebirdYaml? getShorebirdYaml() { - final file = getShorebirdYamlFile(); - if (!file.existsSync()) return null; - final yaml = file.readAsStringSync(); - return checkedYamlDecode(yaml, (m) => ShorebirdYaml.fromJson(m!)); - } - - static bool get isShorebirdInitialized { - return hasShorebirdYaml && pubspecContainsShorebirdYaml; - } - - static bool get hasShorebirdYaml { - return ShorebirdEnvironment.getShorebirdYamlFile().existsSync(); - } - - static bool get hasPubspecYaml { - return ShorebirdEnvironment.getPubspecYaml() != null; - } - - static bool get pubspecContainsShorebirdYaml { - final file = File(p.join(Directory.current.path, 'pubspec.yaml')); - final pubspecContents = file.readAsStringSync(); - final yaml = loadYaml(pubspecContents, sourceUrl: file.uri) as Map; - if (!yaml.containsKey('flutter')) return false; - if (!(yaml['flutter'] as Map).containsKey('assets')) return false; - final assets = (yaml['flutter'] as Map)['assets'] as List; - return assets.contains('shorebird.yaml'); - } - - /// The `pubspec.yaml` file for this project, parsed into a [Pubspec] object. - /// - /// Returns `null` if the file does not exist. - /// Throws a [ParsedYamlException] if the file exists but is invalid. - static Pubspec? getPubspecYaml() { - final file = File(p.join(Directory.current.path, 'pubspec.yaml')); - if (!file.existsSync()) return null; - final yaml = file.readAsStringSync(); - return Pubspec.parse(yaml); - } - - /// The base URL for the Shorebird code push server that overrides the default - /// used by [CodePushClient]. If none is provided, [CodePushClient] will use - /// its default. - static Uri? get hostedUri { - try { - final baseUrl = platform.environment['SHOREBIRD_HOSTED_URL'] ?? - getShorebirdYaml()?.baseUrl; - return baseUrl == null ? null : Uri.tryParse(baseUrl); - } catch (_) { - return null; - } - } -} diff --git a/packages/shorebird_cli/lib/src/shorebird_validator.dart b/packages/shorebird_cli/lib/src/shorebird_validator.dart index 3e677bab..58b9371d 100644 --- a/packages/shorebird_cli/lib/src/shorebird_validator.dart +++ b/packages/shorebird_cli/lib/src/shorebird_validator.dart @@ -4,7 +4,7 @@ import 'package:scoped/scoped.dart'; import 'package:shorebird_cli/src/auth/auth.dart'; import 'package:shorebird_cli/src/logger.dart'; import 'package:shorebird_cli/src/platform.dart'; -import 'package:shorebird_cli/src/shorebird_environment.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; import 'package:shorebird_cli/src/validators/validators.dart'; abstract interface class PreconditionFailedException implements Exception { @@ -73,8 +73,7 @@ class ShorebirdValidator { throw UserNotAuthorizedException(); } - if (checkShorebirdInitialized && - !ShorebirdEnvironment.isShorebirdInitialized) { + if (checkShorebirdInitialized && !shorebirdEnv.isShorebirdInitialized) { logger.err( 'Shorebird is not initialized. Did you run "shorebird init"?', ); diff --git a/packages/shorebird_cli/lib/src/validators/shorebird_flutter_validator.dart b/packages/shorebird_cli/lib/src/validators/shorebird_flutter_validator.dart index c0dc7daf..f8a410cb 100644 --- a/packages/shorebird_cli/lib/src/validators/shorebird_flutter_validator.dart +++ b/packages/shorebird_cli/lib/src/validators/shorebird_flutter_validator.dart @@ -1,5 +1,6 @@ +import 'package:shorebird_cli/src/platform.dart'; import 'package:shorebird_cli/src/process.dart'; -import 'package:shorebird_cli/src/shorebird_environment.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; import 'package:shorebird_cli/src/validators/validators.dart'; import 'package:version/version.dart'; @@ -28,9 +29,9 @@ class ShorebirdFlutterValidator extends Validator { Future> validate() async { final issues = []; - if (!ShorebirdEnvironment.flutterDirectory.existsSync()) { + if (!shorebirdEnv.flutterDirectory.existsSync()) { final message = 'No Flutter directory found at ' - '${ShorebirdEnvironment.flutterDirectory}'; + '${shorebirdEnv.flutterDirectory}'; issues.add( ValidationIssue( severity: ValidationIssueSeverity.error, @@ -43,7 +44,7 @@ class ShorebirdFlutterValidator extends Validator { issues.add( ValidationIssue( severity: ValidationIssueSeverity.warning, - message: '${ShorebirdEnvironment.flutterDirectory} has local ' + message: '${shorebirdEnv.flutterDirectory} has local ' 'modifications', ), ); @@ -96,7 +97,7 @@ This can cause unexpected behavior if you are switching between the tools and th } final flutterStorageEnvironmentValue = - ShorebirdEnvironment.environment['FLUTTER_STORAGE_BASE_URL']; + platform.environment['FLUTTER_STORAGE_BASE_URL']; if (flutterStorageEnvironmentValue != null && flutterStorageEnvironmentValue.isNotEmpty) { issues.add( @@ -115,7 +116,7 @@ This can cause unexpected behavior if you are switching between the tools and th final result = await process.run( 'git', ['status', '--untracked-files=no', '--porcelain'], - workingDirectory: ShorebirdEnvironment.flutterDirectory.path, + workingDirectory: shorebirdEnv.flutterDirectory.path, ); return result.stdout.toString().trim().isEmpty; } diff --git a/packages/shorebird_cli/test/src/cache_test.dart b/packages/shorebird_cli/test/src/cache_test.dart index bcd2c0a6..90bb6159 100644 --- a/packages/shorebird_cli/test/src/cache_test.dart +++ b/packages/shorebird_cli/test/src/cache_test.dart @@ -8,7 +8,7 @@ import 'package:platform/platform.dart'; import 'package:scoped/scoped.dart'; import 'package:shorebird_cli/src/cache.dart'; import 'package:shorebird_cli/src/platform.dart'; -import 'package:shorebird_cli/src/shorebird_environment.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; import 'package:test/test.dart'; class _FakeBaseRequest extends Fake implements http.BaseRequest {} @@ -17,6 +17,8 @@ class _MockHttpClient extends Mock implements http.Client {} class _MockPlatform extends Mock implements Platform {} +class _MockShorebirdEnv extends Mock implements ShorebirdEnv {} + class TestCachedArtifact extends CachedArtifact { TestCachedArtifact({required super.cache, required super.platform}); @@ -29,9 +31,12 @@ class TestCachedArtifact extends CachedArtifact { void main() { group('Cache', () { + const shorebirdEngineRevision = 'test-revision'; + late Directory shorebirdRoot; late http.Client httpClient; late Platform platform; + late ShorebirdEnv shorebirdEnv; late Cache cache; R runWithOverrides(R Function() body) { @@ -40,6 +45,7 @@ void main() { values: { cacheRef.overrideWith(() => cache), platformRef.overrideWith(() => platform), + shorebirdEnvRef.overrideWith(() => shorebirdEnv), }, ); } @@ -51,24 +57,18 @@ void main() { setUp(() { httpClient = _MockHttpClient(); platform = _MockPlatform(); + shorebirdEnv = _MockShorebirdEnv(); shorebirdRoot = Directory.systemTemp.createTempSync(); - ShorebirdEnvironment.shorebirdEngineRevision = 'test-revision'; + when( + () => shorebirdEnv.shorebirdEngineRevision, + ).thenReturn(shorebirdEngineRevision); + when(() => shorebirdEnv.shorebirdRoot).thenReturn(shorebirdRoot); when(() => platform.environment).thenReturn({}); when(() => platform.isMacOS).thenReturn(true); when(() => platform.isWindows).thenReturn(false); when(() => platform.isLinux).thenReturn(false); - when(() => platform.script).thenReturn( - Uri.file( - p.join( - shorebirdRoot.path, - 'bin', - 'cache', - 'shorebird.snapshot', - ), - ), - ); when(() => httpClient.send(any())).thenAnswer( (_) async => http.StreamedResponse( @@ -77,7 +77,7 @@ void main() { ), ); - cache = Cache(httpClient: httpClient, platform: platform); + cache = runWithOverrides(() => Cache(httpClient: httpClient)); }); test('can be instantiated w/out args', () { @@ -86,7 +86,9 @@ void main() { group('getArtifactDirectory', () { test('returns correct directory', () { - final directory = cache.getArtifactDirectory('test'); + final directory = runWithOverrides( + () => cache.getArtifactDirectory('test'), + ); expect( directory.path.endsWith( p.join( @@ -103,7 +105,9 @@ void main() { group('getPreviewDirectory', () { test('returns correct directory', () { - final directory = cache.getPreviewDirectory('test'); + final directory = runWithOverrides( + () => cache.getPreviewDirectory('test'), + ); expect( directory.path.endsWith( p.join( @@ -132,17 +136,18 @@ void main() { group('clear', () { test('deletes the cache directory', () async { - final shorebirdCacheDirectory = - runWithOverrides(() => Cache.shorebirdCacheDirectory) - ..createSync(recursive: true); + final shorebirdCacheDirectory = runWithOverrides( + () => Cache.shorebirdCacheDirectory, + )..createSync(recursive: true); expect(shorebirdCacheDirectory.existsSync(), isTrue); runWithOverrides(cache.clear); expect(shorebirdCacheDirectory.existsSync(), isFalse); }); test('does nothing if directory does not exist', () { - final shorebirdCacheDirectory = - runWithOverrides(() => Cache.shorebirdCacheDirectory); + final shorebirdCacheDirectory = runWithOverrides( + () => Cache.shorebirdCacheDirectory, + ); expect(shorebirdCacheDirectory.existsSync(), isFalse); runWithOverrides(cache.clear); expect(shorebirdCacheDirectory.existsSync(), isFalse); @@ -175,7 +180,7 @@ void main() { request.url, equals( Uri.parse( - '${cache.storageBaseUrl}/${cache.storageBucket}/shorebird/${ShorebirdEnvironment.shorebirdEngineRevision}/patch-darwin-x64.zip', + '${cache.storageBaseUrl}/${cache.storageBucket}/shorebird/$shorebirdEngineRevision/patch-darwin-x64.zip', ), ), ); @@ -196,7 +201,7 @@ void main() { request.url, equals( Uri.parse( - '${cache.storageBaseUrl}/${cache.storageBucket}/shorebird/${ShorebirdEnvironment.shorebirdEngineRevision}/patch-windows-x64.zip', + '${cache.storageBaseUrl}/${cache.storageBucket}/shorebird/$shorebirdEngineRevision/patch-windows-x64.zip', ), ), ); @@ -216,7 +221,7 @@ void main() { request.url, equals( Uri.parse( - '${cache.storageBaseUrl}/${cache.storageBucket}/shorebird/${ShorebirdEnvironment.shorebirdEngineRevision}/patch-linux-x64.zip', + '${cache.storageBaseUrl}/${cache.storageBucket}/shorebird/$shorebirdEngineRevision/patch-linux-x64.zip', ), ), ); diff --git a/packages/shorebird_cli/test/src/code_push_client_wrapper_test.dart b/packages/shorebird_cli/test/src/code_push_client_wrapper_test.dart index 99ce9ba8..7f685e6a 100644 --- a/packages/shorebird_cli/test/src/code_push_client_wrapper_test.dart +++ b/packages/shorebird_cli/test/src/code_push_client_wrapper_test.dart @@ -9,6 +9,7 @@ import 'package:shorebird_cli/src/code_push_client_wrapper.dart'; import 'package:shorebird_cli/src/logger.dart'; import 'package:shorebird_cli/src/platform.dart'; import 'package:shorebird_cli/src/shorebird_build_mixin.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; import 'package:shorebird_cli/src/third_party/flutter_tools/lib/flutter_tools.dart'; import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; import 'package:test/test.dart'; @@ -25,21 +26,25 @@ class _MockPlatform extends Mock implements Platform {} class _MockProgress extends Mock implements Progress {} +class _MockShorebirdEnv extends Mock implements ShorebirdEnv {} + void main() { group('scoped', () { late Auth auth; late http.Client httpClient; late Platform platform; + late ShorebirdEnv shorebirdEnv; setUp(() { auth = _MockAuth(); httpClient = _MockHttpClient(); platform = _MockPlatform(); + shorebirdEnv = _MockShorebirdEnv(); when(() => auth.client).thenReturn(httpClient); - when(() => platform.environment).thenReturn({ - 'SHOREBIRD_HOSTED_URL': 'http://example.com', - }); + when(() => shorebirdEnv.hostedUri).thenReturn( + Uri.parse('http://example.com'), + ); }); test('creates instance from scoped Auth and ShorebirdEnvironment', () { @@ -49,6 +54,7 @@ void main() { codePushClientWrapperRef, authRef.overrideWith(() => auth), platformRef.overrideWith(() => platform), + shorebirdEnvRef.overrideWith(() => shorebirdEnv), }, ); expect( @@ -160,6 +166,43 @@ void main() { }); group('app', () { + group('createApp', () { + test('prompts for displayName when not provided', () async { + const appName = 'test app'; + const app = App(id: appId, displayName: 'Test App'); + when(() => logger.prompt(any())).thenReturn(appName); + when(() => codePushClient.createApp(displayName: appName)).thenAnswer( + (_) async => app, + ); + + await runWithOverrides( + () => codePushClientWrapper.createApp(), + ); + + verify(() => logger.prompt(any())).called(1); + verify( + () => codePushClient.createApp(displayName: appName), + ).called(1); + }); + + test('does not prompt for displayName when not provided', () async { + const appName = 'test app'; + const app = App(id: appId, displayName: 'Test App'); + when(() => codePushClient.createApp(displayName: appName)).thenAnswer( + (_) async => app, + ); + + await runWithOverrides( + () => codePushClientWrapper.createApp(appName: appName), + ); + + verifyNever(() => logger.prompt(any())); + verify( + () => codePushClient.createApp(displayName: appName), + ).called(1); + }); + }); + group('getApps', () { test('exits with code 70 when getting apps fails', () async { const error = 'something went wrong'; diff --git a/packages/shorebird_cli/test/src/command_runner_test.dart b/packages/shorebird_cli/test/src/command_runner_test.dart index 19dcf517..eb7029cb 100644 --- a/packages/shorebird_cli/test/src/command_runner_test.dart +++ b/packages/shorebird_cli/test/src/command_runner_test.dart @@ -1,29 +1,26 @@ import 'package:args/command_runner.dart'; -import 'package:http/http.dart' as http; import 'package:mason_logger/mason_logger.dart'; import 'package:mocktail/mocktail.dart'; import 'package:scoped/scoped.dart'; -import 'package:shorebird_cli/src/auth/auth.dart' hide auth; import 'package:shorebird_cli/src/command_runner.dart'; import 'package:shorebird_cli/src/logger.dart' hide logger; import 'package:shorebird_cli/src/process.dart'; -import 'package:shorebird_cli/src/shorebird_environment.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; import 'package:shorebird_cli/src/version.dart'; import 'package:test/test.dart'; -class _MockAuth extends Mock implements Auth {} - class _MockLogger extends Mock implements Logger {} -class _MockHttpClient extends Mock implements http.Client {} +class _MockShorebirdEnv extends Mock implements ShorebirdEnv {} class _MockProcessResult extends Mock implements ShorebirdProcessResult {} void main() { group(ShorebirdCliCommandRunner, () { - late http.Client httpClient; - late Auth auth; + const shorebirdEngineRevision = 'test-revision'; + late Logger logger; + late ShorebirdEnv shorebirdEnv; late ShorebirdProcessResult processResult; late ShorebirdCliCommandRunner commandRunner; @@ -31,20 +28,20 @@ void main() { return runScoped( body, values: { - authRef.overrideWith(() => auth), - loggerRef.overrideWith(() => logger) + loggerRef.overrideWith(() => logger), + shorebirdEnvRef.overrideWith(() => shorebirdEnv), }, ); } setUp(() { - httpClient = _MockHttpClient(); - auth = _MockAuth(); logger = _MockLogger(); - ShorebirdEnvironment.shorebirdEngineRevision = 'test-revision'; + shorebirdEnv = _MockShorebirdEnv(); processResult = _MockProcessResult(); - when(() => auth.client).thenReturn(httpClient); when(() => processResult.exitCode).thenReturn(ExitCode.success.code); + when( + () => shorebirdEnv.shorebirdEngineRevision, + ).thenReturn(shorebirdEngineRevision); commandRunner = runWithOverrides(ShorebirdCliCommandRunner.new); }); @@ -122,7 +119,7 @@ ${lightCyan.wrap('shorebird release android -- --no-pub lib/main.dart')}''', () => logger.info( ''' Shorebird $packageVersion -Shorebird Engine • revision ${ShorebirdEnvironment.shorebirdEngineRevision}''', +Shorebird Engine • revision $shorebirdEngineRevision''', ), ).called(1); }); diff --git a/packages/shorebird_cli/test/src/commands/account/downgrade_account_command_test.dart b/packages/shorebird_cli/test/src/commands/account/downgrade_account_command_test.dart index 0948c427..9567b291 100644 --- a/packages/shorebird_cli/test/src/commands/account/downgrade_account_command_test.dart +++ b/packages/shorebird_cli/test/src/commands/account/downgrade_account_command_test.dart @@ -1,20 +1,18 @@ -import 'package:http/http.dart' as http; import 'package:mason_logger/mason_logger.dart'; import 'package:mocktail/mocktail.dart'; import 'package:scoped/scoped.dart'; -import 'package:shorebird_cli/src/auth/auth.dart'; +import 'package:shorebird_cli/src/code_push_client_wrapper.dart'; import 'package:shorebird_cli/src/commands/account/account.dart'; import 'package:shorebird_cli/src/logger.dart'; import 'package:shorebird_cli/src/shorebird_validator.dart'; import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; import 'package:test/test.dart'; -class _MockAuth extends Mock implements Auth {} +class _MockCodePushClientWrapper extends Mock + implements CodePushClientWrapper {} class _MockCodePushClient extends Mock implements CodePushClient {} -class _MockHttpClient extends Mock implements http.Client {} - class _MockLogger extends Mock implements Logger {} class _MockProgress extends Mock implements Progress {} @@ -30,9 +28,8 @@ void main() { hasActiveSubscription: true, ); - late Auth auth; + late CodePushClientWrapper codePushClientWrapper; late CodePushClient codePushClient; - late http.Client httpClient; late Logger logger; late Progress progress; late ShorebirdValidator shorebirdValidator; @@ -42,7 +39,7 @@ void main() { return runScoped( body, values: { - authRef.overrideWith(() => auth), + codePushClientWrapperRef.overrideWith(() => codePushClientWrapper), loggerRef.overrideWith(() => logger), shorebirdValidatorRef.overrideWith(() => shorebirdValidator), }, @@ -50,14 +47,15 @@ void main() { } setUp(() { - auth = _MockAuth(); + codePushClientWrapper = _MockCodePushClientWrapper(); codePushClient = _MockCodePushClient(); - httpClient = _MockHttpClient(); logger = _MockLogger(); progress = _MockProgress(); shorebirdValidator = _MockShorebirdValidator(); - when(() => auth.client).thenReturn(httpClient); + when( + () => codePushClientWrapper.codePushClient, + ).thenReturn(codePushClient); when(() => logger.progress(any())).thenReturn(progress); when( () => shorebirdValidator.validatePreconditions( @@ -65,16 +63,7 @@ void main() { ), ).thenAnswer((_) async {}); - command = runWithOverrides( - () => DowngradeAccountCommand( - buildCodePushClient: ({ - required http.Client httpClient, - Uri? hostedUri, - }) { - return codePushClient; - }, - ), - ); + command = runWithOverrides(DowngradeAccountCommand.new); }); test('has a description', () { @@ -100,7 +89,6 @@ void main() { }); test('prints an error if fetch current user fails', () async { - when(() => auth.isAuthenticated).thenReturn(true); when(() => codePushClient.getCurrentUser()).thenThrow( Exception('an error occurred'), ); @@ -114,7 +102,6 @@ void main() { }); test('prints an error if fetch current user returns null', () async { - when(() => auth.isAuthenticated).thenReturn(true); when(() => codePushClient.getCurrentUser()).thenAnswer((_) async => null); final result = await runWithOverrides(command.run); @@ -130,7 +117,6 @@ void main() { test( 'prints an error if the user does not have an active subscription', () async { - when(() => auth.isAuthenticated).thenReturn(true); when( () => codePushClient.getCurrentUser(), ).thenAnswer((_) async => noSubscriptionUser); @@ -147,7 +133,6 @@ void main() { ); test('exits successfully if the user opts not to cancel', () async { - when(() => auth.isAuthenticated).thenReturn(true); when( () => codePushClient.getCurrentUser(), ).thenAnswer((_) async => subscriptionUser); @@ -162,7 +147,6 @@ void main() { }); test('prints an error if call to cancel subscription fails', () async { - when(() => auth.isAuthenticated).thenReturn(true); when( () => codePushClient.getCurrentUser(), ).thenAnswer((_) async => subscriptionUser); @@ -184,8 +168,6 @@ void main() { test('exits successfully on subscription cancellation', () async { // Fri Apr 14 2023 07:00:00 GMT+0000 const cancellationTimestamp = 1681455600; - - when(() => auth.isAuthenticated).thenReturn(true); when( () => codePushClient.getCurrentUser(), ).thenAnswer((_) async => subscriptionUser); diff --git a/packages/shorebird_cli/test/src/commands/account/upgrade_account_command_test.dart b/packages/shorebird_cli/test/src/commands/account/upgrade_account_command_test.dart index 0214a6a4..73506d58 100644 --- a/packages/shorebird_cli/test/src/commands/account/upgrade_account_command_test.dart +++ b/packages/shorebird_cli/test/src/commands/account/upgrade_account_command_test.dart @@ -1,20 +1,18 @@ -import 'package:http/http.dart' as http; import 'package:mason_logger/mason_logger.dart'; import 'package:mocktail/mocktail.dart'; import 'package:scoped/scoped.dart'; -import 'package:shorebird_cli/src/auth/auth.dart'; +import 'package:shorebird_cli/src/code_push_client_wrapper.dart'; import 'package:shorebird_cli/src/commands/account/account.dart'; import 'package:shorebird_cli/src/logger.dart'; import 'package:shorebird_cli/src/shorebird_validator.dart'; import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; import 'package:test/test.dart'; -class _MockAuth extends Mock implements Auth {} +class _MockCodePushClientWrapper extends Mock + implements CodePushClientWrapper {} class _MockCodePushClient extends Mock implements CodePushClient {} -class _MockHttpClient extends Mock implements http.Client {} - class _MockLogger extends Mock implements Logger {} class _MockProgress extends Mock implements Progress {} @@ -26,9 +24,8 @@ class _MockUser extends Mock implements User {} void main() { final paymentLink = Uri.parse('https://example.com/payment-link'); - late Auth auth; + late CodePushClientWrapper codePushClientWrapper; late CodePushClient codePushClient; - late http.Client httpClient; late Logger logger; late Progress progress; late ShorebirdValidator shorebirdValidator; @@ -40,7 +37,7 @@ void main() { return runScoped( body, values: { - authRef.overrideWith(() => auth), + codePushClientWrapperRef.overrideWith(() => codePushClientWrapper), loggerRef.overrideWith(() => logger), shorebirdValidatorRef.overrideWith(() => shorebirdValidator), }, @@ -48,17 +45,16 @@ void main() { } setUp(() { - auth = _MockAuth(); + codePushClientWrapper = _MockCodePushClientWrapper(); codePushClient = _MockCodePushClient(); - httpClient = _MockHttpClient(); logger = _MockLogger(); progress = _MockProgress(); shorebirdValidator = _MockShorebirdValidator(); user = _MockUser(); - when(() => auth.client).thenReturn(httpClient); - when(() => auth.isAuthenticated).thenReturn(true); - + when( + () => codePushClientWrapper.codePushClient, + ).thenReturn(codePushClient); when( () => codePushClient.createPaymentLink(), ).thenAnswer((_) async => paymentLink); @@ -76,13 +72,7 @@ void main() { when(() => user.hasActiveSubscription).thenReturn(false); - command = runWithOverrides( - () => UpgradeAccountCommand( - buildCodePushClient: ({required httpClient, hostedUri}) { - return codePushClient; - }, - ), - ); + command = runWithOverrides(UpgradeAccountCommand.new); }); test('has a description', () { diff --git a/packages/shorebird_cli/test/src/commands/apps/create_apps_command_test.dart b/packages/shorebird_cli/test/src/commands/apps/create_apps_command_test.dart index 9715b89b..5d583f31 100644 --- a/packages/shorebird_cli/test/src/commands/apps/create_apps_command_test.dart +++ b/packages/shorebird_cli/test/src/commands/apps/create_apps_command_test.dart @@ -1,9 +1,8 @@ import 'package:args/args.dart'; -import 'package:http/http.dart' as http; import 'package:mason_logger/mason_logger.dart'; import 'package:mocktail/mocktail.dart'; import 'package:scoped/scoped.dart'; -import 'package:shorebird_cli/src/auth/auth.dart'; +import 'package:shorebird_cli/src/code_push_client_wrapper.dart'; import 'package:shorebird_cli/src/commands/commands.dart'; import 'package:shorebird_cli/src/logger.dart'; import 'package:shorebird_cli/src/shorebird_validator.dart'; @@ -12,11 +11,8 @@ import 'package:test/test.dart'; class _MockArgResults extends Mock implements ArgResults {} -class _MockHttpClient extends Mock implements http.Client {} - -class _MockAuth extends Mock implements Auth {} - -class _MockCodePushClient extends Mock implements CodePushClient {} +class _MockCodePushClientWrapper extends Mock + implements CodePushClientWrapper {} class _MockLogger extends Mock implements Logger {} @@ -25,13 +21,11 @@ class _MockShorebirdValidator extends Mock implements ShorebirdValidator {} void main() { group(CreateAppCommand, () { const appId = 'app-id'; - const displayName = 'Example App'; + const appName = 'Example App'; late ArgResults argResults; - late http.Client httpClient; - late Auth auth; late Logger logger; - late CodePushClient codePushClient; + late CodePushClientWrapper codePushClientWrapper; late ShorebirdValidator shorebirdValidator; late CreateAppCommand command; @@ -39,7 +33,7 @@ void main() { return runScoped( body, values: { - authRef.overrideWith(() => auth), + codePushClientWrapperRef.overrideWith(() => codePushClientWrapper), loggerRef.overrideWith(() => logger), shorebirdValidatorRef.overrideWith(() => shorebirdValidator), }, @@ -48,30 +42,22 @@ void main() { setUp(() { argResults = _MockArgResults(); - httpClient = _MockHttpClient(); - auth = _MockAuth(); logger = _MockLogger(); - codePushClient = _MockCodePushClient(); + codePushClientWrapper = _MockCodePushClientWrapper(); shorebirdValidator = _MockShorebirdValidator(); - when(() => auth.client).thenReturn(httpClient); - when(() => auth.isAuthenticated).thenReturn(true); + when(() => argResults['app-name']).thenReturn(appName); when( () => shorebirdValidator.validatePreconditions( checkUserIsAuthenticated: any(named: 'checkUserIsAuthenticated'), ), ).thenAnswer((_) async {}); + when( + () => codePushClientWrapper.createApp(appName: appName), + ).thenAnswer((_) async => const App(id: appId, displayName: appName)); - command = runWithOverrides( - () => CreateAppCommand( - buildCodePushClient: ({ - required http.Client httpClient, - Uri? hostedUri, - }) { - return codePushClient; - }, - ), - )..testArgResults = argResults; + command = runWithOverrides(CreateAppCommand.new) + ..testArgResults = argResults; }); test('has a description', () { @@ -96,42 +82,28 @@ void main() { ).called(1); }); - test('prompts for app name when not provided', () async { - when( - () => logger.prompt(any(), defaultValue: any(named: 'defaultValue')), - ).thenReturn(displayName); + test('calls createApp with no app-name when not provided', () async { + when(() => argResults['app-name']).thenReturn(null); await runWithOverrides(command.run); - verify( - () => logger.prompt(any(), defaultValue: any(named: 'defaultValue')), - ).called(1); - verify( - () => codePushClient.createApp(displayName: displayName), - ).called(1); + verify(() => codePushClientWrapper.createApp()).called(1); }); test('uses provided app name when provided', () async { - when(() => argResults['app-name']).thenReturn(displayName); await runWithOverrides(command.run); - verifyNever(() => logger.prompt(any())); verify( - () => codePushClient.createApp(displayName: displayName), + () => codePushClientWrapper.createApp(appName: appName), ).called(1); }); test('returns success when app is created', () async { - when(() => argResults['app-name']).thenReturn(displayName); - when( - () => codePushClient.createApp(displayName: displayName), - ).thenAnswer((_) async => const App(id: appId, displayName: displayName)); final result = await runWithOverrides(command.run); expect(result, ExitCode.success.code); }); test('returns software error when app creation fails', () async { final error = Exception('oops'); - when(() => argResults['app-name']).thenReturn(displayName); when( - () => codePushClient.createApp(displayName: displayName), + () => codePushClientWrapper.createApp(appName: appName), ).thenThrow(error); final result = await runWithOverrides(command.run); expect(result, ExitCode.software.code); diff --git a/packages/shorebird_cli/test/src/commands/apps/delete_apps_command_test.dart b/packages/shorebird_cli/test/src/commands/apps/delete_apps_command_test.dart index d2bc0c2e..dd5e92a4 100644 --- a/packages/shorebird_cli/test/src/commands/apps/delete_apps_command_test.dart +++ b/packages/shorebird_cli/test/src/commands/apps/delete_apps_command_test.dart @@ -1,20 +1,19 @@ import 'package:args/args.dart'; -import 'package:http/http.dart' as http; import 'package:mason_logger/mason_logger.dart'; import 'package:mocktail/mocktail.dart'; import 'package:scoped/scoped.dart'; -import 'package:shorebird_cli/src/auth/auth.dart'; +import 'package:shorebird_cli/src/code_push_client_wrapper.dart'; import 'package:shorebird_cli/src/commands/commands.dart'; import 'package:shorebird_cli/src/logger.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; import 'package:shorebird_cli/src/shorebird_validator.dart'; import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; import 'package:test/test.dart'; class _MockArgResults extends Mock implements ArgResults {} -class _MockHttpClient extends Mock implements http.Client {} - -class _MockAuth extends Mock implements Auth {} +class _MockCodePushClientWrapper extends Mock + implements CodePushClientWrapper {} class _MockCodePushClient extends Mock implements CodePushClient {} @@ -22,15 +21,17 @@ class _MockLogger extends Mock implements Logger {} class _MockShorebirdValidator extends Mock implements ShorebirdValidator {} +class _MockShorebirdEnv extends Mock implements ShorebirdEnv {} + void main() { group(DeleteAppCommand, () { const appId = 'example'; late ArgResults argResults; - late http.Client httpClient; - late Auth auth; late Logger logger; + late CodePushClientWrapper codePushClientWrapper; late CodePushClient codePushClient; + late ShorebirdEnv shorebirdEnv; late ShorebirdValidator shorebirdValidator; late DeleteAppCommand command; @@ -38,8 +39,9 @@ void main() { return runScoped( body, values: { - authRef.overrideWith(() => auth), + codePushClientWrapperRef.overrideWith(() => codePushClientWrapper), loggerRef.overrideWith(() => logger), + shorebirdEnvRef.overrideWith(() => shorebirdEnv), shorebirdValidatorRef.overrideWith(() => shorebirdValidator), }, ); @@ -47,30 +49,23 @@ void main() { setUp(() { argResults = _MockArgResults(); - httpClient = _MockHttpClient(); - auth = _MockAuth(); logger = _MockLogger(); + codePushClientWrapper = _MockCodePushClientWrapper(); codePushClient = _MockCodePushClient(); + shorebirdEnv = _MockShorebirdEnv(); shorebirdValidator = _MockShorebirdValidator(); - when(() => auth.isAuthenticated).thenReturn(true); - when(() => auth.client).thenReturn(httpClient); when( () => shorebirdValidator.validatePreconditions( checkUserIsAuthenticated: any(named: 'checkUserIsAuthenticated'), ), ).thenAnswer((_) async {}); + when( + () => codePushClientWrapper.codePushClient, + ).thenReturn(codePushClient); - command = runWithOverrides( - () => DeleteAppCommand( - buildCodePushClient: ({ - required http.Client httpClient, - Uri? hostedUri, - }) { - return codePushClient; - }, - ), - )..testArgResults = argResults; + command = runWithOverrides(DeleteAppCommand.new) + ..testArgResults = argResults; }); test('has a description', () { 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 d340a7e7..f9b12763 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 @@ -1,67 +1,38 @@ -import 'dart:io'; - import 'package:args/args.dart'; -import 'package:http/http.dart' as http; 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/auth/auth.dart'; import 'package:shorebird_cli/src/commands/build/build.dart'; import 'package:shorebird_cli/src/logger.dart'; import 'package:shorebird_cli/src/process.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; import 'package:shorebird_cli/src/shorebird_validator.dart'; import 'package:test/test.dart'; class _MockArgResults extends Mock implements ArgResults {} -class _MockAuth extends Mock implements Auth {} - -class _MockHttpClient extends Mock implements http.Client {} - class _MockLogger extends Mock implements Logger {} class _MockProcessResult extends Mock implements ShorebirdProcessResult {} class _MockProgress extends Mock implements Progress {} +class _MockShorebirdEnv extends Mock implements ShorebirdEnv {} + class _MockShorebirdProcess extends Mock implements ShorebirdProcess {} class _MockShorebirdValidator extends Mock implements ShorebirdValidator {} void main() { group(BuildAarCommand, () { - const appId = 'test-app-id'; const buildNumber = '1.0'; - const noModulePubspecYamlContent = ''' -name: example -version: 1.0.0 -environment: - sdk: ">=2.19.0 <3.0.0" - -flutter: - assets: - - shorebird.yaml'''; - - const pubspecYamlContent = ''' -name: example -version: 1.0.0 -environment: - sdk: ">=2.19.0 <3.0.0" - -flutter: - module: - androidX: true - androidPackage: com.example.my_flutter_module - iosBundleIdentifier: com.example.myFlutterModule - assets: - - shorebird.yaml'''; + const androidPackageName = 'com.example.my_flutter_module'; late ArgResults argResults; - late Auth auth; - late http.Client httpClient; late Logger logger; late Progress progress; + late ShorebirdEnv shorebirdEnv; late ShorebirdProcess shorebirdProcess; late ShorebirdProcessResult processResult; late ShorebirdValidator shorebirdValidator; @@ -71,41 +42,25 @@ flutter: return runScoped( body, values: { - authRef.overrideWith(() => auth), loggerRef.overrideWith(() => logger), processRef.overrideWith(() => shorebirdProcess), + shorebirdEnvRef.overrideWith(() => shorebirdEnv), shorebirdValidatorRef.overrideWith(() => shorebirdValidator), }, ); } - Directory setUpTempDir({bool includeModule = true}) { - final tempDir = Directory.systemTemp.createTempSync(); - File( - p.join(tempDir.path, 'pubspec.yaml'), - ).writeAsStringSync( - includeModule ? pubspecYamlContent : noModulePubspecYamlContent, - ); - File( - p.join(tempDir.path, 'shorebird.yaml'), - ).writeAsStringSync('app_id: $appId'); - return tempDir; - } - setUp(() { argResults = _MockArgResults(); - auth = _MockAuth(); - httpClient = _MockHttpClient(); logger = _MockLogger(); processResult = _MockProcessResult(); progress = _MockProgress(); + shorebirdEnv = _MockShorebirdEnv(); shorebirdProcess = _MockShorebirdProcess(); shorebirdValidator = _MockShorebirdValidator(); when(() => argResults['build-number']).thenReturn(buildNumber); when(() => argResults.rest).thenReturn([]); - when(() => auth.client).thenReturn(httpClient); - when(() => auth.isAuthenticated).thenReturn(true); when(() => logger.progress(any())).thenReturn(progress); when( @@ -125,6 +80,10 @@ flutter: ), ).thenAnswer((_) async {}); + when( + () => shorebirdEnv.androidPackageName, + ).thenReturn(androidPackageName); + command = runWithOverrides(BuildAarCommand.new) ..testArgResults = argResults; }); @@ -153,35 +112,17 @@ flutter: ).called(1); }); - test('exits with 78 if no pubspec.yaml exists', () async { - final tempDir = Directory.systemTemp.createTempSync(); - final result = await IOOverrides.runZoned( - () async => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, - ); - - expect(result, ExitCode.config.code); - }); - test('exits with 78 if no module entry exists in pubspec.yaml', () async { - final tempDir = setUpTempDir(includeModule: false); - final result = await IOOverrides.runZoned( - () async => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, - ); - + when(() => shorebirdEnv.androidPackageName).thenReturn(null); + final result = await runWithOverrides(command.run); expect(result, ExitCode.config.code); }); test('exits with code 70 when building aar fails', () async { when(() => processResult.exitCode).thenReturn(1); when(() => processResult.stderr).thenReturn('oops'); - final tempDir = setUpTempDir(); - final result = await IOOverrides.runZoned( - () async => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, - ); + final result = await runWithOverrides(command.run); expect(result, equals(ExitCode.software.code)); verify( @@ -204,11 +145,7 @@ flutter: test('exits with code 0 when building aar succeeds', () async { when(() => processResult.exitCode).thenReturn(ExitCode.success.code); - final tempDir = setUpTempDir(); - final result = await IOOverrides.runZoned( - () async => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, - ); + final result = await runWithOverrides(command.run); expect(result, equals(ExitCode.success.code)); diff --git a/packages/shorebird_cli/test/src/commands/build/build_apk_command_test.dart b/packages/shorebird_cli/test/src/commands/build/build_apk_command_test.dart index 3be11238..e5c275f4 100644 --- a/packages/shorebird_cli/test/src/commands/build/build_apk_command_test.dart +++ b/packages/shorebird_cli/test/src/commands/build/build_apk_command_test.dart @@ -1,12 +1,10 @@ import 'dart:io'; import 'package:args/args.dart'; -import 'package:http/http.dart' as http; 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/auth/auth.dart'; import 'package:shorebird_cli/src/commands/build/build.dart'; import 'package:shorebird_cli/src/doctor.dart'; import 'package:shorebird_cli/src/logger.dart'; @@ -17,10 +15,6 @@ import 'package:test/test.dart'; class _MockArgResults extends Mock implements ArgResults {} -class _MockHttpClient extends Mock implements http.Client {} - -class _MockAuth extends Mock implements Auth {} - class _MockDoctor extends Mock implements Doctor {} class _MockLogger extends Mock implements Logger {} @@ -36,11 +30,11 @@ class _MockShorebirdProcess extends Mock implements ShorebirdProcess {} class _MockShorebirdValidator extends Mock implements ShorebirdValidator {} +class _FakeShorebirdProcess extends Fake implements ShorebirdProcess {} + void main() { group(BuildApkCommand, () { late ArgResults argResults; - late http.Client httpClient; - late Auth auth; late Doctor doctor; late Logger logger; late ShorebirdProcessResult processResult; @@ -53,7 +47,6 @@ void main() { return runScoped( body, values: { - authRef.overrideWith(() => auth), doctorRef.overrideWith(() => doctor), loggerRef.overrideWith(() => logger), processRef.overrideWith(() => shorebirdProcess), @@ -62,10 +55,12 @@ void main() { ); } + setUpAll(() { + registerFallbackValue(_FakeShorebirdProcess()); + }); + setUp(() { argResults = _MockArgResults(); - httpClient = _MockHttpClient(); - auth = _MockAuth(); doctor = _MockDoctor(); logger = _MockLogger(); shorebirdProcess = _MockShorebirdProcess(); @@ -73,8 +68,6 @@ void main() { flutterValidator = _MockShorebirdFlutterValidator(); shorebirdValidator = _MockShorebirdValidator(); - registerFallbackValue(shorebirdProcess); - when( () => shorebirdProcess.run( any(), @@ -83,8 +76,6 @@ void main() { ), ).thenAnswer((_) async => processResult); when(() => argResults.rest).thenReturn([]); - when(() => auth.isAuthenticated).thenReturn(true); - when(() => auth.client).thenReturn(httpClient); when(() => logger.progress(any())).thenReturn(_MockProgress()); when(() => logger.info(any())).thenReturn(null); when( diff --git a/packages/shorebird_cli/test/src/commands/build/build_app_bundle_command_test.dart b/packages/shorebird_cli/test/src/commands/build/build_app_bundle_command_test.dart index 606c4f1f..086fb557 100644 --- a/packages/shorebird_cli/test/src/commands/build/build_app_bundle_command_test.dart +++ b/packages/shorebird_cli/test/src/commands/build/build_app_bundle_command_test.dart @@ -1,12 +1,10 @@ import 'dart:io'; import 'package:args/args.dart'; -import 'package:http/http.dart' as http; 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/auth/auth.dart'; import 'package:shorebird_cli/src/commands/build/build.dart'; import 'package:shorebird_cli/src/doctor.dart'; import 'package:shorebird_cli/src/logger.dart'; @@ -17,10 +15,6 @@ import 'package:test/test.dart'; class _MockArgResults extends Mock implements ArgResults {} -class _MockHttpClient extends Mock implements http.Client {} - -class _MockAuth extends Mock implements Auth {} - class _MockDoctor extends Mock implements Doctor {} class _MockLogger extends Mock implements Logger {} @@ -41,8 +35,6 @@ class _FakeShorebirdProcess extends Fake implements ShorebirdProcess {} void main() { group(BuildAppBundleCommand, () { late ArgResults argResults; - late http.Client httpClient; - late Auth auth; late Doctor doctor; late Logger logger; late ShorebirdProcessResult processResult; @@ -55,7 +47,6 @@ void main() { return runScoped( body, values: { - authRef.overrideWith(() => auth), doctorRef.overrideWith(() => doctor), engineConfigRef.overrideWith(() => const EngineConfig.empty()), loggerRef.overrideWith(() => logger), @@ -72,8 +63,6 @@ void main() { setUp(() { argResults = _MockArgResults(); doctor = _MockDoctor(); - httpClient = _MockHttpClient(); - auth = _MockAuth(); logger = _MockLogger(); processResult = _MockProcessResult(); flutterValidator = _MockShorebirdFlutterValidator(); @@ -88,8 +77,6 @@ void main() { ), ).thenAnswer((_) async => processResult); when(() => argResults.rest).thenReturn([]); - when(() => auth.isAuthenticated).thenReturn(true); - when(() => auth.client).thenReturn(httpClient); when(() => logger.progress(any())).thenReturn(_MockProgress()); when(() => logger.info(any())).thenReturn(null); when( diff --git a/packages/shorebird_cli/test/src/commands/build/build_ipa_command_test.dart b/packages/shorebird_cli/test/src/commands/build/build_ipa_command_test.dart index 6950ab6a..27f3809d 100644 --- a/packages/shorebird_cli/test/src/commands/build/build_ipa_command_test.dart +++ b/packages/shorebird_cli/test/src/commands/build/build_ipa_command_test.dart @@ -1,13 +1,11 @@ import 'dart:io'; import 'package:args/args.dart'; -import 'package:http/http.dart' as http; import 'package:mason_logger/mason_logger.dart'; import 'package:mocktail/mocktail.dart'; import 'package:path/path.dart' as p; import 'package:propertylistserialization/propertylistserialization.dart'; import 'package:scoped/scoped.dart'; -import 'package:shorebird_cli/src/auth/auth.dart'; import 'package:shorebird_cli/src/commands/build/build.dart'; import 'package:shorebird_cli/src/doctor.dart'; import 'package:shorebird_cli/src/logger.dart'; @@ -18,10 +16,6 @@ import 'package:test/test.dart'; class _MockArgResults extends Mock implements ArgResults {} -class _MockHttpClient extends Mock implements http.Client {} - -class _MockAuth extends Mock implements Auth {} - class _MockDoctor extends Mock implements Doctor {} class _MockLogger extends Mock implements Logger {} @@ -42,8 +36,6 @@ class _FakeShorebirdProcess extends Fake implements ShorebirdProcess {} void main() { group(BuildIpaCommand, () { late ArgResults argResults; - late http.Client httpClient; - late Auth auth; late Doctor doctor; late Logger logger; late ShorebirdProcessResult processResult; @@ -56,7 +48,6 @@ void main() { return runScoped( body, values: { - authRef.overrideWith(() => auth), doctorRef.overrideWith(() => doctor), loggerRef.overrideWith(() => logger), processRef.overrideWith(() => shorebirdProcess), @@ -71,8 +62,6 @@ void main() { setUp(() { argResults = _MockArgResults(); - httpClient = _MockHttpClient(); - auth = _MockAuth(); doctor = _MockDoctor(); logger = _MockLogger(); shorebirdProcess = _MockShorebirdProcess(); @@ -89,8 +78,6 @@ void main() { ).thenAnswer((_) async => processResult); when(() => argResults['codesign']).thenReturn(true); when(() => argResults.rest).thenReturn([]); - when(() => auth.isAuthenticated).thenReturn(true); - when(() => auth.client).thenReturn(httpClient); when(() => logger.progress(any())).thenReturn(_MockProgress()); when(() => logger.info(any())).thenReturn(null); when(() => doctor.iosCommandValidators).thenReturn([flutterValidator]); diff --git a/packages/shorebird_cli/test/src/commands/cache/clean_cache_command_test.dart b/packages/shorebird_cli/test/src/commands/cache/clean_cache_command_test.dart index 6a41328c..2ba36b14 100644 --- a/packages/shorebird_cli/test/src/commands/cache/clean_cache_command_test.dart +++ b/packages/shorebird_cli/test/src/commands/cache/clean_cache_command_test.dart @@ -2,13 +2,13 @@ import 'dart:io'; import 'package:mason_logger/mason_logger.dart'; import 'package:mocktail/mocktail.dart'; -import 'package:path/path.dart' as p; import 'package:platform/platform.dart'; import 'package:scoped/scoped.dart'; import 'package:shorebird_cli/src/cache.dart'; import 'package:shorebird_cli/src/commands/commands.dart'; import 'package:shorebird_cli/src/logger.dart'; import 'package:shorebird_cli/src/platform.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; import 'package:test/test.dart'; class _MockCache extends Mock implements Cache {} @@ -19,12 +19,15 @@ class _MockPlatform extends Mock implements Platform {} class _MockProgress extends Mock implements Progress {} +class _MockShorebirdEnv extends Mock implements ShorebirdEnv {} + void main() { group('cache clean', () { late Cache cache; late Logger logger; late Platform platform; late Progress progress; + late ShorebirdEnv shorebirdEnv; late CleanCacheCommand command; R runWithOverrides(R Function() body) { @@ -34,6 +37,7 @@ void main() { cacheRef.overrideWith(() => cache), loggerRef.overrideWith(() => logger), platformRef.overrideWith(() => platform), + shorebirdEnvRef.overrideWith(() => shorebirdEnv), }, ); } @@ -43,18 +47,12 @@ void main() { logger = _MockLogger(); platform = _MockPlatform(); progress = _MockProgress(); + shorebirdEnv = _MockShorebirdEnv(); command = runWithOverrides(CleanCacheCommand.new); when(() => logger.progress(any())).thenReturn(progress); - when(() => platform.script).thenReturn( - Uri.file( - p.join( - Directory.systemTemp.createTempSync().path, - 'bin', - 'cache', - 'shorebird.snapshot', - ), - ), + when(() => shorebirdEnv.shorebirdRoot).thenReturn( + Directory.systemTemp.createTempSync(), ); }); diff --git a/packages/shorebird_cli/test/src/commands/collaborators/add_collaborators_command_test.dart b/packages/shorebird_cli/test/src/commands/collaborators/add_collaborators_command_test.dart index d19c9aea..10efb5d0 100644 --- a/packages/shorebird_cli/test/src/commands/collaborators/add_collaborators_command_test.dart +++ b/packages/shorebird_cli/test/src/commands/collaborators/add_collaborators_command_test.dart @@ -1,20 +1,20 @@ import 'package:args/args.dart'; -import 'package:http/http.dart' as http; import 'package:mason_logger/mason_logger.dart'; import 'package:mocktail/mocktail.dart'; import 'package:scoped/scoped.dart'; -import 'package:shorebird_cli/src/auth/auth.dart'; +import 'package:shorebird_cli/src/code_push_client_wrapper.dart'; import 'package:shorebird_cli/src/commands/commands.dart'; +import 'package:shorebird_cli/src/config/config.dart'; import 'package:shorebird_cli/src/logger.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; import 'package:shorebird_cli/src/shorebird_validator.dart'; import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; import 'package:test/test.dart'; class _MockArgResults extends Mock implements ArgResults {} -class _MockHttpClient extends Mock implements http.Client {} - -class _MockAuth extends Mock implements Auth {} +class _MockCodePushClientWrapper extends Mock + implements CodePushClientWrapper {} class _MockCodePushClient extends Mock implements CodePushClient {} @@ -22,19 +22,22 @@ class _MockLogger extends Mock implements Logger {} class _MockProgress extends Mock implements Progress {} +class _MockShorebirdEnv extends Mock implements ShorebirdEnv {} + class _MockShorebirdValidator extends Mock implements ShorebirdValidator {} void main() { group(AddCollaboratorsCommand, () { const appId = 'test-app-id'; const email = 'jane.doe@shorebird.dev'; + const shorebirdYaml = ShorebirdYaml(appId: appId); late ArgResults argResults; - late http.Client httpClient; - late Auth auth; + late CodePushClientWrapper codePushClientWrapper; late CodePushClient codePushClient; late Logger logger; late Progress progress; + late ShorebirdEnv shorebirdEnv; late ShorebirdValidator shorebirdValidator; late AddCollaboratorsCommand command; @@ -42,8 +45,9 @@ void main() { return runScoped( body, values: { - authRef.overrideWith(() => auth), + codePushClientWrapperRef.overrideWith(() => codePushClientWrapper), loggerRef.overrideWith(() => logger), + shorebirdEnvRef.overrideWith(() => shorebirdEnv), shorebirdValidatorRef.overrideWith(() => shorebirdValidator), }, ); @@ -51,35 +55,29 @@ void main() { setUp(() { argResults = _MockArgResults(); - httpClient = _MockHttpClient(); - auth = _MockAuth(); + codePushClientWrapper = _MockCodePushClientWrapper(); codePushClient = _MockCodePushClient(); logger = _MockLogger(); progress = _MockProgress(); + shorebirdEnv = _MockShorebirdEnv(); shorebirdValidator = _MockShorebirdValidator(); when(() => argResults['app-id']).thenReturn(appId); when(() => argResults['email']).thenReturn(email); - when(() => auth.client).thenReturn(httpClient); - when(() => auth.isAuthenticated).thenReturn(true); + when( + () => codePushClientWrapper.codePushClient, + ).thenReturn(codePushClient); when(() => logger.confirm(any())).thenReturn(true); when(() => logger.progress(any())).thenReturn(progress); + when(() => shorebirdEnv.getShorebirdYaml()).thenReturn(shorebirdYaml); when( () => shorebirdValidator.validatePreconditions( checkUserIsAuthenticated: any(named: 'checkUserIsAuthenticated'), ), ).thenAnswer((_) async {}); - command = runWithOverrides( - () => AddCollaboratorsCommand( - buildCodePushClient: ({ - required http.Client httpClient, - Uri? hostedUri, - }) { - return codePushClient; - }, - ), - )..testArgResults = argResults; + command = runWithOverrides(AddCollaboratorsCommand.new) + ..testArgResults = argResults; }); test('name is correct', () { @@ -113,6 +111,7 @@ void main() { test('returns ExitCode.usage when app id is missing.', () async { when(() => argResults['app-id']).thenReturn(null); + when(() => shorebirdEnv.getShorebirdYaml()).thenReturn(null); expect(await runWithOverrides(command.run), ExitCode.usage.code); }); diff --git a/packages/shorebird_cli/test/src/commands/collaborators/delete_collaborators_command_test.dart b/packages/shorebird_cli/test/src/commands/collaborators/delete_collaborators_command_test.dart index 03a98a94..d038fc2f 100644 --- a/packages/shorebird_cli/test/src/commands/collaborators/delete_collaborators_command_test.dart +++ b/packages/shorebird_cli/test/src/commands/collaborators/delete_collaborators_command_test.dart @@ -1,23 +1,23 @@ import 'dart:io'; import 'package:args/args.dart'; -import 'package:http/http.dart' as http; 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/auth/auth.dart'; +import 'package:shorebird_cli/src/code_push_client_wrapper.dart'; import 'package:shorebird_cli/src/commands/commands.dart'; +import 'package:shorebird_cli/src/config/config.dart'; import 'package:shorebird_cli/src/logger.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; import 'package:shorebird_cli/src/shorebird_validator.dart'; import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; import 'package:test/test.dart'; class _MockArgResults extends Mock implements ArgResults {} -class _MockHttpClient extends Mock implements http.Client {} - -class _MockAuth extends Mock implements Auth {} +class _MockCodePushClientWrapper extends Mock + implements CodePushClientWrapper {} class _MockCodePushClient extends Mock implements CodePushClient {} @@ -25,12 +25,15 @@ class _MockLogger extends Mock implements Logger {} class _MockProgress extends Mock implements Progress {} +class _MockShorebirdEnv extends Mock implements ShorebirdEnv {} + class _MockShorebirdValidator extends Mock implements ShorebirdValidator {} void main() { group(DeleteCollaboratorsCommand, () { const appId = 'test-app-id'; const email = 'jane.doe@shorebird.dev'; + const shorebirdYaml = ShorebirdYaml(appId: appId); const collaborator = Collaborator( userId: 0, email: email, @@ -38,11 +41,11 @@ void main() { ); late ArgResults argResults; - late http.Client httpClient; - late Auth auth; + late CodePushClientWrapper codePushClientWrapper; late CodePushClient codePushClient; late Logger logger; late Progress progress; + late ShorebirdEnv shorebirdEnv; late ShorebirdValidator shorebirdValidator; late DeleteCollaboratorsCommand command; @@ -50,8 +53,9 @@ void main() { return runScoped( body, values: { - authRef.overrideWith(() => auth), + codePushClientWrapperRef.overrideWith(() => codePushClientWrapper), loggerRef.overrideWith(() => logger), + shorebirdEnvRef.overrideWith(() => shorebirdEnv), shorebirdValidatorRef.overrideWith(() => shorebirdValidator), }, ); @@ -59,19 +63,20 @@ void main() { setUp(() { argResults = _MockArgResults(); - httpClient = _MockHttpClient(); - auth = _MockAuth(); + codePushClientWrapper = _MockCodePushClientWrapper(); codePushClient = _MockCodePushClient(); logger = _MockLogger(); progress = _MockProgress(); + shorebirdEnv = _MockShorebirdEnv(); shorebirdValidator = _MockShorebirdValidator(); when(() => argResults['app-id']).thenReturn(appId); when(() => argResults['email']).thenReturn(email); - when(() => auth.isAuthenticated).thenReturn(true); - when(() => auth.client).thenReturn(httpClient); when(() => logger.confirm(any())).thenReturn(true); when(() => logger.progress(any())).thenReturn(progress); + when( + () => codePushClientWrapper.codePushClient, + ).thenReturn(codePushClient); when( () => codePushClient.getCollaborators(appId: any(named: 'appId')), ).thenAnswer((_) async => [collaborator]); @@ -81,22 +86,15 @@ void main() { userId: any(named: 'userId'), ), ).thenAnswer((_) async {}); + when(() => shorebirdEnv.getShorebirdYaml()).thenReturn(shorebirdYaml); when( () => shorebirdValidator.validatePreconditions( checkUserIsAuthenticated: any(named: 'checkUserIsAuthenticated'), ), ).thenAnswer((_) async {}); - command = runWithOverrides( - () => DeleteCollaboratorsCommand( - buildCodePushClient: ({ - required http.Client httpClient, - Uri? hostedUri, - }) { - return codePushClient; - }, - ), - )..testArgResults = argResults; + command = runWithOverrides(DeleteCollaboratorsCommand.new) + ..testArgResults = argResults; }); test('description is correct', () { @@ -126,6 +124,7 @@ void main() { test('returns ExitCode.usage when app id is missing.', () async { when(() => argResults['app-id']).thenReturn(null); + when(() => shorebirdEnv.getShorebirdYaml()).thenReturn(null); expect(await runWithOverrides(command.run), ExitCode.usage.code); }); diff --git a/packages/shorebird_cli/test/src/commands/collaborators/list_collaborators_command_test.dart b/packages/shorebird_cli/test/src/commands/collaborators/list_collaborators_command_test.dart index 0082721b..d64be2a3 100644 --- a/packages/shorebird_cli/test/src/commands/collaborators/list_collaborators_command_test.dart +++ b/packages/shorebird_cli/test/src/commands/collaborators/list_collaborators_command_test.dart @@ -1,36 +1,39 @@ import 'package:args/args.dart'; -import 'package:http/http.dart' as http; import 'package:mason_logger/mason_logger.dart'; import 'package:mocktail/mocktail.dart'; import 'package:scoped/scoped.dart'; -import 'package:shorebird_cli/src/auth/auth.dart'; +import 'package:shorebird_cli/src/code_push_client_wrapper.dart'; import 'package:shorebird_cli/src/commands/commands.dart'; +import 'package:shorebird_cli/src/config/config.dart'; import 'package:shorebird_cli/src/logger.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; import 'package:shorebird_cli/src/shorebird_validator.dart'; import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; import 'package:test/test.dart'; class _MockArgResults extends Mock implements ArgResults {} -class _MockHttpClient extends Mock implements http.Client {} - -class _MockAuth extends Mock implements Auth {} +class _MockCodePushClientWrapper extends Mock + implements CodePushClientWrapper {} class _MockCodePushClient extends Mock implements CodePushClient {} class _MockLogger extends Mock implements Logger {} +class _MockShorebirdEnv extends Mock implements ShorebirdEnv {} + class _MockShorebirdValidator extends Mock implements ShorebirdValidator {} void main() { group(ListCollaboratorsCommand, () { const appId = 'test-app-id'; + const shorebirdYaml = ShorebirdYaml(appId: appId); late ArgResults argResults; - late http.Client httpClient; - late Auth auth; + late CodePushClientWrapper codePushClientWrapper; late CodePushClient codePushClient; late Logger logger; + late ShorebirdEnv shorebirdEnv; late ShorebirdValidator shorebirdValidator; late ListCollaboratorsCommand command; @@ -38,8 +41,9 @@ void main() { return runScoped( body, values: { - authRef.overrideWith(() => auth), + codePushClientWrapperRef.overrideWith(() => codePushClientWrapper), loggerRef.overrideWith(() => logger), + shorebirdEnvRef.overrideWith(() => shorebirdEnv), shorebirdValidatorRef.overrideWith(() => shorebirdValidator), }, ); @@ -47,31 +51,25 @@ void main() { setUp(() { argResults = _MockArgResults(); - httpClient = _MockHttpClient(); - auth = _MockAuth(); + codePushClientWrapper = _MockCodePushClientWrapper(); codePushClient = _MockCodePushClient(); logger = _MockLogger(); + shorebirdEnv = _MockShorebirdEnv(); shorebirdValidator = _MockShorebirdValidator(); when(() => argResults['app-id']).thenReturn(appId); - when(() => auth.client).thenReturn(httpClient); - when(() => auth.isAuthenticated).thenReturn(true); + when( + () => codePushClientWrapper.codePushClient, + ).thenReturn(codePushClient); + when(() => shorebirdEnv.getShorebirdYaml()).thenReturn(shorebirdYaml); when( () => shorebirdValidator.validatePreconditions( checkUserIsAuthenticated: any(named: 'checkUserIsAuthenticated'), ), ).thenAnswer((_) async {}); - command = runWithOverrides( - () => ListCollaboratorsCommand( - buildCodePushClient: ({ - required http.Client httpClient, - Uri? hostedUri, - }) { - return codePushClient; - }, - ), - )..testArgResults = argResults; + command = runWithOverrides(ListCollaboratorsCommand.new) + ..testArgResults = argResults; }); test('name is correct', () { @@ -109,6 +107,7 @@ void main() { test('returns ExitCode.usage when app id is missing.', () async { when(() => argResults['app-id']).thenReturn(null); + when(() => shorebirdEnv.getShorebirdYaml()).thenReturn(null); expect(await runWithOverrides(command.run), ExitCode.usage.code); }); diff --git a/packages/shorebird_cli/test/src/commands/doctor_command_test.dart b/packages/shorebird_cli/test/src/commands/doctor_command_test.dart index ce2708d6..ced74fa9 100644 --- a/packages/shorebird_cli/test/src/commands/doctor_command_test.dart +++ b/packages/shorebird_cli/test/src/commands/doctor_command_test.dart @@ -5,7 +5,7 @@ import 'package:scoped/scoped.dart'; import 'package:shorebird_cli/src/commands/commands.dart'; import 'package:shorebird_cli/src/doctor.dart'; import 'package:shorebird_cli/src/logger.dart'; -import 'package:shorebird_cli/src/shorebird_environment.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; import 'package:shorebird_cli/src/validators/validators.dart'; import 'package:shorebird_cli/src/version.dart'; import 'package:test/test.dart'; @@ -16,14 +16,19 @@ class _MockDoctor extends Mock implements Doctor {} class _MockLogger extends Mock implements Logger {} +class _MockShorebirdEnv extends Mock implements ShorebirdEnv {} + class _MockValidator extends Mock implements Validator {} void main() { group('doctor', () { + const shorebirdEngineRevision = 'test-revision'; + late ArgResults argResults; late Doctor doctor; late DoctorCommand command; late Logger logger; + late ShorebirdEnv shorebirdEnv; late Validator validator; R runWithOverrides(R Function() body) { @@ -32,6 +37,7 @@ void main() { values: { doctorRef.overrideWith(() => doctor), loggerRef.overrideWith(() => logger), + shorebirdEnvRef.overrideWith(() => shorebirdEnv), }, ); } @@ -40,10 +46,12 @@ void main() { argResults = _MockArgResults(); doctor = _MockDoctor(); logger = _MockLogger(); + shorebirdEnv = _MockShorebirdEnv(); validator = _MockValidator(); - ShorebirdEnvironment.shorebirdEngineRevision = 'test-revision'; - + when( + () => shorebirdEnv.shorebirdEngineRevision, + ).thenReturn(shorebirdEngineRevision); when(() => doctor.allValidators).thenReturn([validator]); when( () => doctor.runValidators(any(), applyFixes: any(named: 'applyFixes')), @@ -60,7 +68,7 @@ void main() { () => logger.info(''' Shorebird v$packageVersion -Shorebird Engine • revision ${ShorebirdEnvironment.shorebirdEngineRevision} +Shorebird Engine • revision $shorebirdEngineRevision '''), ).called(1); }); @@ -79,8 +87,9 @@ Shorebird Engine • revision ${ShorebirdEnvironment.shorebirdEngineRevision} final result = await runWithOverrides(command.run); expect(result, equals(ExitCode.success.code)); - verify(() => doctor.runValidators([validator], applyFixes: true)) - .called(1); + verify( + () => doctor.runValidators([validator], applyFixes: true), + ).called(1); }); }); } diff --git a/packages/shorebird_cli/test/src/commands/init_command_test.dart b/packages/shorebird_cli/test/src/commands/init_command_test.dart index 44fd385d..f76577b7 100644 --- a/packages/shorebird_cli/test/src/commands/init_command_test.dart +++ b/packages/shorebird_cli/test/src/commands/init_command_test.dart @@ -1,19 +1,19 @@ import 'dart:io' hide Platform; import 'package:args/args.dart'; -import 'package:http/http.dart' as http; import 'package:mason_logger/mason_logger.dart'; import 'package:mocktail/mocktail.dart'; import 'package:path/path.dart' as p; import 'package:platform/platform.dart'; import 'package:scoped/scoped.dart'; -import 'package:shorebird_cli/src/auth/auth.dart'; +import 'package:shorebird_cli/src/code_push_client_wrapper.dart'; import 'package:shorebird_cli/src/commands/init_command.dart'; import 'package:shorebird_cli/src/doctor.dart'; import 'package:shorebird_cli/src/gradlew.dart'; import 'package:shorebird_cli/src/logger.dart'; import 'package:shorebird_cli/src/platform.dart'; import 'package:shorebird_cli/src/process.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; import 'package:shorebird_cli/src/shorebird_validator.dart'; import 'package:shorebird_cli/src/xcodebuild.dart'; import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; @@ -21,22 +21,23 @@ import 'package:test/test.dart'; class _MockArgResults extends Mock implements ArgResults {} -class _MockHttpClient extends Mock implements http.Client {} - -class _MockAuth extends Mock implements Auth {} - -class _MockCodePushClient extends Mock implements CodePushClient {} +class _MockCodePushClientWrapper extends Mock + implements CodePushClientWrapper {} class _MockDoctor extends Mock implements Doctor {} class _MockGradlew extends Mock implements Gradlew {} +class _MockFile extends Mock implements File {} + class _MockLogger extends Mock implements Logger {} class _MockPlatform extends Mock implements Platform {} class _MockProgress extends Mock implements Progress {} +class _MockShorebirdEnv extends Mock implements ShorebirdEnv {} + class _MockShorebirdValidator extends Mock implements ShorebirdValidator {} class _MockXcodeBuild extends Mock implements XcodeBuild {} @@ -47,22 +48,22 @@ void main() { const appId = 'test_app_id'; const appName = 'test_app_name'; const app = App(id: appId, displayName: appName); - const appMetadata = AppMetadata(appId: appId, displayName: appName); const pubspecYamlContent = ''' name: $appName version: $version environment: sdk: ">=2.19.0 <3.0.0"'''; - late http.Client httpClient; late ArgResults argResults; - late Auth auth; late Doctor doctor; late Gradlew gradlew; - late CodePushClient codePushClient; + late CodePushClientWrapper codePushClientWrapper; + late File shorebirdYamlFile; + late File pubspecYamlFile; late Logger logger; late Platform platform; late Progress progress; + late ShorebirdEnv shorebirdEnv; late ShorebirdValidator shorebirdValidator; late XcodeBuild xcodeBuild; late InitCommand command; @@ -71,56 +72,59 @@ environment: return runScoped( body, values: { - authRef.overrideWith(() => auth), + codePushClientWrapperRef.overrideWith(() => codePushClientWrapper), doctorRef.overrideWith(() => doctor), gradlewRef.overrideWith(() => gradlew), loggerRef.overrideWith(() => logger), platformRef.overrideWith(() => platform), processRef.overrideWith(() => process), + shorebirdEnvRef.overrideWith(() => shorebirdEnv), shorebirdValidatorRef.overrideWith(() => shorebirdValidator), xcodeBuildRef.overrideWith(() => xcodeBuild), }, ); } - Directory setUpAppTempDir() { - final tempDir = Directory.systemTemp.createTempSync(); - Directory(p.join(tempDir.path, 'android')).createSync(recursive: true); - Directory(p.join(tempDir.path, 'ios')).createSync(recursive: true); - return tempDir; - } - setUp(() { - httpClient = _MockHttpClient(); argResults = _MockArgResults(); - auth = _MockAuth(); doctor = _MockDoctor(); gradlew = _MockGradlew(); - codePushClient = _MockCodePushClient(); + codePushClientWrapper = _MockCodePushClientWrapper(); + shorebirdYamlFile = _MockFile(); + pubspecYamlFile = _MockFile(); logger = _MockLogger(); platform = _MockPlatform(); progress = _MockProgress(); + shorebirdEnv = _MockShorebirdEnv(); shorebirdValidator = _MockShorebirdValidator(); xcodeBuild = _MockXcodeBuild(); - when(() => auth.isAuthenticated).thenReturn(true); - when(() => auth.client).thenReturn(httpClient); when( - () => codePushClient.createApp(displayName: any(named: 'displayName')), + () => codePushClientWrapper.createApp(appName: any(named: 'appName')), ).thenAnswer((_) async => app); - when( - () => codePushClient.getApps(), - ).thenAnswer((_) async => [appMetadata]); when( () => doctor.runValidators(any(), applyFixes: any(named: 'applyFixes')), ).thenAnswer((_) async => {}); when(() => doctor.allValidators).thenReturn([]); + when( + () => shorebirdEnv.getShorebirdYamlFile(), + ).thenReturn(shorebirdYamlFile); + when(() => shorebirdEnv.getPubspecYamlFile()).thenReturn(pubspecYamlFile); + when( + () => pubspecYamlFile.readAsStringSync(), + ).thenReturn(pubspecYamlContent); + when( + () => pubspecYamlFile.uri, + ).thenReturn(File(p.join('pubspec.yaml')).uri); when( () => logger.prompt(any(), defaultValue: any(named: 'defaultValue')), ).thenReturn(appName); when(() => logger.progress(any())).thenReturn(progress); when(() => gradlew.productFlavors(any())).thenAnswer((_) async => {}); when(() => platform.isMacOS).thenReturn(true); + when(() => shorebirdEnv.hasPubspecYaml).thenReturn(true); + when(() => shorebirdEnv.hasShorebirdYaml).thenReturn(false); + when(() => shorebirdEnv.pubspecContainsShorebirdYaml).thenReturn(false); when( () => shorebirdValidator.validatePreconditions( checkUserIsAuthenticated: any(named: 'checkUserIsAuthenticated'), @@ -130,16 +134,7 @@ environment: () => xcodeBuild.list(any()), ).thenAnswer((_) async => const XcodeProjectBuildInfo()); - command = runWithOverrides( - () => InitCommand( - buildCodePushClient: ({ - required http.Client httpClient, - Uri? hostedUri, - }) { - return codePushClient; - }, - ), - )..testArgResults = argResults; + command = runWithOverrides(InitCommand.new)..testArgResults = argResults; }); test('exits when validation fails', () async { @@ -161,11 +156,8 @@ environment: }); test('throws no input error when pubspec.yaml is not found.', () async { - final tempDir = Directory.systemTemp.createTempSync(); - final exitCode = await IOOverrides.runZoned( - () => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, - ); + when(() => shorebirdEnv.hasPubspecYaml).thenReturn(false); + final exitCode = await runWithOverrides(command.run); verify( () => logger.err( ''' @@ -178,30 +170,18 @@ Please make sure you are running "shorebird init" from the root of your Flutter }); test('throws software error when pubspec.yaml is malformed.', () async { - final tempDir = Directory.systemTemp.createTempSync(); - File(p.join(tempDir.path, 'pubspec.yaml')).createSync(); - final exitCode = await IOOverrides.runZoned( - () => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, - ); + final exception = Exception('oops'); + when(() => shorebirdEnv.hasPubspecYaml).thenThrow(exception); + final exitCode = await runWithOverrides(command.run); verify( - () => logger.err( - any(that: contains('Error parsing "pubspec.yaml":')), - ), + () => logger.err('Error parsing "pubspec.yaml": $exception'), ).called(1); expect(exitCode, ExitCode.software.code); }); test('throws software error when shorebird.yaml already exists', () async { - final tempDir = Directory.systemTemp.createTempSync(); - File( - p.join(tempDir.path, 'pubspec.yaml'), - ).writeAsStringSync(pubspecYamlContent); - File(p.join(tempDir.path, 'shorebird.yaml')).createSync(); - final exitCode = await IOOverrides.runZoned( - () => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, - ); + when(() => shorebirdEnv.hasShorebirdYaml).thenReturn(true); + final exitCode = await runWithOverrides(command.run); verify( () => logger.err( ''' @@ -213,16 +193,9 @@ If you want to reinitialize Shorebird, please run "shorebird init --force".''', }); test('--force overwrites existing shorebird.yaml', () async { + when(() => shorebirdEnv.hasShorebirdYaml).thenReturn(true); when(() => argResults['force']).thenReturn(true); - final tempDir = Directory.systemTemp.createTempSync(); - File( - p.join(tempDir.path, 'pubspec.yaml'), - ).writeAsStringSync(pubspecYamlContent); - File(p.join(tempDir.path, 'shorebird.yaml')).createSync(); - final exitCode = await IOOverrides.runZoned( - () => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, - ); + final exitCode = await runWithOverrides(command.run); verifyNever( () => logger.err( ''' @@ -231,23 +204,17 @@ If you want to reinitialize Shorebird, please run "shorebird init --force".''', ), ); expect(exitCode, ExitCode.success.code); - expect( - File(p.join(tempDir.path, 'shorebird.yaml')).readAsStringSync(), - contains('app_id: $appId'), - ); + verify( + () => shorebirdYamlFile.writeAsStringSync( + any(that: contains('app_id: $appId')), + ), + ).called(1); }); test('fails when an error occurs while extracting flavors', () async { final exception = Exception('oops'); when(() => gradlew.productFlavors(any())).thenThrow(exception); - final tempDir = setUpAppTempDir(); - File( - p.join(tempDir.path, 'pubspec.yaml'), - ).writeAsStringSync(pubspecYamlContent); - final exitCode = await IOOverrides.runZoned( - () => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, - ); + final exitCode = await runWithOverrides(command.run); verify(() => logger.progress('Detecting product flavors')).called(1); verify( () => logger.err( @@ -260,17 +227,10 @@ If you want to reinitialize Shorebird, please run "shorebird init --force".''', test('throws software error when error occurs creating app.', () async { final error = Exception('oops'); - final tempDir = Directory.systemTemp.createTempSync(); - File( - p.join(tempDir.path, 'pubspec.yaml'), - ).writeAsStringSync(pubspecYamlContent); when( - () => codePushClient.createApp(displayName: any(named: 'displayName')), + () => codePushClientWrapper.createApp(appName: any(named: 'appName')), ).thenThrow(error); - final exitCode = await IOOverrides.runZoned( - () => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, - ); + final exitCode = await runWithOverrides(command.run); verify( () => logger.prompt(any(), defaultValue: any(named: 'defaultValue')), ).called(1); @@ -285,9 +245,6 @@ If you want to reinitialize Shorebird, please run "shorebird init --force".''', test('throws software error when unable to detect schemes', () async { final tempDir = Directory.systemTemp.createTempSync(); - File( - p.join(tempDir.path, 'pubspec.yaml'), - ).writeAsStringSync(pubspecYamlContent); Directory(p.join(tempDir.path, 'ios')).createSync(recursive: true); final exitCode = await IOOverrides.runZoned( () => runWithOverrides(command.run), @@ -304,18 +261,16 @@ If you want to reinitialize Shorebird, please run "shorebird init --force".''', test('creates shorebird for an android-only app', () async { final tempDir = Directory.systemTemp.createTempSync(); - File( - p.join(tempDir.path, 'pubspec.yaml'), - ).writeAsStringSync(pubspecYamlContent); final exitCode = await IOOverrides.runZoned( () => runWithOverrides(command.run), getCurrentDirectory: () => tempDir, ); - expect( - File(p.join(tempDir.path, 'shorebird.yaml')).readAsStringSync(), - contains('app_id: $appId'), - ); expect(exitCode, equals(ExitCode.success.code)); + verify( + () => shorebirdYamlFile.writeAsStringSync( + any(that: contains('app_id: $appId')), + ), + ).called(1); verifyNever(() => xcodeBuild.list(any())); }); @@ -324,9 +279,6 @@ If you want to reinitialize Shorebird, please run "shorebird init --force".''', when( () => gradlew.productFlavors(any()), ).thenThrow(MissingAndroidProjectException(tempDir.path)); - File( - p.join(tempDir.path, 'pubspec.yaml'), - ).writeAsStringSync(pubspecYamlContent); File( p.join( tempDir.path, @@ -341,11 +293,12 @@ If you want to reinitialize Shorebird, please run "shorebird init --force".''', () => runWithOverrides(command.run), getCurrentDirectory: () => tempDir, ); - expect( - File(p.join(tempDir.path, 'shorebird.yaml')).readAsStringSync(), - contains('app_id: $appId'), - ); expect(exitCode, equals(ExitCode.success.code)); + verify( + () => shorebirdYamlFile.writeAsStringSync( + any(that: contains('app_id: $appId')), + ), + ).called(1); verifyNever(() => xcodeBuild.list(any())); }); @@ -353,19 +306,15 @@ If you want to reinitialize Shorebird, please run "shorebird init --force".''', const appIds = ['test-appId-1', 'test-appId-2']; var index = 0; when( - () => - codePushClient.createApp(displayName: any(named: 'displayName')), + () => codePushClientWrapper.createApp(appName: any(named: 'appName')), ).thenAnswer((invocation) async { - final displayName = invocation.namedArguments[#displayName] as String; - return App(id: appIds[index++], displayName: displayName); + final appName = invocation.namedArguments[#appName] as String?; + return App(id: appIds[index++], displayName: appName ?? '-'); }); final tempDir = Directory.systemTemp.createTempSync(); when( () => gradlew.productFlavors(any()), ).thenThrow(MissingAndroidProjectException(tempDir.path)); - File( - p.join(tempDir.path, 'pubspec.yaml'), - ).writeAsStringSync(pubspecYamlContent); final schemesPath = p.join( tempDir.path, 'ios', @@ -373,46 +322,45 @@ If you want to reinitialize Shorebird, please run "shorebird init --force".''', 'xcshareddata', 'xcschemes', ); - File(p.join(schemesPath, 'Runner.xcscheme')) - .createSync(recursive: true); - File(p.join(schemesPath, 'internal.xcscheme')) - .createSync(recursive: true); - File(p.join(schemesPath, 'stable.xcscheme')) - .createSync(recursive: true); + File( + p.join(schemesPath, 'Runner.xcscheme'), + ).createSync(recursive: true); + File( + p.join(schemesPath, 'internal.xcscheme'), + ).createSync(recursive: true); + File( + p.join(schemesPath, 'stable.xcscheme'), + ).createSync(recursive: true); final exitCode = await IOOverrides.runZoned( () => runWithOverrides(command.run), getCurrentDirectory: () => tempDir, ); - expect( - File(p.join(tempDir.path, 'shorebird.yaml')).readAsStringSync(), - contains(''' + expect(exitCode, equals(ExitCode.success.code)); + verify( + () => shorebirdYamlFile.writeAsStringSync( + any( + that: contains(''' app_id: ${appIds[0]} flavors: internal: ${appIds[0]} stable: ${appIds[1]}'''), - ); - + ), + ), + ).called(1); verifyInOrder([ - () => codePushClient.createApp(displayName: '$appName (internal)'), - () => codePushClient.createApp(displayName: '$appName (stable)'), + () => codePushClientWrapper.createApp(appName: '$appName (internal)'), + () => codePushClientWrapper.createApp(appName: '$appName (stable)'), ]); - expect(exitCode, equals(ExitCode.success.code)); verifyNever(() => xcodeBuild.list(any())); }); }); test('creates shorebird.yaml for an app without flavors', () async { - final tempDir = Directory.systemTemp.createTempSync(); - File( - p.join(tempDir.path, 'pubspec.yaml'), - ).writeAsStringSync(pubspecYamlContent); - await IOOverrides.runZoned( - () => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, - ); - expect( - File(p.join(tempDir.path, 'shorebird.yaml')).readAsStringSync(), - contains('app_id: $appId'), + await runWithOverrides(command.run); + verify( + () => shorebirdYamlFile.writeAsStringSync( + any(that: contains('app_id: $appId')), + ), ); }); @@ -438,26 +386,19 @@ flavors: }, ); when( - () => - codePushClient.createApp(displayName: any(named: 'displayName')), + () => codePushClientWrapper.createApp(appName: any(named: 'appName')), ).thenAnswer((invocation) async { - final displayName = invocation.namedArguments[#displayName] as String; - return App(id: appIds[index++], displayName: displayName); + final appName = invocation.namedArguments[#appName] as String?; + return App(id: appIds[index++], displayName: appName ?? '--'); }); - final tempDir = setUpAppTempDir(); when( () => xcodeBuild.list(any()), - ).thenThrow(MissingIOSProjectException(tempDir.path)); - File( - p.join(tempDir.path, 'pubspec.yaml'), - ).writeAsStringSync(pubspecYamlContent); - await IOOverrides.runZoned( - () => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, - ); - expect( - File(p.join(tempDir.path, 'shorebird.yaml')).readAsStringSync(), - contains(''' + ).thenThrow(const MissingIOSProjectException('')); + await runWithOverrides(command.run); + verify( + () => shorebirdYamlFile.writeAsStringSync( + any( + that: contains(''' app_id: ${appIds[0]} flavors: development: ${appIds[0]} @@ -466,20 +407,27 @@ flavors: productionInternal: ${appIds[3]} staging: ${appIds[4]} stagingInternal: ${appIds[5]}'''), + ), + ), ); - verifyInOrder([ - () => codePushClient.createApp(displayName: '$appName (development)'), - () => codePushClient.createApp( - displayName: '$appName (developmentInternal)', + () => codePushClientWrapper.createApp( + appName: '$appName (development)', ), - () => codePushClient.createApp(displayName: '$appName (production)'), - () => codePushClient.createApp( - displayName: '$appName (productionInternal)', + () => codePushClientWrapper.createApp( + appName: '$appName (developmentInternal)', ), - () => codePushClient.createApp(displayName: '$appName (staging)'), - () => codePushClient.createApp( - displayName: '$appName (stagingInternal)', + () => codePushClientWrapper.createApp( + appName: '$appName (production)', + ), + () => codePushClientWrapper.createApp( + appName: '$appName (productionInternal)', + ), + () => codePushClientWrapper.createApp( + appName: '$appName (staging)', + ), + () => codePushClientWrapper.createApp( + appName: '$appName (stagingInternal)', ), ]); }); @@ -507,26 +455,19 @@ flavors: ), ); when( - () => - codePushClient.createApp(displayName: any(named: 'displayName')), + () => codePushClientWrapper.createApp(appName: any(named: 'appName')), ).thenAnswer((invocation) async { - final displayName = invocation.namedArguments[#displayName] as String; - return App(id: appIds[index++], displayName: displayName); + final appName = invocation.namedArguments[#appName] as String?; + return App(id: appIds[index++], displayName: appName ?? '-'); }); - final tempDir = setUpAppTempDir(); when( () => gradlew.productFlavors(any()), - ).thenThrow(MissingAndroidProjectException(tempDir.path)); - File( - p.join(tempDir.path, 'pubspec.yaml'), - ).writeAsStringSync(pubspecYamlContent); - await IOOverrides.runZoned( - () => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, - ); - expect( - File(p.join(tempDir.path, 'shorebird.yaml')).readAsStringSync(), - contains(''' + ).thenThrow(const MissingAndroidProjectException('')); + await runWithOverrides(command.run); + verify( + () => shorebirdYamlFile.writeAsStringSync( + any( + that: contains(''' app_id: ${appIds[0]} flavors: development: ${appIds[0]} @@ -535,20 +476,27 @@ flavors: productionInternal: ${appIds[3]} staging: ${appIds[4]} stagingInternal: ${appIds[5]}'''), + ), + ), ); - verifyInOrder([ - () => codePushClient.createApp(displayName: '$appName (development)'), - () => codePushClient.createApp( - displayName: '$appName (developmentInternal)', + () => codePushClientWrapper.createApp( + appName: '$appName (development)', ), - () => codePushClient.createApp(displayName: '$appName (production)'), - () => codePushClient.createApp( - displayName: '$appName (productionInternal)', + () => codePushClientWrapper.createApp( + appName: '$appName (developmentInternal)', ), - () => codePushClient.createApp(displayName: '$appName (staging)'), - () => codePushClient.createApp( - displayName: '$appName (stagingInternal)', + () => codePushClientWrapper.createApp( + appName: '$appName (production)', + ), + () => codePushClientWrapper.createApp( + appName: '$appName (productionInternal)', + ), + () => codePushClientWrapper.createApp( + appName: '$appName (staging)', + ), + () => codePushClientWrapper.createApp( + appName: '$appName (stagingInternal)', ), ]); }); @@ -578,23 +526,16 @@ flavors: ); when(() => gradlew.productFlavors(any())).thenAnswer((_) async => {}); when( - () => - codePushClient.createApp(displayName: any(named: 'displayName')), + () => codePushClientWrapper.createApp(appName: any(named: 'appName')), ).thenAnswer((invocation) async { - final displayName = invocation.namedArguments[#displayName] as String; - return App(id: appIds[index++], displayName: displayName); + final appName = invocation.namedArguments[#appName] as String?; + return App(id: appIds[index++], displayName: appName ?? '--'); }); - final tempDir = setUpAppTempDir(); - File( - p.join(tempDir.path, 'pubspec.yaml'), - ).writeAsStringSync(pubspecYamlContent); - await IOOverrides.runZoned( - () => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, - ); - expect( - File(p.join(tempDir.path, 'shorebird.yaml')).readAsStringSync(), - contains(''' + await runWithOverrides(command.run); + verify( + () => shorebirdYamlFile.writeAsStringSync( + any( + that: contains(''' app_id: ${appIds[0]} flavors: development: ${appIds[1]} @@ -603,20 +544,27 @@ flavors: productionInternal: ${appIds[4]} staging: ${appIds[5]} stagingInternal: ${appIds[6]}'''), + ), + ), ); - verifyInOrder([ - () => codePushClient.createApp(displayName: '$appName (development)'), - () => codePushClient.createApp( - displayName: '$appName (developmentInternal)', + () => codePushClientWrapper.createApp( + appName: '$appName (development)', ), - () => codePushClient.createApp(displayName: '$appName (production)'), - () => codePushClient.createApp( - displayName: '$appName (productionInternal)', + () => codePushClientWrapper.createApp( + appName: '$appName (developmentInternal)', ), - () => codePushClient.createApp(displayName: '$appName (staging)'), - () => codePushClient.createApp( - displayName: '$appName (stagingInternal)', + () => codePushClientWrapper.createApp( + appName: '$appName (production)', + ), + () => codePushClientWrapper.createApp( + appName: '$appName (productionInternal)', + ), + () => codePushClientWrapper.createApp( + appName: '$appName (staging)', + ), + () => codePushClientWrapper.createApp( + appName: '$appName (stagingInternal)', ), ]); }); @@ -646,23 +594,16 @@ flavors: }, ); when( - () => - codePushClient.createApp(displayName: any(named: 'displayName')), + () => codePushClientWrapper.createApp(appName: any(named: 'appName')), ).thenAnswer((invocation) async { - final displayName = invocation.namedArguments[#displayName] as String; - return App(id: appIds[index++], displayName: displayName); + final appName = invocation.namedArguments[#appName] as String?; + return App(id: appIds[index++], displayName: appName ?? '-'); }); - final tempDir = setUpAppTempDir(); - File( - p.join(tempDir.path, 'pubspec.yaml'), - ).writeAsStringSync(pubspecYamlContent); - await IOOverrides.runZoned( - () => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, - ); - expect( - File(p.join(tempDir.path, 'shorebird.yaml')).readAsStringSync(), - contains(''' + await runWithOverrides(command.run); + verify( + () => shorebirdYamlFile.writeAsStringSync( + any( + that: contains(''' app_id: ${appIds[0]} flavors: development: ${appIds[1]} @@ -671,20 +612,27 @@ flavors: productionInternal: ${appIds[4]} staging: ${appIds[5]} stagingInternal: ${appIds[6]}'''), + ), + ), ); - verifyInOrder([ - () => codePushClient.createApp(displayName: '$appName (development)'), - () => codePushClient.createApp( - displayName: '$appName (developmentInternal)', + () => codePushClientWrapper.createApp( + appName: '$appName (development)', ), - () => codePushClient.createApp(displayName: '$appName (production)'), - () => codePushClient.createApp( - displayName: '$appName (productionInternal)', + () => codePushClientWrapper.createApp( + appName: '$appName (developmentInternal)', ), - () => codePushClient.createApp(displayName: '$appName (staging)'), - () => codePushClient.createApp( - displayName: '$appName (stagingInternal)', + () => codePushClientWrapper.createApp( + appName: '$appName (production)', + ), + () => codePushClientWrapper.createApp( + appName: '$appName (productionInternal)', + ), + () => codePushClientWrapper.createApp( + appName: '$appName (staging)', + ), + () => codePushClientWrapper.createApp( + appName: '$appName (stagingInternal)', ), ]); }); @@ -714,23 +662,16 @@ flavors: (_) async => const XcodeProjectBuildInfo(schemes: variants), ); when( - () => - codePushClient.createApp(displayName: any(named: 'displayName')), + () => codePushClientWrapper.createApp(appName: any(named: 'appName')), ).thenAnswer((invocation) async { - final displayName = invocation.namedArguments[#displayName] as String; - return App(id: appIds[index++], displayName: displayName); + final appName = invocation.namedArguments[#appName] as String?; + return App(id: appIds[index++], displayName: appName ?? '-'); }); - final tempDir = setUpAppTempDir(); - File( - p.join(tempDir.path, 'pubspec.yaml'), - ).writeAsStringSync(pubspecYamlContent); - await IOOverrides.runZoned( - () => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, - ); - expect( - File(p.join(tempDir.path, 'shorebird.yaml')).readAsStringSync(), - contains(''' + await runWithOverrides(command.run); + verify( + () => shorebirdYamlFile.writeAsStringSync( + any( + that: contains(''' app_id: ${appIds[0]} flavors: development: ${appIds[0]} @@ -739,20 +680,27 @@ flavors: productionInternal: ${appIds[3]} staging: ${appIds[4]} stagingInternal: ${appIds[5]}'''), + ), + ), ); - verifyInOrder([ - () => codePushClient.createApp(displayName: '$appName (development)'), - () => codePushClient.createApp( - displayName: '$appName (developmentInternal)', + () => codePushClientWrapper.createApp( + appName: '$appName (development)', ), - () => codePushClient.createApp(displayName: '$appName (production)'), - () => codePushClient.createApp( - displayName: '$appName (productionInternal)', + () => codePushClientWrapper.createApp( + appName: '$appName (developmentInternal)', ), - () => codePushClient.createApp(displayName: '$appName (staging)'), - () => codePushClient.createApp( - displayName: '$appName (stagingInternal)', + () => codePushClientWrapper.createApp( + appName: '$appName (production)', + ), + () => codePushClientWrapper.createApp( + appName: '$appName (productionInternal)', + ), + () => codePushClientWrapper.createApp( + appName: '$appName (staging)', + ), + () => codePushClientWrapper.createApp( + appName: '$appName (stagingInternal)', ), ]); }); @@ -790,23 +738,16 @@ flavors: (_) async => const XcodeProjectBuildInfo(schemes: iosVariants), ); when( - () => - codePushClient.createApp(displayName: any(named: 'displayName')), + () => codePushClientWrapper.createApp(appName: any(named: 'appName')), ).thenAnswer((invocation) async { - final displayName = invocation.namedArguments[#displayName] as String; - return App(id: appIds[index++], displayName: displayName); + final appName = invocation.namedArguments[#appName] as String?; + return App(id: appIds[index++], displayName: appName ?? '-'); }); - final tempDir = setUpAppTempDir(); - File( - p.join(tempDir.path, 'pubspec.yaml'), - ).writeAsStringSync(pubspecYamlContent); - await IOOverrides.runZoned( - () => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, - ); - expect( - File(p.join(tempDir.path, 'shorebird.yaml')).readAsStringSync(), - contains(''' + await runWithOverrides(command.run); + verify( + () => shorebirdYamlFile.writeAsStringSync( + any( + that: contains(''' app_id: test-appId-1 flavors: dev: test-appId-1 @@ -817,123 +758,114 @@ flavors: developmentInternal: test-appId-6 staging: test-appId-7 stagingInternal: test-appId-8'''), + ), + ), ); - verifyInOrder([ - () => codePushClient.createApp(displayName: '$appName (dev)'), - () => codePushClient.createApp(displayName: '$appName (devInternal)'), - () => codePushClient.createApp(displayName: '$appName (production)'), - () => codePushClient.createApp( - displayName: '$appName (productionInternal)', + () => codePushClientWrapper.createApp(appName: '$appName (dev)'), + () => codePushClientWrapper.createApp( + appName: '$appName (devInternal)', ), - () => codePushClient.createApp(displayName: '$appName (development)'), - () => codePushClient.createApp( - displayName: '$appName (developmentInternal)', + () => codePushClientWrapper.createApp( + appName: '$appName (production)', ), - () => codePushClient.createApp(displayName: '$appName (staging)'), - () => codePushClient.createApp( - displayName: '$appName (stagingInternal)', + () => codePushClientWrapper.createApp( + appName: '$appName (productionInternal)', + ), + () => codePushClientWrapper.createApp( + appName: '$appName (development)', + ), + () => codePushClientWrapper.createApp( + appName: '$appName (developmentInternal)', + ), + () => codePushClientWrapper.createApp( + appName: '$appName (staging)', + ), + () => codePushClientWrapper.createApp( + appName: '$appName (stagingInternal)', ), ]); }); }); test('detects existing shorebird.yaml in pubspec.yaml assets', () async { - final tempDir = Directory.systemTemp.createTempSync(); - File( - p.join(tempDir.path, 'pubspec.yaml'), - ).writeAsStringSync(''' + when(() => pubspecYamlFile.readAsStringSync()).thenReturn(''' $pubspecYamlContent flutter: assets: - shorebird.yaml '''); - await IOOverrides.runZoned( - () => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, - ); - expect( - File(p.join(tempDir.path, 'shorebird.yaml')).readAsStringSync(), - contains('app_id: $appId'), + await runWithOverrides(command.run); + verify( + () => shorebirdYamlFile.writeAsStringSync( + any(that: contains('app_id: $appId')), + ), ); }); test('creates flutter.assets and adds shorebird.yaml', () async { - final tempDir = Directory.systemTemp.createTempSync(); - File( - p.join(tempDir.path, 'pubspec.yaml'), - ).writeAsStringSync(pubspecYamlContent); - await IOOverrides.runZoned( - () => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, - ); - expect( - File(p.join(tempDir.path, 'pubspec.yaml')).readAsStringSync(), - equals(''' + await runWithOverrides(command.run); + verify( + () => pubspecYamlFile.writeAsStringSync( + any( + that: equals(''' $pubspecYamlContent flutter: assets: - shorebird.yaml '''), + ), + ), ); }); test('creates assets and adds shorebird.yaml', () async { - final tempDir = Directory.systemTemp.createTempSync(); - File(p.join(tempDir.path, 'pubspec.yaml')).writeAsStringSync(''' + when(() => pubspecYamlFile.readAsStringSync()).thenReturn(''' $pubspecYamlContent flutter: uses-material-design: true '''); - await IOOverrides.runZoned( - () => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, - ); - expect( - File(p.join(tempDir.path, 'pubspec.yaml')).readAsStringSync(), - equals(''' + await runWithOverrides(command.run); + verify( + () => pubspecYamlFile.writeAsStringSync( + any( + that: equals(''' $pubspecYamlContent flutter: assets: - shorebird.yaml uses-material-design: true '''), + ), + ), ); }); test('adds shorebird.yaml to assets', () async { - final tempDir = Directory.systemTemp.createTempSync(); - File(p.join(tempDir.path, 'pubspec.yaml')).writeAsStringSync(''' + when(() => pubspecYamlFile.readAsStringSync()).thenReturn(''' $pubspecYamlContent flutter: assets: - some/asset.txt '''); - await IOOverrides.runZoned( - () => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, - ); - expect( - File(p.join(tempDir.path, 'pubspec.yaml')).readAsStringSync(), - equals(''' + await runWithOverrides(command.run); + verify( + () => pubspecYamlFile.writeAsStringSync( + any( + that: equals(''' $pubspecYamlContent flutter: assets: - some/asset.txt - shorebird.yaml '''), - ); + ), + ), + ).called(1); }); test('fixes fixable validation errors', () async { - final tempDir = Directory.systemTemp.createTempSync(); - File( - p.join(tempDir.path, 'pubspec.yaml'), - ).writeAsStringSync(pubspecYamlContent); - await IOOverrides.runZoned( - () => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, - ); + await runWithOverrides(command.run); verify(() => doctor.runValidators(any(), applyFixes: true)).called(1); }); }); diff --git a/packages/shorebird_cli/test/src/commands/patch/patch_aar_command_test.dart b/packages/shorebird_cli/test/src/commands/patch/patch_aar_command_test.dart index 850c0b50..217ab4a2 100644 --- a/packages/shorebird_cli/test/src/commands/patch/patch_aar_command_test.dart +++ b/packages/shorebird_cli/test/src/commands/patch/patch_aar_command_test.dart @@ -12,12 +12,14 @@ import 'package:shorebird_cli/src/auth/auth.dart'; import 'package:shorebird_cli/src/cache.dart' show Cache, cacheRef; import 'package:shorebird_cli/src/code_push_client_wrapper.dart'; import 'package:shorebird_cli/src/commands/commands.dart'; +import 'package:shorebird_cli/src/config/config.dart'; import 'package:shorebird_cli/src/logger.dart'; import 'package:shorebird_cli/src/platform.dart'; import 'package:shorebird_cli/src/process.dart'; import 'package:shorebird_cli/src/shorebird_build_mixin.dart'; -import 'package:shorebird_cli/src/shorebird_environment.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; import 'package:shorebird_cli/src/shorebird_validator.dart'; +import 'package:shorebird_cli/src/shorebird_version_manager.dart'; import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; import 'package:test/test.dart'; @@ -44,16 +46,22 @@ class _MockProcessResult extends Mock implements ShorebirdProcessResult {} class _MockHttpClient extends Mock implements http.Client {} +class _MockShorebirdEnv extends Mock implements ShorebirdEnv {} + class _MockShorebirdProcess extends Mock implements ShorebirdProcess {} class _MockShorebirdValidator extends Mock implements ShorebirdValidator {} +class _MockShorebirdVersionManager extends Mock + implements ShorebirdVersionManager {} + class _FakeShorebirdProcess extends Fake implements ShorebirdProcess {} void main() { group(PatchAarCommand, () { const flutterRevision = '83305b5088e6fe327fb3334a73ff190828d85713'; const appId = 'test-app-id'; + const shorebirdYaml = ShorebirdYaml(appId: appId); const buildNumber = '1.0'; const versionName = '1.2.3'; const versionCode = '1'; @@ -63,6 +71,7 @@ void main() { const channelName = 'stable'; const appDisplayName = 'Test App'; const appMetadata = AppMetadata(appId: appId, displayName: appDisplayName); + const androidPackageName = 'com.example.my_flutter_module'; const releaseArtifact = ReleaseArtifact( id: 0, releaseId: 0, @@ -89,45 +98,24 @@ void main() { displayName: '1.2.3+1', platformStatuses: {}, ); - const noModulePubspecYamlContent = ''' -name: example -version: 1.0.0 -environment: - sdk: ">=2.19.0 <3.0.0" - -flutter: - assets: - - shorebird.yaml'''; - - const pubspecYamlContent = ''' -name: example -version: 1.0.0 -environment: - sdk: ">=2.19.0 <3.0.0" - -flutter: - module: - androidX: true - androidPackage: com.example.my_flutter_module - iosBundleIdentifier: com.example.myFlutterModule - assets: - - shorebird.yaml'''; late AarDiffer aarDiffer; late ArgResults argResults; late Auth auth; late CodePushClientWrapper codePushClientWrapper; late Directory shorebirdRoot; + late Directory flutterDirectory; late Platform platform; late Progress progress; late Logger logger; late ShorebirdProcessResult flutterBuildProcessResult; - late ShorebirdProcessResult flutterRevisionProcessResult; late ShorebirdProcessResult patchProcessResult; late http.Client httpClient; late Cache cache; + late ShorebirdEnv shorebirdEnv; late ShorebirdProcess shorebirdProcess; late ShorebirdValidator shorebirdValidator; + late ShorebirdVersionManager shorebirdVersionManager; late PatchAarCommand command; R runWithOverrides(R Function() body) { @@ -141,23 +129,16 @@ flutter: loggerRef.overrideWith(() => logger), platformRef.overrideWith(() => platform), processRef.overrideWith(() => shorebirdProcess), + shorebirdEnvRef.overrideWith(() => shorebirdEnv), shorebirdValidatorRef.overrideWith(() => shorebirdValidator), + shorebirdVersionManagerRef.overrideWith( + () => shorebirdVersionManager, + ), }, ); } - Directory setUpTempDir({bool includeModule = true}) { - final tempDir = Directory.systemTemp.createTempSync(); - File( - p.join(tempDir.path, 'pubspec.yaml'), - ).writeAsStringSync( - includeModule ? pubspecYamlContent : noModulePubspecYamlContent, - ); - File( - p.join(tempDir.path, 'shorebird.yaml'), - ).writeAsStringSync('app_id: $appId'); - return tempDir; - } + Directory setUpTempDir() => Directory.systemTemp.createTempSync(); void setUpTempArtifacts(Directory dir) { final aarDir = p.join( @@ -199,30 +180,30 @@ flutter: auth = _MockAuth(); codePushClientWrapper = _MockCodePushClientWrapper(); shorebirdRoot = Directory.systemTemp.createTempSync(); + flutterDirectory = Directory( + p.join(shorebirdRoot.path, 'bin', 'cache', 'flutter'), + ); platform = _MockPlatform(); progress = _MockProgress(); logger = _MockLogger(); flutterBuildProcessResult = _MockProcessResult(); - flutterRevisionProcessResult = _MockProcessResult(); patchProcessResult = _MockProcessResult(); httpClient = _MockHttpClient(); cache = _MockCache(); + shorebirdEnv = _MockShorebirdEnv(); shorebirdProcess = _MockShorebirdProcess(); shorebirdValidator = _MockShorebirdValidator(); + shorebirdVersionManager = _MockShorebirdVersionManager(); registerFallbackValue(ReleasePlatform.android); when(() => platform.environment).thenReturn({}); - when(() => platform.script).thenReturn( - Uri.file( - p.join( - shorebirdRoot.path, - 'bin', - 'cache', - 'shorebird.snapshot', - ), - ), - ); + when(() => shorebirdEnv.shorebirdRoot).thenReturn(shorebirdRoot); + when(() => shorebirdEnv.flutterDirectory).thenReturn(flutterDirectory); + when( + () => shorebirdEnv.androidPackageName, + ).thenReturn(androidPackageName); + when(() => shorebirdEnv.getShorebirdYaml()).thenReturn(shorebirdYaml); when( () => shorebirdProcess.run( 'flutter', @@ -230,14 +211,6 @@ flutter: runInShell: any(named: 'runInShell'), ), ).thenAnswer((_) async => flutterBuildProcessResult); - when( - () => shorebirdProcess.run( - 'git', - any(), - runInShell: any(named: 'runInShell'), - workingDirectory: any(named: 'workingDirectory'), - ), - ).thenAnswer((_) async => flutterRevisionProcessResult); when( () => shorebirdProcess.run( any(that: endsWith('patch')), @@ -253,10 +226,12 @@ flutter: return patchProcessResult; }); - when(() => aarDiffer.changedFiles(any(), any())) - .thenReturn(FileSetDiff.empty()); - when(() => aarDiffer.containsPotentiallyBreakingAssetDiffs(any())) - .thenReturn(false); + when( + () => aarDiffer.changedFiles(any(), any()), + ).thenReturn(FileSetDiff.empty()); + when( + () => aarDiffer.containsPotentiallyBreakingAssetDiffs(any()), + ).thenReturn(false); when(() => argResults.rest).thenReturn([]); when(() => argResults['channel']).thenReturn(channelName); when(() => argResults['dry-run']).thenReturn(false); @@ -273,14 +248,6 @@ flutter: when( () => flutterBuildProcessResult.exitCode, ).thenReturn(ExitCode.success.code); - - when( - () => flutterRevisionProcessResult.exitCode, - ).thenReturn(ExitCode.success.code); - when( - () => flutterRevisionProcessResult.stdout, - ).thenReturn(flutterRevision); - when(() => patchProcessResult.exitCode).thenReturn(ExitCode.success.code); when(() => httpClient.send(any())).thenAnswer( (_) async => http.StreamedResponse(const Stream.empty(), HttpStatus.ok), @@ -335,6 +302,9 @@ flutter: checkShorebirdInitialized: any(named: 'checkShorebirdInitialized'), ), ).thenAnswer((_) async {}); + when( + () => shorebirdVersionManager.fetchCurrentGitHash(), + ).thenAnswer((_) async => flutterRevision); command = runWithOverrides( () => PatchAarCommand( @@ -370,26 +340,15 @@ flutter: }); test('exits with 78 if no module entry exists in pubspec.yaml', () async { - final tempDir = setUpTempDir(includeModule: false); - - final result = await IOOverrides.runZoned( - () => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, - ); - - expect(result, ExitCode.config.code); + when(() => shorebirdEnv.androidPackageName).thenReturn(null); + final exitCode = await runWithOverrides(command.run); + expect(exitCode, ExitCode.config.code); }); test('exits with code 70 when building fails', () async { when(() => flutterBuildProcessResult.exitCode).thenReturn(1); when(() => flutterBuildProcessResult.stderr).thenReturn('oops'); - - final tempDir = setUpTempDir(); - final exitCode = await IOOverrides.runZoned( - () => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, - ); - + final exitCode = await runWithOverrides(command.run); expect(exitCode, equals(ExitCode.software.code)); }); @@ -398,11 +357,7 @@ flutter: 'both --dry-run and --force are specified', () async { when(() => argResults['dry-run']).thenReturn(true); when(() => argResults['force']).thenReturn(true); - final tempDir = setUpTempDir(); - final exitCode = await IOOverrides.runZoned( - () => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, - ); + final exitCode = await runWithOverrides(command.run); expect(exitCode, equals(ExitCode.usage.code)); }); @@ -469,9 +424,10 @@ Please re-run the release command for this version or create a new release.'''), ); test('errors when unable to detect flutter revision', () async { - const error = 'oops'; - when(() => flutterRevisionProcessResult.exitCode).thenReturn(1); - when(() => flutterRevisionProcessResult.stderr).thenReturn(error); + final exception = Exception('oops'); + when( + () => shorebirdVersionManager.fetchCurrentGitHash(), + ).thenThrow(exception); final tempDir = setUpTempDir(); setUpTempArtifacts(tempDir); final exitCode = await IOOverrides.runZoned( @@ -479,21 +435,17 @@ Please re-run the release command for this version or create a new release.'''), getCurrentDirectory: () => tempDir, ); expect(exitCode, ExitCode.software.code); - verify( - () => progress.fail( - 'Exception: Unable to determine flutter revision: $error', - ), - ).called(1); + verify(() => progress.fail('$exception')).called(1); }); test( 'errors when shorebird flutter revision ' 'does not match release revision', () async { const otherRevision = 'other-revision'; - when(() => flutterRevisionProcessResult.stdout).thenReturn(otherRevision); + when( + () => shorebirdVersionManager.fetchCurrentGitHash(), + ).thenAnswer((_) async => otherRevision); final tempDir = setUpTempDir(); - final flutterDir = - runWithOverrides(() => ShorebirdEnvironment.flutterDirectory.path); setUpTempArtifacts(tempDir); final exitCode = await IOOverrides.runZoned( @@ -504,21 +456,11 @@ Please re-run the release command for this version or create a new release.'''), expect(exitCode, ExitCode.software.code); verify( () => logger.err(''' -Flutter revision mismatch. - -The release you are trying to patch was built with a different version of Flutter. - -Release Flutter Revision: $flutterRevision -Current Flutter Revision: $otherRevision -'''), - ).called(1); - verify( - () => logger.info(''' Either create a new release using: ${lightCyan.wrap('shorebird release aar')} Or downgrade your Flutter version and try again using: - ${lightCyan.wrap('cd $flutterDir')} + ${lightCyan.wrap('cd ${shorebirdEnv.flutterDirectory.path}')} ${lightCyan.wrap('git checkout ${release.flutterRevision}')} Shorebird plans to support this automatically, let us know if it's important to you: diff --git a/packages/shorebird_cli/test/src/commands/patch/patch_android_command_test.dart b/packages/shorebird_cli/test/src/commands/patch/patch_android_command_test.dart index 0a48e590..b3c800a9 100644 --- a/packages/shorebird_cli/test/src/commands/patch/patch_android_command_test.dart +++ b/packages/shorebird_cli/test/src/commands/patch/patch_android_command_test.dart @@ -14,14 +14,16 @@ import 'package:shorebird_cli/src/bundletool.dart'; import 'package:shorebird_cli/src/cache.dart' show Cache, cacheRef; import 'package:shorebird_cli/src/code_push_client_wrapper.dart'; import 'package:shorebird_cli/src/commands/patch/patch_android_command.dart'; +import 'package:shorebird_cli/src/config/config.dart'; import 'package:shorebird_cli/src/doctor.dart'; import 'package:shorebird_cli/src/java.dart'; import 'package:shorebird_cli/src/logger.dart'; import 'package:shorebird_cli/src/platform.dart'; import 'package:shorebird_cli/src/process.dart'; import 'package:shorebird_cli/src/shorebird_build_mixin.dart'; -import 'package:shorebird_cli/src/shorebird_environment.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; import 'package:shorebird_cli/src/shorebird_validator.dart'; +import 'package:shorebird_cli/src/shorebird_version_manager.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'; @@ -55,6 +57,8 @@ class _MockProcessResult extends Mock implements ShorebirdProcessResult {} class _MockHttpClient extends Mock implements http.Client {} +class _MockShorebirdEnv extends Mock implements ShorebirdEnv {} + class _MockShorebirdFlutterValidator extends Mock implements ShorebirdFlutterValidator {} @@ -62,12 +66,16 @@ class _MockShorebirdProcess extends Mock implements ShorebirdProcess {} class _MockShorebirdValidator extends Mock implements ShorebirdValidator {} +class _MockShorebirdVersionManager extends Mock + implements ShorebirdVersionManager {} + class _FakeShorebirdProcess extends Fake implements ShorebirdProcess {} void main() { group(PatchAndroidCommand, () { const flutterRevision = '83305b5088e6fe327fb3334a73ff190828d85713'; const appId = 'test-app-id'; + const shorebirdYaml = ShorebirdYaml(appId: appId); const versionName = '1.2.3'; const versionCode = '1'; const version = '$versionName+$versionCode'; @@ -117,20 +125,22 @@ flutter: late Auth auth; late Bundletool bundletool; late CodePushClientWrapper codePushClientWrapper; + late Directory flutterDirectory; late Directory shorebirdRoot; late Doctor doctor; late Java java; late Platform platform; late Progress progress; late Logger logger; + late ShorebirdEnv shorebirdEnv; late ShorebirdProcessResult flutterBuildProcessResult; - late ShorebirdProcessResult flutterRevisionProcessResult; late ShorebirdProcessResult patchProcessResult; late http.Client httpClient; late Cache cache; late ShorebirdFlutterValidator flutterValidator; late ShorebirdProcess shorebirdProcess; late ShorebirdValidator shorebirdValidator; + late ShorebirdVersionManager shorebirdVersionManager; late PatchAndroidCommand command; R runWithOverrides(R Function() body) { @@ -145,9 +155,13 @@ flutter: engineConfigRef.overrideWith(() => const EngineConfig.empty()), javaRef.overrideWith(() => java), loggerRef.overrideWith(() => logger), + shorebirdEnvRef.overrideWith(() => shorebirdEnv), platformRef.overrideWith(() => platform), processRef.overrideWith(() => shorebirdProcess), shorebirdValidatorRef.overrideWith(() => shorebirdValidator), + shorebirdVersionManagerRef.overrideWith( + () => shorebirdVersionManager, + ), }, ); } @@ -198,17 +212,21 @@ flutter: doctor = _MockDoctor(); java = _MockJava(); shorebirdRoot = Directory.systemTemp.createTempSync(); + flutterDirectory = Directory( + p.join(shorebirdRoot.path, 'bin', 'cache', 'flutter'), + ); platform = _MockPlatform(); progress = _MockProgress(); logger = _MockLogger(); flutterBuildProcessResult = _MockProcessResult(); - flutterRevisionProcessResult = _MockProcessResult(); patchProcessResult = _MockProcessResult(); httpClient = _MockHttpClient(); flutterValidator = _MockShorebirdFlutterValidator(); cache = _MockCache(); + shorebirdEnv = _MockShorebirdEnv(); shorebirdProcess = _MockShorebirdProcess(); shorebirdValidator = _MockShorebirdValidator(); + shorebirdVersionManager = _MockShorebirdVersionManager(); command = runWithOverrides( () => PatchAndroidCommand( aabDiffer: aabDiffer, @@ -216,16 +234,9 @@ flutter: ), )..testArgResults = argResults; - when(() => platform.script).thenReturn( - Uri.file( - p.join( - shorebirdRoot.path, - 'bin', - 'cache', - 'shorebird.snapshot', - ), - ), - ); + when(() => shorebirdEnv.getShorebirdYaml()).thenReturn(shorebirdYaml); + when(() => shorebirdEnv.shorebirdRoot).thenReturn(shorebirdRoot); + when(() => shorebirdEnv.flutterDirectory).thenReturn(flutterDirectory); when( () => shorebirdProcess.run( 'flutter', @@ -233,14 +244,6 @@ flutter: runInShell: any(named: 'runInShell'), ), ).thenAnswer((_) async => flutterBuildProcessResult); - when( - () => shorebirdProcess.run( - 'git', - any(), - runInShell: any(named: 'runInShell'), - workingDirectory: any(named: 'workingDirectory'), - ), - ).thenAnswer((_) async => flutterRevisionProcessResult); when( () => shorebirdProcess.run( any(that: endsWith('patch')), @@ -255,17 +258,23 @@ flutter: ..writeAsStringSync('diff'); return patchProcessResult; }); + when(() => patchProcessResult.exitCode).thenReturn(ExitCode.success.code); - when(() => aabDiffer.changedFiles(any(), any())) - .thenReturn(FileSetDiff.empty()); - when(() => aabDiffer.assetsFileSetDiff(any())) - .thenReturn(FileSetDiff.empty()); - when(() => aabDiffer.nativeFileSetDiff(any())) - .thenReturn(FileSetDiff.empty()); - when(() => aabDiffer.containsPotentiallyBreakingAssetDiffs(any())) - .thenReturn(false); - when(() => aabDiffer.containsPotentiallyBreakingNativeDiffs(any())) - .thenReturn(false); + when( + () => aabDiffer.changedFiles(any(), any()), + ).thenReturn(FileSetDiff.empty()); + when( + () => aabDiffer.assetsFileSetDiff(any()), + ).thenReturn(FileSetDiff.empty()); + when( + () => aabDiffer.nativeFileSetDiff(any()), + ).thenReturn(FileSetDiff.empty()); + when( + () => aabDiffer.containsPotentiallyBreakingAssetDiffs(any()), + ).thenReturn(false); + when( + () => aabDiffer.containsPotentiallyBreakingNativeDiffs(any()), + ).thenReturn(false); when(() => argResults.rest).thenReturn([]); when(() => argResults['arch']).thenReturn(arch); when(() => argResults['channel']).thenReturn(channelName); @@ -281,13 +290,6 @@ flutter: when( () => flutterBuildProcessResult.exitCode, ).thenReturn(ExitCode.success.code); - when( - () => flutterRevisionProcessResult.exitCode, - ).thenReturn(ExitCode.success.code); - when(() => patchProcessResult.exitCode).thenReturn(ExitCode.success.code); - when( - () => flutterRevisionProcessResult.stdout, - ).thenReturn(flutterRevision); when(() => httpClient.send(any())).thenAnswer( (_) async => http.StreamedResponse(const Stream.empty(), HttpStatus.ok), ); @@ -354,6 +356,9 @@ flutter: validators: any(named: 'validators'), ), ).thenAnswer((_) async {}); + when( + () => shorebirdVersionManager.fetchCurrentGitHash(), + ).thenAnswer((_) async => flutterRevision); }); test('has a description', () { @@ -483,9 +488,10 @@ Please re-run the release command for this version or create a new release.'''), ); test('errors when unable to detect flutter revision', () async { - const error = 'oops'; - when(() => flutterRevisionProcessResult.exitCode).thenReturn(1); - when(() => flutterRevisionProcessResult.stderr).thenReturn(error); + final exception = Exception('oops'); + when( + () => shorebirdVersionManager.fetchCurrentGitHash(), + ).thenThrow(exception); final tempDir = setUpTempDir(); setUpTempArtifacts(tempDir); final exitCode = await IOOverrides.runZoned( @@ -493,45 +499,32 @@ Please re-run the release command for this version or create a new release.'''), getCurrentDirectory: () => tempDir, ); expect(exitCode, ExitCode.software.code); - verify( - () => progress.fail( - 'Exception: Unable to determine flutter revision: $error', - ), - ).called(1); + verify(() => progress.fail('$exception')).called(1); }); test( 'errors when shorebird flutter revision ' 'does not match release revision', () async { const otherRevision = 'other-revision'; - when(() => flutterRevisionProcessResult.stdout).thenReturn(otherRevision); + when( + () => shorebirdVersionManager.fetchCurrentGitHash(), + ).thenAnswer((_) async => otherRevision); final tempDir = setUpTempDir(); setUpTempArtifacts(tempDir); + final exitCode = await IOOverrides.runZoned( () => runWithOverrides(command.run), getCurrentDirectory: () => tempDir, ); + expect(exitCode, ExitCode.software.code); - final shorebirdFlutterPath = runWithOverrides( - () => ShorebirdEnvironment.flutterDirectory.path, - ); verify( () => logger.err(''' -Flutter revision mismatch. - -The release you are trying to patch was built with a different version of Flutter. - -Release Flutter Revision: $flutterRevision -Current Flutter Revision: $otherRevision -'''), - ).called(1); - verify( - () => logger.info(''' Either create a new release using: - ${lightCyan.wrap('shorebird release')} + ${lightCyan.wrap('shorebird release aar')} Or downgrade your Flutter version and try again using: - ${lightCyan.wrap('cd $shorebirdFlutterPath')} + ${lightCyan.wrap('cd ${shorebirdEnv.flutterDirectory.path}')} ${lightCyan.wrap('git checkout ${release.flutterRevision}')} Shorebird plans to support this automatically, let us know if it's important to you: @@ -846,7 +839,6 @@ https://github.com/shorebirdtech/shorebird/issues/472 ).called(1); verify(() => logger.success('\nāœ… Published Patch!')).called(1); expect(exitCode, ExitCode.success.code); - // expect(capturedHostedUri, isNull); }); test( diff --git a/packages/shorebird_cli/test/src/commands/patch/patch_ios_command_test.dart b/packages/shorebird_cli/test/src/commands/patch/patch_ios_command_test.dart index 00af646e..cb5b7083 100644 --- a/packages/shorebird_cli/test/src/commands/patch/patch_ios_command_test.dart +++ b/packages/shorebird_cli/test/src/commands/patch/patch_ios_command_test.dart @@ -12,12 +12,14 @@ import 'package:shorebird_cli/src/archive_analysis/archive_analysis.dart'; import 'package:shorebird_cli/src/auth/auth.dart'; import 'package:shorebird_cli/src/code_push_client_wrapper.dart'; import 'package:shorebird_cli/src/commands/patch/patch.dart'; +import 'package:shorebird_cli/src/config/config.dart'; import 'package:shorebird_cli/src/doctor.dart'; import 'package:shorebird_cli/src/logger.dart'; import 'package:shorebird_cli/src/platform.dart'; import 'package:shorebird_cli/src/process.dart'; -import 'package:shorebird_cli/src/shorebird_environment.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; import 'package:shorebird_cli/src/shorebird_validator.dart'; +import 'package:shorebird_cli/src/shorebird_version_manager.dart'; import 'package:shorebird_cli/src/third_party/flutter_tools/lib/flutter_tools.dart'; import 'package:shorebird_cli/src/validators/validators.dart'; import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; @@ -48,6 +50,8 @@ class _MockProcessResult extends Mock implements ShorebirdProcessResult {} class _MockHttpClient extends Mock implements http.Client {} +class _MockShorebirdEnv extends Mock implements ShorebirdEnv {} + class _MockShorebirdFlutterValidator extends Mock implements ShorebirdFlutterValidator {} @@ -55,11 +59,15 @@ class _MockShorebirdProcess extends Mock implements ShorebirdProcess {} class _MockShorebirdValidator extends Mock implements ShorebirdValidator {} +class _MockShorebirdVersionManager extends Mock + implements ShorebirdVersionManager {} + class _FakeShorebirdProcess extends Fake implements ShorebirdProcess {} void main() { const flutterRevision = '83305b5088e6fe327fb3334a73ff190828d85713'; const appId = 'test-app-id'; + const shorebirdYaml = ShorebirdYaml(appId: appId); const versionName = '1.2.3'; const versionCode = '1'; const version = '$versionName+$versionCode'; @@ -101,6 +109,9 @@ flutter: late ArgResults argResults; late Auth auth; late CodePushClientWrapper codePushClientWrapper; + late Directory flutterDirectory; + late Directory shorebirdRoot; + late File genSnapshotFile; late Doctor doctor; late Ipa ipa; late IpaReader ipaReader; @@ -109,11 +120,12 @@ flutter: late Platform platform; late ShorebirdProcessResult aotBuildProcessResult; late ShorebirdProcessResult flutterBuildProcessResult; - late ShorebirdProcessResult flutterRevisionProcessResult; late http.Client httpClient; + late ShorebirdEnv shorebirdEnv; late ShorebirdFlutterValidator flutterValidator; late ShorebirdProcess shorebirdProcess; late ShorebirdValidator shorebirdValidator; + late ShorebirdVersionManager shorebirdVersionManager; late PatchIosCommand command; R runWithOverrides(R Function() body) { @@ -126,7 +138,11 @@ flutter: loggerRef.overrideWith(() => logger), platformRef.overrideWith(() => platform), processRef.overrideWith(() => shorebirdProcess), + shorebirdEnvRef.overrideWith(() => shorebirdEnv), shorebirdValidatorRef.overrideWith(() => shorebirdValidator), + shorebirdVersionManagerRef.overrideWith( + () => shorebirdVersionManager, + ), }, ); } @@ -178,6 +194,21 @@ flutter: auth = _MockAuth(); codePushClientWrapper = _MockCodePushClientWrapper(); doctor = _MockDoctor(); + shorebirdRoot = Directory.systemTemp.createTempSync(); + flutterDirectory = Directory( + p.join(shorebirdRoot.path, 'bin', 'cache', 'flutter'), + ); + genSnapshotFile = File( + p.join( + flutterDirectory.path, + 'bin', + 'cache', + 'artifacts', + 'engine', + 'ios-release', + 'gen_snapshot_arm64', + ), + ); ipaReader = _MockIpaReader(); ipa = _MockIpa(); progress = _MockProgress(); @@ -185,11 +216,12 @@ flutter: platform = _MockPlatform(); aotBuildProcessResult = _MockProcessResult(); flutterBuildProcessResult = _MockProcessResult(); - flutterRevisionProcessResult = _MockProcessResult(); httpClient = _MockHttpClient(); + shorebirdEnv = _MockShorebirdEnv(); flutterValidator = _MockShorebirdFlutterValidator(); shorebirdProcess = _MockShorebirdProcess(); shorebirdValidator = _MockShorebirdValidator(); + shorebirdVersionManager = _MockShorebirdVersionManager(); when(() => argResults['arch']).thenReturn(arch); when(() => argResults['dry-run']).thenReturn(false); @@ -223,33 +255,16 @@ flutter: when(() => logger.progress(any())).thenReturn(progress); when(() => platform.operatingSystem).thenReturn(Platform.macOS); when(() => platform.environment).thenReturn({}); - when(() => platform.script).thenReturn( - Uri.file( - p.join( - Directory.systemTemp.createTempSync().path, - 'bin', - 'cache', - 'shorebird.snapshot', - ), - ), - ); - when(() => aotBuildProcessResult.exitCode) - .thenReturn(ExitCode.success.code); - when(() => flutterBuildProcessResult.exitCode) - .thenReturn(ExitCode.success.code); - when(() => flutterRevisionProcessResult.exitCode) - .thenReturn(ExitCode.success.code); + when(() => shorebirdEnv.getShorebirdYaml()).thenReturn(shorebirdYaml); + when(() => shorebirdEnv.shorebirdRoot).thenReturn(shorebirdRoot); + when(() => shorebirdEnv.flutterDirectory).thenReturn(flutterDirectory); + when(() => shorebirdEnv.genSnapshotFile).thenReturn(genSnapshotFile); when( - () => flutterRevisionProcessResult.stdout, - ).thenReturn(flutterRevision); + () => aotBuildProcessResult.exitCode, + ).thenReturn(ExitCode.success.code); when( - () => shorebirdProcess.run( - 'git', - any(), - runInShell: any(named: 'runInShell'), - workingDirectory: any(named: 'workingDirectory'), - ), - ).thenAnswer((_) async => flutterRevisionProcessResult); + () => flutterBuildProcessResult.exitCode, + ).thenReturn(ExitCode.success.code); when( () => shorebirdProcess.run( 'flutter', @@ -272,6 +287,9 @@ flutter: supportedOperatingSystems: any(named: 'supportedOperatingSystems'), ), ).thenAnswer((_) async {}); + when( + () => shorebirdVersionManager.fetchCurrentGitHash(), + ).thenAnswer((_) async => flutterRevision); command = runWithOverrides(() => PatchIosCommand(ipaReader: ipaReader)) ..testArgResults = argResults; @@ -478,9 +496,10 @@ Please re-run the release command for this version or create a new release.'''), ); test('errors when unable to detect flutter revision', () async { - const error = 'oops'; - when(() => flutterRevisionProcessResult.exitCode).thenReturn(1); - when(() => flutterRevisionProcessResult.stderr).thenReturn(error); + final exception = Exception('oops'); + when( + () => shorebirdVersionManager.fetchCurrentGitHash(), + ).thenThrow(exception); final tempDir = setUpTempDir(); setUpTempArtifacts(tempDir); final exitCode = await IOOverrides.runZoned( @@ -488,45 +507,32 @@ Please re-run the release command for this version or create a new release.'''), getCurrentDirectory: () => tempDir, ); expect(exitCode, ExitCode.software.code); - verify( - () => progress.fail( - 'Exception: Unable to determine flutter revision: $error', - ), - ).called(1); + verify(() => progress.fail('$exception')).called(1); }); test( 'errors when shorebird flutter revision ' 'does not match release revision', () async { const otherRevision = 'other-revision'; - when(() => flutterRevisionProcessResult.stdout).thenReturn(otherRevision); + when( + () => shorebirdVersionManager.fetchCurrentGitHash(), + ).thenAnswer((_) async => otherRevision); final tempDir = setUpTempDir(); setUpTempArtifacts(tempDir); + final exitCode = await IOOverrides.runZoned( () => runWithOverrides(command.run), getCurrentDirectory: () => tempDir, ); + expect(exitCode, ExitCode.software.code); - final shorebirdFlutterPath = runWithOverrides( - () => ShorebirdEnvironment.flutterDirectory.path, - ); verify( () => logger.err(''' -Flutter revision mismatch. - -The release you are trying to patch was built with a different version of Flutter. - -Release Flutter Revision: $flutterRevision -Current Flutter Revision: $otherRevision -'''), - ).called(1); - verify( - () => logger.info(''' Either create a new release using: - ${lightCyan.wrap('shorebird release')} + ${lightCyan.wrap('shorebird release aar')} Or downgrade your Flutter version and try again using: - ${lightCyan.wrap('cd $shorebirdFlutterPath')} + ${lightCyan.wrap('cd ${shorebirdEnv.flutterDirectory.path}')} ${lightCyan.wrap('git checkout ${release.flutterRevision}')} Shorebird plans to support this automatically, let us know if it's important to you: diff --git a/packages/shorebird_cli/test/src/commands/patch/patch_ios_framework_command_test.dart b/packages/shorebird_cli/test/src/commands/patch/patch_ios_framework_command_test.dart index cc0c1986..8f24088c 100644 --- a/packages/shorebird_cli/test/src/commands/patch/patch_ios_framework_command_test.dart +++ b/packages/shorebird_cli/test/src/commands/patch/patch_ios_framework_command_test.dart @@ -9,12 +9,14 @@ import 'package:scoped/scoped.dart'; import 'package:shorebird_cli/src/auth/auth.dart'; import 'package:shorebird_cli/src/code_push_client_wrapper.dart'; import 'package:shorebird_cli/src/commands/patch/patch.dart'; +import 'package:shorebird_cli/src/config/config.dart'; import 'package:shorebird_cli/src/doctor.dart'; import 'package:shorebird_cli/src/logger.dart'; import 'package:shorebird_cli/src/platform.dart'; import 'package:shorebird_cli/src/process.dart'; -import 'package:shorebird_cli/src/shorebird_environment.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; import 'package:shorebird_cli/src/shorebird_validator.dart'; +import 'package:shorebird_cli/src/shorebird_version_manager.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'; @@ -36,6 +38,8 @@ class _MockProgress extends Mock implements Progress {} class _MockProcessResult extends Mock implements ShorebirdProcessResult {} +class _MockShorebirdEnv extends Mock implements ShorebirdEnv {} + class _MockShorebirdFlutterValidator extends Mock implements ShorebirdFlutterValidator {} @@ -43,10 +47,14 @@ class _MockShorebirdProcess extends Mock implements ShorebirdProcess {} class _MockShorebirdValidator extends Mock implements ShorebirdValidator {} +class _MockShorebirdVersionManager extends Mock + implements ShorebirdVersionManager {} + void main() { group(PatchIosFrameworkCommand, () { const appDisplayName = 'Test App'; const appId = 'test-app-id'; + const shorebirdYaml = ShorebirdYaml(appId: appId); const versionName = '1.2.3'; const versionCode = '1'; const version = '$versionName+$versionCode'; @@ -74,6 +82,8 @@ flutter: late ArgResults argResults; late CodePushClientWrapper codePushClientWrapper; late Directory shorebirdRoot; + late Directory flutterDirectory; + late File genSnapshotFile; late Doctor doctor; late Platform platform; late Auth auth; @@ -81,10 +91,11 @@ flutter: late Logger logger; late ShorebirdProcessResult aotBuildProcessResult; late ShorebirdProcessResult flutterBuildProcessResult; - late ShorebirdProcessResult flutterRevisionProcessResult; + late ShorebirdEnv shorebirdEnv; late ShorebirdFlutterValidator flutterValidator; late ShorebirdProcess shorebirdProcess; late ShorebirdValidator shorebirdValidator; + late ShorebirdVersionManager shorebirdVersionManager; late PatchIosFrameworkCommand command; R runWithOverrides(R Function() body) { @@ -97,7 +108,11 @@ flutter: loggerRef.overrideWith(() => logger), platformRef.overrideWith(() => platform), processRef.overrideWith(() => shorebirdProcess), + shorebirdEnvRef.overrideWith(() => shorebirdEnv), shorebirdValidatorRef.overrideWith(() => shorebirdValidator), + shorebirdVersionManagerRef.overrideWith( + () => shorebirdVersionManager, + ), }, ); } @@ -142,15 +157,30 @@ flutter: doctor = _MockDoctor(); platform = _MockPlatform(); shorebirdRoot = Directory.systemTemp.createTempSync(); + flutterDirectory = Directory( + p.join(shorebirdRoot.path, 'bin', 'cache', 'flutter'), + ); + genSnapshotFile = File( + p.join( + flutterDirectory.path, + 'bin', + 'cache', + 'artifacts', + 'engine', + 'ios-release', + 'gen_snapshot_arm64', + ), + ); auth = _MockAuth(); progress = _MockProgress(); logger = _MockLogger(); aotBuildProcessResult = _MockProcessResult(); flutterBuildProcessResult = _MockProcessResult(); - flutterRevisionProcessResult = _MockProcessResult(); + shorebirdEnv = _MockShorebirdEnv(); flutterValidator = _MockShorebirdFlutterValidator(); shorebirdProcess = _MockShorebirdProcess(); shorebirdValidator = _MockShorebirdValidator(); + shorebirdVersionManager = _MockShorebirdVersionManager(); when( () => shorebirdProcess.run( @@ -166,14 +196,6 @@ flutter: runInShell: any(named: 'runInShell'), ), ).thenAnswer((_) async => aotBuildProcessResult); - when( - () => shorebirdProcess.run( - 'git', - any(), - runInShell: any(named: 'runInShell'), - workingDirectory: any(named: 'workingDirectory'), - ), - ).thenAnswer((_) async => flutterRevisionProcessResult); when(() => argResults['force']).thenReturn(false); when(() => argResults['release-version']).thenReturn(version); when(() => argResults.rest).thenReturn([]); @@ -183,25 +205,16 @@ flutter: when(() => logger.progress(any())).thenReturn(progress); when(() => logger.confirm(any())).thenReturn(true); when(() => platform.operatingSystem).thenReturn(Platform.macOS); - when(() => platform.script).thenReturn( - Uri.file( - p.join( - shorebirdRoot.path, - 'bin', - 'cache', - 'shorebird.snapshot', - ), - ), - ); - when(() => aotBuildProcessResult.exitCode) - .thenReturn(ExitCode.success.code); - when(() => flutterBuildProcessResult.exitCode) - .thenReturn(ExitCode.success.code); - when(() => flutterRevisionProcessResult.exitCode) - .thenReturn(ExitCode.success.code); + when(() => shorebirdEnv.getShorebirdYaml()).thenReturn(shorebirdYaml); + when(() => shorebirdEnv.shorebirdRoot).thenReturn(shorebirdRoot); + when(() => shorebirdEnv.flutterDirectory).thenReturn(flutterDirectory); + when(() => shorebirdEnv.genSnapshotFile).thenReturn(genSnapshotFile); when( - () => flutterRevisionProcessResult.stdout, - ).thenReturn(flutterRevision); + () => aotBuildProcessResult.exitCode, + ).thenReturn(ExitCode.success.code); + when( + () => flutterBuildProcessResult.exitCode, + ).thenReturn(ExitCode.success.code); when( () => codePushClientWrapper.getApp(appId: any(named: 'appId')), ).thenAnswer((_) async => appMetadata); @@ -228,6 +241,9 @@ flutter: supportedOperatingSystems: any(named: 'supportedOperatingSystems'), ), ).thenAnswer((_) async {}); + when( + () => shorebirdVersionManager.fetchCurrentGitHash(), + ).thenAnswer((_) async => flutterRevision); command = runWithOverrides(PatchIosFrameworkCommand.new) ..testArgResults = argResults; @@ -350,9 +366,10 @@ Please re-run the release command for this version or create a new release.'''), }); test('errors when unable to detect flutter revision', () async { - const error = 'oops'; - when(() => flutterRevisionProcessResult.exitCode).thenReturn(1); - when(() => flutterRevisionProcessResult.stderr).thenReturn(error); + final exception = Exception('oops'); + when( + () => shorebirdVersionManager.fetchCurrentGitHash(), + ).thenThrow(exception); final tempDir = setUpTempDir(); setUpTempArtifacts(tempDir); final exitCode = await IOOverrides.runZoned( @@ -360,45 +377,32 @@ Please re-run the release command for this version or create a new release.'''), getCurrentDirectory: () => tempDir, ); expect(exitCode, ExitCode.software.code); - verify( - () => progress.fail( - 'Exception: Unable to determine flutter revision: $error', - ), - ).called(1); + verify(() => progress.fail('$exception')).called(1); }); test( 'errors when shorebird flutter revision ' 'does not match release revision', () async { const otherRevision = 'other-revision'; - when(() => flutterRevisionProcessResult.stdout).thenReturn(otherRevision); + when( + () => shorebirdVersionManager.fetchCurrentGitHash(), + ).thenAnswer((_) async => otherRevision); final tempDir = setUpTempDir(); setUpTempArtifacts(tempDir); + final exitCode = await IOOverrides.runZoned( () => runWithOverrides(command.run), getCurrentDirectory: () => tempDir, ); + expect(exitCode, ExitCode.software.code); - final shorebirdFlutterPath = runWithOverrides( - () => ShorebirdEnvironment.flutterDirectory.path, - ); verify( () => logger.err(''' -Flutter revision mismatch. - -The release you are trying to patch was built with a different version of Flutter. - -Release Flutter Revision: $flutterRevision -Current Flutter Revision: $otherRevision -'''), - ).called(1); - verify( - () => logger.info(''' Either create a new release using: - ${lightCyan.wrap('shorebird release')} + ${lightCyan.wrap('shorebird release aar')} Or downgrade your Flutter version and try again using: - ${lightCyan.wrap('cd $shorebirdFlutterPath')} + ${lightCyan.wrap('cd ${shorebirdEnv.flutterDirectory.path}')} ${lightCyan.wrap('git checkout ${release.flutterRevision}')} Shorebird plans to support this automatically, let us know if it's important to you: diff --git a/packages/shorebird_cli/test/src/commands/release/release_aar_command_test.dart b/packages/shorebird_cli/test/src/commands/release/release_aar_command_test.dart index a5623aba..c7e84c6c 100644 --- a/packages/shorebird_cli/test/src/commands/release/release_aar_command_test.dart +++ b/packages/shorebird_cli/test/src/commands/release/release_aar_command_test.dart @@ -10,12 +10,15 @@ import 'package:scoped/scoped.dart'; import 'package:shorebird_cli/src/auth/auth.dart'; import 'package:shorebird_cli/src/code_push_client_wrapper.dart'; import 'package:shorebird_cli/src/commands/commands.dart'; +import 'package:shorebird_cli/src/config/config.dart'; import 'package:shorebird_cli/src/java.dart'; import 'package:shorebird_cli/src/logger.dart'; import 'package:shorebird_cli/src/platform.dart'; import 'package:shorebird_cli/src/process.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'; +import 'package:shorebird_cli/src/shorebird_version_manager.dart'; import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; import 'package:test/test.dart'; @@ -40,8 +43,13 @@ class _MockCodePushClientWrapper extends Mock class _MockShorebirdProcess extends Mock implements ShorebirdProcess {} +class _MockShorebirdEnv extends Mock implements ShorebirdEnv {} + class _MockShorebirdValidator extends Mock implements ShorebirdValidator {} +class _MockShorebirdVersionManager extends Mock + implements ShorebirdVersionManager {} + class _FakeRelease extends Fake implements Release {} class _FakeShorebirdProcess extends Fake implements ShorebirdProcess {} @@ -50,6 +58,8 @@ void main() { group(ReleaseAarCommand, () { const appDisplayName = 'Test App'; const appId = 'test-app-id'; + const shorebirdYaml = ShorebirdYaml(appId: appId); + const androidPackageName = 'com.example.my_flutter_module'; const appMetadata = AppMetadata(appId: appId, displayName: appDisplayName); const flutterRevision = '83305b5088e6fe327fb3334a73ff190828d85713'; const versionName = '1.2.3'; @@ -65,29 +75,6 @@ void main() { ); const releasePlatform = ReleasePlatform.android; const buildNumber = '1.0'; - const noModulePubspecYamlContent = ''' -name: example -version: 1.0.0 -environment: - sdk: ">=2.19.0 <3.0.0" - -flutter: - assets: - - shorebird.yaml'''; - - const pubspecYamlContent = ''' -name: example -version: 1.0.0 -environment: - sdk: ">=2.19.0 <3.0.0" - -flutter: - module: - androidX: true - androidPackage: com.example.my_flutter_module - iosBundleIdentifier: com.example.myFlutterModule - assets: - - shorebird.yaml'''; late ArgResults argResults; late http.Client httpClient; @@ -99,9 +86,10 @@ flutter: late Progress progress; late Logger logger; late ShorebirdProcessResult flutterBuildProcessResult; - late ShorebirdProcessResult flutterRevisionProcessResult; + late ShorebirdEnv shorebirdEnv; late ShorebirdProcess shorebirdProcess; late ShorebirdValidator shorebirdValidator; + late ShorebirdVersionManager shorebirdVersionManager; late ReleaseAarCommand command; R runWithOverrides(R Function() body) { @@ -115,25 +103,17 @@ flutter: loggerRef.overrideWith(() => logger), platformRef.overrideWith(() => platform), processRef.overrideWith(() => shorebirdProcess), + shorebirdEnvRef.overrideWith(() => shorebirdEnv), shorebirdValidatorRef.overrideWith(() => shorebirdValidator), + shorebirdVersionManagerRef.overrideWith( + () => shorebirdVersionManager, + ), }, ); } - Directory setUpTempDir({bool includeModule = true}) { - final tempDir = Directory.systemTemp.createTempSync(); - File( - p.join(tempDir.path, 'pubspec.yaml'), - ).writeAsStringSync( - includeModule ? pubspecYamlContent : noModulePubspecYamlContent, - ); - File( - p.join(tempDir.path, 'shorebird.yaml'), - ).writeAsStringSync('app_id: $appId'); - return tempDir; - } - - void setUpTempArtifacts(Directory dir) { + Directory setUpTempArtifacts() { + final dir = Directory.systemTemp.createTempSync(); final aarDir = p.join( dir.path, 'build', @@ -159,6 +139,7 @@ flutter: File(artifactPath).createSync(recursive: true); } File(aarPath).createSync(recursive: true); + return dir; } setUpAll(() { @@ -178,10 +159,11 @@ flutter: progress = _MockProgress(); logger = _MockLogger(); flutterBuildProcessResult = _MockProcessResult(); - flutterRevisionProcessResult = _MockProcessResult(); shorebirdProcess = _MockShorebirdProcess(); shorebirdRoot = Directory.systemTemp.createTempSync(); + shorebirdEnv = _MockShorebirdEnv(); shorebirdValidator = _MockShorebirdValidator(); + shorebirdVersionManager = _MockShorebirdVersionManager(); when(() => auth.client).thenReturn(httpClient); when(() => argResults['build-number']).thenReturn(buildNumber); @@ -191,26 +173,15 @@ flutter: when(() => logger.confirm(any())).thenReturn(true); when(() => logger.progress(any())).thenReturn(progress); - when(() => platform.script).thenReturn( - Uri.file( - p.join( - shorebirdRoot.path, - 'bin', - 'cache', - 'shorebird.snapshot', - ), - ), - ); - - when(() => flutterBuildProcessResult.exitCode) - .thenReturn(ExitCode.success.code); + when(() => shorebirdEnv.getShorebirdYaml()).thenReturn(shorebirdYaml); + when(() => shorebirdEnv.shorebirdRoot).thenReturn(shorebirdRoot); + when( + () => shorebirdEnv.androidPackageName, + ).thenReturn(androidPackageName); when( - () => flutterRevisionProcessResult.exitCode, + () => flutterBuildProcessResult.exitCode, ).thenReturn(ExitCode.success.code); - when( - () => flutterRevisionProcessResult.stdout, - ).thenReturn(flutterRevision); when( () => shorebirdProcess.run( @@ -221,14 +192,6 @@ flutter: ).thenAnswer((invocation) async { return flutterBuildProcessResult; }); - when( - () => shorebirdProcess.run( - 'git', - any(), - runInShell: any(named: 'runInShell'), - workingDirectory: any(named: 'workingDirectory'), - ), - ).thenAnswer((_) async => flutterRevisionProcessResult); when( () => codePushClientWrapper.getApp(appId: any(named: 'appId')), @@ -278,6 +241,9 @@ flutter: checkShorebirdInitialized: any(named: 'checkShorebirdInitialized'), ), ).thenAnswer((_) async {}); + when( + () => shorebirdVersionManager.fetchCurrentGitHash(), + ).thenAnswer((_) async => flutterRevision); command = runWithOverrides( () => ReleaseAarCommand(unzipFn: (_, __) async {}), @@ -308,24 +274,10 @@ flutter: ).called(1); }); - test('exits with 78 if no pubspec.yaml exists', () async { - final tempDir = Directory.systemTemp.createTempSync(); - - final result = await IOOverrides.runZoned( - () => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, - ); - - expect(result, ExitCode.config.code); - }); - test('exits with 78 if no module entry exists in pubspec.yaml', () async { - final tempDir = setUpTempDir(includeModule: false); + when(() => shorebirdEnv.androidPackageName).thenReturn(null); - final result = await IOOverrides.runZoned( - () => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, - ); + final result = await runWithOverrides(command.run); expect(result, ExitCode.config.code); }); @@ -333,13 +285,7 @@ flutter: test('exits with code 70 when building aar fails', () async { when(() => flutterBuildProcessResult.exitCode).thenReturn(1); when(() => flutterBuildProcessResult.stderr).thenReturn('oops'); - final tempDir = setUpTempDir(); - - final result = await IOOverrides.runZoned( - () => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, - ); - + final result = await runWithOverrides(command.run); expect(result, equals(ExitCode.software.code)); verify( () => shorebirdProcess.run( @@ -354,8 +300,9 @@ flutter: runInShell: any(named: 'runInShell'), ), ).called(1); - verify(() => progress.fail(any(that: contains('Failed to build')))) - .called(1); + verify( + () => progress.fail(any(that: contains('Failed to build'))), + ).called(1); }); test('aborts when user opts out', () async { @@ -366,7 +313,7 @@ flutter: defaultValue: any(named: 'defaultValue'), ), ).thenAnswer((_) => '1.0.0'); - final tempDir = setUpTempDir(); + final tempDir = setUpTempArtifacts(); final exitCode = await IOOverrides.runZoned( () => runWithOverrides(command.run), getCurrentDirectory: () => tempDir, @@ -376,32 +323,22 @@ flutter: }); test('throws error when unable to detect flutter revision', () async { - const error = 'oops'; - when(() => flutterRevisionProcessResult.exitCode).thenReturn(1); - when(() => flutterRevisionProcessResult.stderr).thenReturn(error); - final tempDir = setUpTempDir(); - final exitCode = await IOOverrides.runZoned( - () => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, - ); + final exception = Exception('oops'); + when( + () => shorebirdVersionManager.fetchCurrentGitHash(), + ).thenThrow(exception); + final exitCode = await runWithOverrides(command.run); expect(exitCode, ExitCode.software.code); - verify( - () => progress.fail( - 'Exception: Unable to determine flutter revision: $error', - ), - ).called(1); + verify(() => progress.fail('$exception')).called(1); }); test('does not prompt for confirmation when --force is used', () async { when(() => argResults['force']).thenReturn(true); - final tempDir = setUpTempDir(); - setUpTempArtifacts(tempDir); - + final tempDir = setUpTempArtifacts(); final exitCode = await IOOverrides.runZoned( () => runWithOverrides(command.run), getCurrentDirectory: () => tempDir, ); - expect(exitCode, ExitCode.success.code); verify(() => logger.success('\nāœ… Published Release!')).called(1); verifyNever( @@ -410,14 +347,11 @@ flutter: }); test('succeeds when release is successful', () async { - final tempDir = setUpTempDir(); - setUpTempArtifacts(tempDir); - + final tempDir = setUpTempArtifacts(); final exitCode = await IOOverrides.runZoned( () => runWithOverrides(command.run), getCurrentDirectory: () => tempDir, ); - expect(exitCode, ExitCode.success.code); verify(() => logger.success('\nāœ… Published Release!')).called(1); verify( @@ -458,14 +392,11 @@ flutter: releaseVersion: any(named: 'releaseVersion'), ), ).thenAnswer((_) async => release); - final tempDir = setUpTempDir(); - setUpTempArtifacts(tempDir); - + final tempDir = setUpTempArtifacts(); final exitCode = await IOOverrides.runZoned( () => runWithOverrides(command.run), getCurrentDirectory: () => tempDir, ); - expect(exitCode, ExitCode.success.code); verifyNever( () => codePushClientWrapper.createRelease( diff --git a/packages/shorebird_cli/test/src/commands/release/release_android_command_test.dart b/packages/shorebird_cli/test/src/commands/release/release_android_command_test.dart index 71fb8344..17f6c4d6 100644 --- a/packages/shorebird_cli/test/src/commands/release/release_android_command_test.dart +++ b/packages/shorebird_cli/test/src/commands/release/release_android_command_test.dart @@ -12,12 +12,15 @@ import 'package:shorebird_cli/src/bundletool.dart'; import 'package:shorebird_cli/src/cache.dart'; import 'package:shorebird_cli/src/code_push_client_wrapper.dart'; import 'package:shorebird_cli/src/commands/commands.dart'; +import 'package:shorebird_cli/src/config/config.dart'; import 'package:shorebird_cli/src/doctor.dart'; import 'package:shorebird_cli/src/java.dart'; import 'package:shorebird_cli/src/logger.dart'; import 'package:shorebird_cli/src/platform.dart'; import 'package:shorebird_cli/src/process.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; import 'package:shorebird_cli/src/shorebird_validator.dart'; +import 'package:shorebird_cli/src/shorebird_version_manager.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'; @@ -50,8 +53,13 @@ class _MockShorebirdFlutterValidator extends Mock class _MockShorebirdProcess extends Mock implements ShorebirdProcess {} +class _MockShorebirdEnv extends Mock implements ShorebirdEnv {} + class _MockShorebirdValidator extends Mock implements ShorebirdValidator {} +class _MockShorebirdVersionManager extends Mock + implements ShorebirdVersionManager {} + class _MockJava extends Mock implements Java {} class _FakeRelease extends Fake implements Release {} @@ -61,6 +69,7 @@ class _FakeShorebirdProcess extends Fake implements ShorebirdProcess {} void main() { group(ReleaseAndroidCommand, () { const appId = 'test-app-id'; + const shorebirdYaml = ShorebirdYaml(appId: appId); const flutterRevision = '83305b5088e6fe327fb3334a73ff190828d85713'; const versionName = '1.2.3'; const versionCode = '1'; @@ -78,16 +87,6 @@ void main() { platformStatuses: {}, ); - const pubspecYamlContent = ''' -name: example -version: $version -environment: - sdk: ">=2.19.0 <3.0.0" - -flutter: - assets: - - shorebird.yaml'''; - const javaHome = 'test-java-home'; late ArgResults argResults; @@ -103,10 +102,11 @@ flutter: late Progress progress; late Logger logger; late ShorebirdProcessResult flutterBuildProcessResult; - late ShorebirdProcessResult flutterRevisionProcessResult; late ShorebirdFlutterValidator flutterValidator; late ShorebirdProcess shorebirdProcess; + late ShorebirdEnv shorebirdEnv; late ShorebirdValidator shorebirdValidator; + late ShorebirdVersionManager shorebirdVersionManager; late ReleaseAndroidCommand command; R runWithOverrides(R Function() body) { @@ -123,22 +123,15 @@ flutter: loggerRef.overrideWith(() => logger), platformRef.overrideWith(() => platform), processRef.overrideWith(() => shorebirdProcess), + shorebirdEnvRef.overrideWith(() => shorebirdEnv), shorebirdValidatorRef.overrideWith(() => shorebirdValidator), + shorebirdVersionManagerRef.overrideWith( + () => shorebirdVersionManager, + ), }, ); } - Directory setUpTempDir() { - final tempDir = Directory.systemTemp.createTempSync(); - File( - p.join(tempDir.path, 'pubspec.yaml'), - ).writeAsStringSync(pubspecYamlContent); - File( - p.join(tempDir.path, 'shorebird.yaml'), - ).writeAsStringSync('app_id: $appId'); - return tempDir; - } - setUpAll(() { registerFallbackValue(ReleasePlatform.android); registerFallbackValue(ReleaseStatus.draft); @@ -160,21 +153,15 @@ flutter: progress = _MockProgress(); logger = _MockLogger(); flutterBuildProcessResult = _MockProcessResult(); - flutterRevisionProcessResult = _MockProcessResult(); flutterValidator = _MockShorebirdFlutterValidator(); shorebirdProcess = _MockShorebirdProcess(); + shorebirdEnv = _MockShorebirdEnv(); shorebirdValidator = _MockShorebirdValidator(); + shorebirdVersionManager = _MockShorebirdVersionManager(); + + when(() => shorebirdEnv.getShorebirdYaml()).thenReturn(shorebirdYaml); + when(() => shorebirdEnv.shorebirdRoot).thenReturn(shorebirdRoot); - when(() => platform.script).thenReturn( - Uri.file( - p.join( - shorebirdRoot.path, - 'bin', - 'cache', - 'shorebird.snapshot', - ), - ), - ); when( () => shorebirdProcess.run( 'flutter', @@ -182,14 +169,7 @@ flutter: runInShell: any(named: 'runInShell'), ), ).thenAnswer((_) async => flutterBuildProcessResult); - when( - () => shorebirdProcess.run( - 'git', - any(), - runInShell: any(named: 'runInShell'), - workingDirectory: any(named: 'workingDirectory'), - ), - ).thenAnswer((_) async => flutterRevisionProcessResult); + when(() => argResults.rest).thenReturn([]); when(() => argResults['arch']).thenReturn(arch); when(() => argResults['platform']).thenReturn(releasePlatform); @@ -208,12 +188,6 @@ flutter: when( () => flutterBuildProcessResult.exitCode, ).thenReturn(ExitCode.success.code); - when( - () => flutterRevisionProcessResult.exitCode, - ).thenReturn(ExitCode.success.code); - when( - () => flutterRevisionProcessResult.stdout, - ).thenReturn(flutterRevision); when( () => codePushClientWrapper.getApp(appId: any(named: 'appId')), ).thenAnswer((_) async => appMetadata); @@ -273,6 +247,9 @@ flutter: supportedOperatingSystems: any(named: 'supportedOperatingSystems'), ), ).thenAnswer((_) async {}); + when( + () => shorebirdVersionManager.fetchCurrentGitHash(), + ).thenAnswer((_) async => flutterRevision); command = runWithOverrides(ReleaseAndroidCommand.new) ..testArgResults = argResults; @@ -307,13 +284,7 @@ flutter: test('exits with code 70 when building fails', () async { when(() => flutterBuildProcessResult.exitCode).thenReturn(1); when(() => flutterBuildProcessResult.stderr).thenReturn('oops'); - - final tempDir = setUpTempDir(); - final exitCode = await IOOverrides.runZoned( - () => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, - ); - + final exitCode = await runWithOverrides(command.run); expect(exitCode, equals(ExitCode.software.code)); }); @@ -322,13 +293,7 @@ flutter: 'Failed to extract version name from app bundle: oops', ); when(() => bundletool.getVersionName(any())).thenThrow(exception); - final tempDir = setUpTempDir(); - - final exitCode = await IOOverrides.runZoned( - () => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, - ); - + final exitCode = await runWithOverrides(command.run); expect(exitCode, ExitCode.software.code); verify(() => progress.fail('$exception')).called(1); }); @@ -338,13 +303,7 @@ flutter: 'Failed to extract version code from app bundle: oops', ); when(() => bundletool.getVersionCode(any())).thenThrow(exception); - final tempDir = setUpTempDir(); - - final exitCode = await IOOverrides.runZoned( - () => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, - ); - + final exitCode = await runWithOverrides(command.run); expect(exitCode, ExitCode.software.code); verify(() => progress.fail('$exception')).called(1); }); @@ -357,13 +316,7 @@ flutter: defaultValue: any(named: 'defaultValue'), ), ).thenAnswer((_) => '1.0.0'); - final tempDir = setUpTempDir(); - - final exitCode = await IOOverrides.runZoned( - () => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, - ); - + final exitCode = await runWithOverrides(command.run); expect(exitCode, ExitCode.success.code); verify(() => logger.info('Aborting.')).called(1); verifyNever( @@ -377,22 +330,13 @@ flutter: }); test('throws error when unable to detect flutter revision', () async { - const error = 'oops'; - when(() => flutterRevisionProcessResult.exitCode).thenReturn(1); - when(() => flutterRevisionProcessResult.stderr).thenReturn(error); - final tempDir = setUpTempDir(); - - final exitCode = await IOOverrides.runZoned( - () => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, - ); - + final exception = Exception('oops'); + when( + () => shorebirdVersionManager.fetchCurrentGitHash(), + ).thenThrow(exception); + final exitCode = await runWithOverrides(command.run); expect(exitCode, ExitCode.software.code); - verify( - () => progress.fail( - 'Exception: Unable to determine flutter revision: $error', - ), - ).called(1); + verify(() => progress.fail('$exception')).called(1); }); test( @@ -400,13 +344,7 @@ flutter: 'when --release-version and --force are used', () async { when(() => argResults['force']).thenReturn(true); when(() => argResults['release-version']).thenReturn(version); - final tempDir = setUpTempDir(); - - final exitCode = await IOOverrides.runZoned( - () => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, - ); - + final exitCode = await runWithOverrides(command.run); verify(() => logger.success('\nāœ… Published Release!')).called(1); expect(exitCode, ExitCode.success.code); verifyNever( @@ -415,13 +353,7 @@ flutter: }); test('succeeds when release is successful', () async { - final tempDir = setUpTempDir(); - - final exitCode = await IOOverrides.runZoned( - () => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, - ); - + final exitCode = await runWithOverrides(command.run); verify(() => logger.success('\nāœ… Published Release!')).called(1); verify( () => codePushClientWrapper.createAndroidReleaseArtifacts( @@ -445,13 +377,7 @@ flutter: test('succeeds when release is successful (with apk)', () async { when(() => argResults['artifact']).thenReturn('apk'); - final tempDir = setUpTempDir(); - - final exitCode = await IOOverrides.runZoned( - () => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, - ); - + final exitCode = await runWithOverrides(command.run); verify(() => logger.success('\nāœ… Published Release!')).called(1); verify( () => codePushClientWrapper.createAndroidReleaseArtifacts( @@ -480,18 +406,12 @@ flutter: final target = p.join('lib', 'main_development.dart'); when(() => argResults['flavor']).thenReturn(flavor); when(() => argResults['target']).thenReturn(target); - final tempDir = setUpTempDir(); - File( - p.join(tempDir.path, 'shorebird.yaml'), - ).writeAsStringSync(''' -app_id: productionAppId -flavors: - development: $appId'''); - - final exitCode = await IOOverrides.runZoned( - () => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, + const shorebirdYaml = ShorebirdYaml( + appId: 'productionAppId', + flavors: {flavor: appId}, ); + when(() => shorebirdEnv.getShorebirdYaml()).thenReturn(shorebirdYaml); + final exitCode = await runWithOverrides(command.run); verify(() => logger.success('\nāœ… Published Release!')).called(1); verify( @@ -523,13 +443,7 @@ flavors: releaseVersion: any(named: 'releaseVersion'), ), ).thenAnswer((_) async => release); - final tempDir = setUpTempDir(); - - final exitCode = await IOOverrides.runZoned( - () => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, - ); - + final exitCode = await runWithOverrides(command.run); expect(exitCode, ExitCode.success.code); verifyNever( () => codePushClientWrapper.createRelease( diff --git a/packages/shorebird_cli/test/src/commands/release/release_ios_command_test.dart b/packages/shorebird_cli/test/src/commands/release/release_ios_command_test.dart index 94c265ad..93549d19 100644 --- a/packages/shorebird_cli/test/src/commands/release/release_ios_command_test.dart +++ b/packages/shorebird_cli/test/src/commands/release/release_ios_command_test.dart @@ -11,11 +11,14 @@ import 'package:shorebird_cli/src/archive_analysis/archive_analysis.dart'; import 'package:shorebird_cli/src/auth/auth.dart'; import 'package:shorebird_cli/src/code_push_client_wrapper.dart'; import 'package:shorebird_cli/src/commands/commands.dart'; +import 'package:shorebird_cli/src/config/config.dart'; import 'package:shorebird_cli/src/doctor.dart'; import 'package:shorebird_cli/src/logger.dart'; import 'package:shorebird_cli/src/platform.dart'; import 'package:shorebird_cli/src/process.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; import 'package:shorebird_cli/src/shorebird_validator.dart'; +import 'package:shorebird_cli/src/shorebird_version_manager.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'; @@ -46,8 +49,13 @@ class _MockShorebirdFlutterValidator extends Mock class _MockShorebirdProcess extends Mock implements ShorebirdProcess {} +class _MockShorebirdEnv extends Mock implements ShorebirdEnv {} + class _MockShorebirdValidator extends Mock implements ShorebirdValidator {} +class _MockShorebirdVersionManager extends Mock + implements ShorebirdVersionManager {} + class _FakeRelease extends Fake implements Release {} class _FakeShorebirdProcess extends Fake implements ShorebirdProcess {} @@ -55,6 +63,7 @@ class _FakeShorebirdProcess extends Fake implements ShorebirdProcess {} void main() { group(ReleaseIosCommand, () { const appId = 'test-app-id'; + const shorebirdYaml = ShorebirdYaml(appId: appId); const flutterRevision = '83305b5088e6fe327fb3334a73ff190828d85713'; const versionName = '1.2.3'; const versionCode = '1'; @@ -102,10 +111,11 @@ flutter: late Progress progress; late Logger logger; late ShorebirdProcessResult flutterBuildProcessResult; - late ShorebirdProcessResult flutterRevisionProcessResult; late ShorebirdFlutterValidator flutterValidator; late ShorebirdProcess shorebirdProcess; + late ShorebirdEnv shorebirdEnv; late ShorebirdValidator shorebirdValidator; + late ShorebirdVersionManager shorebirdVersionManager; late ReleaseIosCommand command; R runWithOverrides(R Function() body) { @@ -118,7 +128,11 @@ flutter: loggerRef.overrideWith(() => logger), platformRef.overrideWith(() => platform), processRef.overrideWith(() => shorebirdProcess), + shorebirdEnvRef.overrideWith(() => shorebirdEnv), shorebirdValidatorRef.overrideWith(() => shorebirdValidator), + shorebirdVersionManagerRef.overrideWith( + () => shorebirdVersionManager, + ), }, ); } @@ -159,21 +173,14 @@ flutter: progress = _MockProgress(); logger = _MockLogger(); flutterBuildProcessResult = _MockProcessResult(); - flutterRevisionProcessResult = _MockProcessResult(); flutterValidator = _MockShorebirdFlutterValidator(); shorebirdProcess = _MockShorebirdProcess(); + shorebirdEnv = _MockShorebirdEnv(); shorebirdValidator = _MockShorebirdValidator(); + shorebirdVersionManager = _MockShorebirdVersionManager(); - when(() => platform.script).thenReturn( - Uri.file( - p.join( - shorebirdRoot.path, - 'bin', - 'cache', - 'shorebird.snapshot', - ), - ), - ); + when(() => shorebirdEnv.getShorebirdYaml()).thenReturn(shorebirdYaml); + when(() => shorebirdEnv.shorebirdRoot).thenReturn(shorebirdRoot); when( () => shorebirdProcess.run( 'flutter', @@ -181,14 +188,6 @@ flutter: runInShell: any(named: 'runInShell'), ), ).thenAnswer((_) async => flutterBuildProcessResult); - when( - () => shorebirdProcess.run( - 'git', - any(), - runInShell: any(named: 'runInShell'), - workingDirectory: any(named: 'workingDirectory'), - ), - ).thenAnswer((_) async => flutterRevisionProcessResult); when(() => argResults.rest).thenReturn([]); when(() => argResults['arch']).thenReturn(arch); when(() => argResults['platform']).thenReturn(releasePlatform); @@ -204,12 +203,6 @@ flutter: when( () => flutterBuildProcessResult.exitCode, ).thenReturn(ExitCode.success.code); - when( - () => flutterRevisionProcessResult.exitCode, - ).thenReturn(ExitCode.success.code); - when( - () => flutterRevisionProcessResult.stdout, - ).thenReturn(flutterRevision); when( () => codePushClientWrapper.getApp(appId: any(named: 'appId')), ).thenAnswer((_) async => appMetadata); @@ -259,6 +252,9 @@ flutter: supportedOperatingSystems: any(named: 'supportedOperatingSystems'), ), ).thenAnswer((_) async {}); + when( + () => shorebirdVersionManager.fetchCurrentGitHash(), + ).thenAnswer((_) async => flutterRevision); command = runWithOverrides(() => ReleaseIosCommand(ipaReader: ipaReader)) ..testArgResults = argResults; @@ -455,9 +451,10 @@ error: exportArchive: No signing certificate "iOS Distribution" found }); test('throws error when unable to detect flutter revision', () async { - const error = 'oops'; - when(() => flutterRevisionProcessResult.exitCode).thenReturn(1); - when(() => flutterRevisionProcessResult.stderr).thenReturn(error); + final exception = Exception('oops'); + when( + () => shorebirdVersionManager.fetchCurrentGitHash(), + ).thenThrow(exception); final tempDir = setUpTempDir(); final exitCode = await IOOverrides.runZoned( @@ -465,11 +462,7 @@ error: exportArchive: No signing certificate "iOS Distribution" found getCurrentDirectory: () => tempDir, ); expect(exitCode, ExitCode.software.code); - verify( - () => progress.fail( - 'Exception: Unable to determine flutter revision: $error', - ), - ).called(1); + verify(() => progress.fail('$exception')).called(1); }); test( diff --git a/packages/shorebird_cli/test/src/commands/release/release_ios_framework_command_test.dart b/packages/shorebird_cli/test/src/commands/release/release_ios_framework_command_test.dart index 9acdac40..6d360eff 100644 --- a/packages/shorebird_cli/test/src/commands/release/release_ios_framework_command_test.dart +++ b/packages/shorebird_cli/test/src/commands/release/release_ios_framework_command_test.dart @@ -9,11 +9,14 @@ import 'package:scoped/scoped.dart'; import 'package:shorebird_cli/src/auth/auth.dart'; import 'package:shorebird_cli/src/code_push_client_wrapper.dart'; import 'package:shorebird_cli/src/commands/release/release.dart'; +import 'package:shorebird_cli/src/config/config.dart'; import 'package:shorebird_cli/src/doctor.dart'; import 'package:shorebird_cli/src/logger.dart'; import 'package:shorebird_cli/src/platform.dart'; import 'package:shorebird_cli/src/process.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; import 'package:shorebird_cli/src/shorebird_validator.dart'; +import 'package:shorebird_cli/src/shorebird_version_manager.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'; @@ -40,8 +43,13 @@ class _MockShorebirdFlutterValidator extends Mock class _MockShorebirdProcess extends Mock implements ShorebirdProcess {} +class _MockShorebirdEnv extends Mock implements ShorebirdEnv {} + class _MockShorebirdValidator extends Mock implements ShorebirdValidator {} +class _MockShorebirdVersionManager extends Mock + implements ShorebirdVersionManager {} + class _FakeRelease extends Fake implements Release {} class _FakeShorebirdProcess extends Fake implements ShorebirdProcess {} @@ -49,6 +57,7 @@ class _FakeShorebirdProcess extends Fake implements ShorebirdProcess {} void main() { group(ReleaseIosFrameworkCommand, () { const appId = 'test-app-id'; + const shorebirdYaml = ShorebirdYaml(appId: appId); const flutterRevision = '83305b5088e6fe327fb3334a73ff190828d85713'; const versionName = '1.2.3'; const versionCode = '1'; @@ -83,10 +92,11 @@ flutter: late Progress progress; late Logger logger; late ShorebirdProcessResult flutterBuildProcessResult; - late ShorebirdProcessResult flutterRevisionProcessResult; late ShorebirdFlutterValidator flutterValidator; late ShorebirdProcess shorebirdProcess; + late ShorebirdEnv shorebirdEnv; late ShorebirdValidator shorebirdValidator; + late ShorebirdVersionManager shorebirdVersionManager; late ReleaseIosFrameworkCommand command; R runWithOverrides(R Function() body) { @@ -99,7 +109,11 @@ flutter: loggerRef.overrideWith(() => logger), platformRef.overrideWith(() => platform), processRef.overrideWith(() => shorebirdProcess), + shorebirdEnvRef.overrideWith(() => shorebirdEnv), shorebirdValidatorRef.overrideWith(() => shorebirdValidator), + shorebirdVersionManagerRef.overrideWith( + () => shorebirdVersionManager, + ), }, ); } @@ -132,21 +146,14 @@ flutter: progress = _MockProgress(); logger = _MockLogger(); flutterBuildProcessResult = _MockProcessResult(); - flutterRevisionProcessResult = _MockProcessResult(); flutterValidator = _MockShorebirdFlutterValidator(); shorebirdProcess = _MockShorebirdProcess(); + shorebirdEnv = _MockShorebirdEnv(); shorebirdValidator = _MockShorebirdValidator(); + shorebirdVersionManager = _MockShorebirdVersionManager(); - when(() => platform.script).thenReturn( - Uri.file( - p.join( - shorebirdRoot.path, - 'bin', - 'cache', - 'shorebird.snapshot', - ), - ), - ); + when(() => shorebirdEnv.getShorebirdYaml()).thenReturn(shorebirdYaml); + when(() => shorebirdEnv.shorebirdRoot).thenReturn(shorebirdRoot); when( () => shorebirdProcess.run( 'flutter', @@ -154,14 +161,6 @@ flutter: runInShell: any(named: 'runInShell'), ), ).thenAnswer((_) async => flutterBuildProcessResult); - when( - () => shorebirdProcess.run( - 'git', - any(), - runInShell: any(named: 'runInShell'), - workingDirectory: any(named: 'workingDirectory'), - ), - ).thenAnswer((_) async => flutterRevisionProcessResult); when(() => argResults['release-version']).thenReturn(version); when(() => argResults['force']).thenReturn(false); when(() => argResults.rest).thenReturn([]); @@ -170,12 +169,6 @@ flutter: when( () => flutterBuildProcessResult.exitCode, ).thenReturn(ExitCode.success.code); - when( - () => flutterRevisionProcessResult.exitCode, - ).thenReturn(ExitCode.success.code); - when( - () => flutterRevisionProcessResult.stdout, - ).thenReturn(flutterRevision); when(() => logger.progress(any())).thenReturn(progress); when(() => logger.confirm(any())).thenReturn(true); when(() => platform.operatingSystem).thenReturn(Platform.macOS); @@ -225,6 +218,9 @@ flutter: supportedOperatingSystems: any(named: 'supportedOperatingSystems'), ), ).thenAnswer((_) async {}); + when( + () => shorebirdVersionManager.fetchCurrentGitHash(), + ).thenAnswer((_) async => flutterRevision); command = runWithOverrides(ReleaseIosFrameworkCommand.new) ..testArgResults = argResults; @@ -319,9 +315,10 @@ flutter: }); test('throws error when unable to detect flutter revision', () async { - const error = 'oops'; - when(() => flutterRevisionProcessResult.exitCode).thenReturn(1); - when(() => flutterRevisionProcessResult.stderr).thenReturn(error); + final exception = Exception('oops'); + when( + () => shorebirdVersionManager.fetchCurrentGitHash(), + ).thenThrow(exception); final tempDir = setUpTempDir(); final exitCode = await IOOverrides.runZoned( @@ -329,11 +326,7 @@ flutter: getCurrentDirectory: () => tempDir, ); expect(exitCode, ExitCode.software.code); - verify( - () => progress.fail( - 'Exception: Unable to determine flutter revision: $error', - ), - ).called(1); + verify(() => progress.fail('$exception')).called(1); }); test( diff --git a/packages/shorebird_cli/test/src/commands/releases/delete_releases_command_test.dart b/packages/shorebird_cli/test/src/commands/releases/delete_releases_command_test.dart index 70909121..76b4f11e 100644 --- a/packages/shorebird_cli/test/src/commands/releases/delete_releases_command_test.dart +++ b/packages/shorebird_cli/test/src/commands/releases/delete_releases_command_test.dart @@ -1,55 +1,45 @@ -import 'dart:io'; - import 'package:args/args.dart'; -import 'package:http/http.dart' as http; 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/auth/auth.dart'; +import 'package:shorebird_cli/src/code_push_client_wrapper.dart'; import 'package:shorebird_cli/src/commands/releases/releases.dart'; +import 'package:shorebird_cli/src/config/config.dart'; import 'package:shorebird_cli/src/logger.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; import 'package:shorebird_cli/src/shorebird_validator.dart'; import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; import 'package:test/test.dart'; class _MockArgResults extends Mock implements ArgResults {} -class _MockAuth extends Mock implements Auth {} +class _MockCodePushClientWrapper extends Mock + implements CodePushClientWrapper {} class _MockCodePushClient extends Mock implements CodePushClient {} -class _MockHttpClient extends Mock implements http.Client {} - class _MockLogger extends Mock implements Logger {} class _MockProgress extends Mock implements Progress {} +class _MockShorebirdEnv extends Mock implements ShorebirdEnv {} + class _MockShorebirdValidator extends Mock implements ShorebirdValidator {} void main() { group(DeleteReleasesCommand, () { const appId = 'test-app-id'; + const shorebirdYaml = ShorebirdYaml(appId: appId); const flutterRevision = '83305b5088e6fe327fb3334a73ff190828d85713'; const releaseId = 3; const versionNumber = '1.0.0'; - const pubspecYamlContent = ''' -name: example -version: 1.0.1 -environment: - sdk: ">=2.19.0 <3.0.0" - -flutter: - assets: - - shorebird.yaml'''; - late ArgResults argResults; - late http.Client httpClient; - late Auth auth; late Logger logger; + late CodePushClientWrapper codePushClientWrapper; late CodePushClient codePushClient; late Progress progress; + late ShorebirdEnv shorebirdEnv; late ShorebirdValidator shorebirdValidator; late DeleteReleasesCommand command; @@ -57,38 +47,28 @@ flutter: return runScoped( body, values: { - authRef.overrideWith(() => auth), + codePushClientWrapperRef.overrideWith(() => codePushClientWrapper), loggerRef.overrideWith(() => logger), + shorebirdEnvRef.overrideWith(() => shorebirdEnv), shorebirdValidatorRef.overrideWith(() => shorebirdValidator), }, ); } - Directory setUpTempDir() { - final tempDir = Directory.systemTemp.createTempSync(); - File( - p.join(tempDir.path, 'pubspec.yaml'), - ).writeAsStringSync(pubspecYamlContent); - File( - p.join(tempDir.path, 'shorebird.yaml'), - ).writeAsStringSync('app_id: $appId'); - return tempDir; - } - setUp(() { argResults = _MockArgResults(); - httpClient = _MockHttpClient(); - auth = _MockAuth(); logger = _MockLogger(); + codePushClientWrapper = _MockCodePushClientWrapper(); codePushClient = _MockCodePushClient(); progress = _MockProgress(); + shorebirdEnv = _MockShorebirdEnv(); shorebirdValidator = _MockShorebirdValidator(); when(() => argResults['version']).thenReturn(versionNumber); - - when(() => auth.isAuthenticated).thenReturn(true); - when(() => auth.client).thenReturn(httpClient); - + when( + () => codePushClientWrapper.codePushClient, + ).thenReturn(codePushClient); + when(() => shorebirdEnv.getShorebirdYaml()).thenReturn(shorebirdYaml); when(() => codePushClient.getReleases(appId: any(named: 'appId'))) .thenAnswer( (_) async => [ @@ -129,16 +109,8 @@ flutter: ), ).thenAnswer((_) async {}); - command = runWithOverrides( - () => DeleteReleasesCommand( - buildCodePushClient: ({ - required http.Client httpClient, - Uri? hostedUri, - }) { - return codePushClient; - }, - ), - )..testArgResults = argResults; + command = runWithOverrides(DeleteReleasesCommand.new) + ..testArgResults = argResults; }); test('returns correct description', () { @@ -172,11 +144,7 @@ flutter: when(() => argResults['version']).thenReturn(null); when(() => logger.prompt(any())).thenReturn(versionNumber); - final tempDir = setUpTempDir(); - await IOOverrides.runZoned( - () => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, - ); + await runWithOverrides(command.run); verify( () => logger.prompt( @@ -187,11 +155,7 @@ flutter: test('does not prompt for version if user provides it with a flag', () async { - final tempDir = setUpTempDir(); - await IOOverrides.runZoned( - () => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, - ); + await runWithOverrides(command.run); verifyNever(() => logger.prompt(any())); }); @@ -200,11 +164,7 @@ flutter: () => codePushClient.getReleases(appId: any(named: 'appId')), ).thenThrow(Exception('oops')); - final tempDir = setUpTempDir(); - final exitCode = await IOOverrides.runZoned( - () => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, - ); + final exitCode = await runWithOverrides(command.run); expect(exitCode, ExitCode.software.code); }); @@ -212,11 +172,7 @@ flutter: test('aborts when user does not confirm', () async { when(() => logger.confirm(any())).thenReturn(false); - final tempDir = setUpTempDir(); - final exitCode = await IOOverrides.runZoned( - () => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, - ); + final exitCode = await runWithOverrides(command.run); expect(exitCode, ExitCode.success.code); verifyNever( @@ -231,11 +187,7 @@ flutter: test('returns software error when release is not found', () async { when(() => argResults['version']).thenReturn('asdf'); - final tempDir = setUpTempDir(); - final exitCode = await IOOverrides.runZoned( - () => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, - ); + final exitCode = await runWithOverrides(command.run); expect(exitCode, ExitCode.software.code); verify(() => logger.err('No release found for version "asdf"')).called(1); @@ -255,11 +207,7 @@ flutter: ), ).thenThrow(Exception('oops')); - final tempDir = setUpTempDir(); - final exitCode = await IOOverrides.runZoned( - () => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, - ); + final exitCode = await runWithOverrides(command.run); expect(exitCode, ExitCode.software.code); verify(() => progress.fail(any(that: contains('oops')))).called(1); @@ -276,11 +224,7 @@ flutter: ), ).thenAnswer((_) async {}); - final tempDir = setUpTempDir(); - final exitCode = await IOOverrides.runZoned( - () => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, - ); + final exitCode = await runWithOverrides(command.run); expect(exitCode, ExitCode.success.code); verify( @@ -294,13 +238,11 @@ flutter: test('uses correct app_id when flavor is specified', () async { const flavor = 'development'; when(() => argResults['flavor']).thenReturn(flavor); - final tempDir = setUpTempDir(); - File( - p.join(tempDir.path, 'shorebird.yaml'), - ).writeAsStringSync(''' -app_id: productionAppId -flavors: - $flavor: $appId'''); + const shorebirdYaml = ShorebirdYaml( + appId: 'productionAppId', + flavors: {flavor: appId}, + ); + when(() => shorebirdEnv.getShorebirdYaml()).thenReturn(shorebirdYaml); when( () => codePushClient.deleteRelease( appId: any(named: 'appId'), @@ -308,10 +250,7 @@ flavors: ), ).thenAnswer((_) async {}); - final exitCode = await IOOverrides.runZoned( - () => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, - ); + final exitCode = await runWithOverrides(command.run); expect(exitCode, ExitCode.success.code); verify(() => codePushClient.getReleases(appId: appId)).called(1); diff --git a/packages/shorebird_cli/test/src/commands/releases/list_releases_command_test.dart b/packages/shorebird_cli/test/src/commands/releases/list_releases_command_test.dart index 7f3e1029..764ff311 100644 --- a/packages/shorebird_cli/test/src/commands/releases/list_releases_command_test.dart +++ b/packages/shorebird_cli/test/src/commands/releases/list_releases_command_test.dart @@ -1,86 +1,67 @@ -import 'dart:io'; - import 'package:args/args.dart'; -import 'package:http/http.dart' as http; 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/auth/auth.dart'; +import 'package:shorebird_cli/src/code_push_client_wrapper.dart'; import 'package:shorebird_cli/src/commands/commands.dart'; +import 'package:shorebird_cli/src/config/config.dart'; import 'package:shorebird_cli/src/logger.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; import 'package:shorebird_cli/src/shorebird_validator.dart'; import 'package:shorebird_code_push_client/shorebird_code_push_client.dart'; import 'package:test/test.dart'; class _MockArgResults extends Mock implements ArgResults {} -class _MockAuth extends Mock implements Auth {} +class _MockCodePushClientWrapper extends Mock + implements CodePushClientWrapper {} class _MockCodePushClient extends Mock implements CodePushClient {} -class _MockHttpClient extends Mock implements http.Client {} - class _MockLogger extends Mock implements Logger {} +class _MockShorebirdEnv extends Mock implements ShorebirdEnv {} + class _MockShorebirdValidator extends Mock implements ShorebirdValidator {} void main() { group(ListReleasesCommand, () { const appId = 'test-app-id'; + const shorebirdYaml = ShorebirdYaml(appId: appId); const flutterRevision = '83305b5088e6fe327fb3334a73ff190828d85713'; late ArgResults argResults; - late Auth auth; - late http.Client httpClient; + late CodePushClientWrapper codePushClientWrapper; late CodePushClient codePushClient; late Logger logger; + late ShorebirdEnv shorebirdEnv; late ShorebirdValidator shorebirdValidator; late ListReleasesCommand command; - const pubspecYamlContent = ''' -name: example -version: 1.0.1 -environment: - sdk: ">=2.19.0 <3.0.0" - -flutter: - assets: - - shorebird.yaml'''; - R runWithOverrides(R Function() body) { return runScoped( body, values: { - authRef.overrideWith(() => auth), + codePushClientWrapperRef.overrideWith(() => codePushClientWrapper), loggerRef.overrideWith(() => logger), + shorebirdEnvRef.overrideWith(() => shorebirdEnv), shorebirdValidatorRef.overrideWith(() => shorebirdValidator), }, ); } - Directory setUpTempDir() { - final tempDir = Directory.systemTemp.createTempSync(); - File( - p.join(tempDir.path, 'pubspec.yaml'), - ).writeAsStringSync(pubspecYamlContent); - File( - p.join(tempDir.path, 'shorebird.yaml'), - ).writeAsStringSync('app_id: $appId'); - return tempDir; - } - setUp(() { argResults = _MockArgResults(); - auth = _MockAuth(); + codePushClientWrapper = _MockCodePushClientWrapper(); codePushClient = _MockCodePushClient(); - httpClient = _MockHttpClient(); logger = _MockLogger(); + shorebirdEnv = _MockShorebirdEnv(); shorebirdValidator = _MockShorebirdValidator(); - when(() => auth.client).thenReturn(httpClient); - when(() => auth.isAuthenticated).thenReturn(true); - + when( + () => codePushClientWrapper.codePushClient, + ).thenReturn(codePushClient); + when(() => shorebirdEnv.getShorebirdYaml()).thenReturn(shorebirdYaml); when( () => shorebirdValidator.validatePreconditions( checkUserIsAuthenticated: any(named: 'checkUserIsAuthenticated'), @@ -88,13 +69,8 @@ flutter: ), ).thenAnswer((_) async {}); - command = runWithOverrides( - () => ListReleasesCommand( - buildCodePushClient: ({required httpClient, hostedUri}) { - return codePushClient; - }, - ), - )..testArgResults = argResults; + command = runWithOverrides(ListReleasesCommand.new) + ..testArgResults = argResults; }); test('description is correct', () { @@ -125,25 +101,18 @@ flutter: when( () => codePushClient.getReleases(appId: any(named: 'appId')), ).thenThrow(Exception()); - final tempDir = setUpTempDir(); - final exitCode = await IOOverrides.runZoned( - () => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, - ); + final exitCode = await runWithOverrides(command.run); expect(exitCode, ExitCode.software.code); }); test('returns ExitCode.success when releases is empty', () async { - when(() => codePushClient.getReleases(appId: appId)) - .thenAnswer((_) async => []); - final tempDir = setUpTempDir(); + when( + () => codePushClient.getReleases(appId: appId), + ).thenAnswer((_) async => []); - final exitCode = await IOOverrides.runZoned( - () => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, - ); + final exitCode = await runWithOverrides(command.run); expect(exitCode, ExitCode.success.code); verify(() => logger.info('(empty)')).called(1); @@ -152,13 +121,11 @@ flutter: test('uses correct app_id when flavor is specified', () async { const flavor = 'development'; when(() => argResults['flavor']).thenReturn(flavor); - final tempDir = setUpTempDir(); - File( - p.join(tempDir.path, 'shorebird.yaml'), - ).writeAsStringSync(''' -app_id: productionAppId -flavors: - $flavor: $appId'''); + const shorebirdYaml = ShorebirdYaml( + appId: 'productionAppId', + flavors: {flavor: appId}, + ); + when(() => shorebirdEnv.getShorebirdYaml()).thenReturn(shorebirdYaml); when(() => codePushClient.getReleases(appId: appId)).thenAnswer( (_) async => [ const Release( @@ -172,10 +139,7 @@ flavors: ], ); - final exitCode = await IOOverrides.runZoned( - () => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, - ); + final exitCode = await runWithOverrides(command.run); expect(exitCode, ExitCode.success.code); verify( @@ -190,8 +154,6 @@ flavors: test('returns ExitCode.success and prints releases when releases exist', () async { - final tempDir = setUpTempDir(); - when(() => codePushClient.getReleases(appId: appId)).thenAnswer( (_) async => [ const Release( @@ -213,10 +175,7 @@ flavors: ], ); - final exitCode = await IOOverrides.runZoned( - () => runWithOverrides(command.run), - getCurrentDirectory: () => tempDir, - ); + final exitCode = await runWithOverrides(command.run); expect(exitCode, ExitCode.success.code); verify( diff --git a/packages/shorebird_cli/test/src/commands/run_command_test.dart b/packages/shorebird_cli/test/src/commands/run_command_test.dart index a4944620..745c3220 100644 --- a/packages/shorebird_cli/test/src/commands/run_command_test.dart +++ b/packages/shorebird_cli/test/src/commands/run_command_test.dart @@ -3,26 +3,19 @@ import 'dart:convert'; import 'dart:io'; import 'package:args/args.dart'; -import 'package:http/http.dart' as http; import 'package:mason_logger/mason_logger.dart'; import 'package:mocktail/mocktail.dart'; import 'package:scoped/scoped.dart'; -import 'package:shorebird_cli/src/auth/auth.dart'; import 'package:shorebird_cli/src/commands/run_command.dart'; import 'package:shorebird_cli/src/doctor.dart'; import 'package:shorebird_cli/src/logger.dart'; import 'package:shorebird_cli/src/process.dart'; import 'package:shorebird_cli/src/shorebird_validator.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'; class _MockArgResults extends Mock implements ArgResults {} -class _MockHttpClient extends Mock implements http.Client {} - -class _MockAuth extends Mock implements Auth {} - class _MockDoctor extends Mock implements Doctor {} class _MockLogger extends Mock implements Logger {} @@ -31,8 +24,6 @@ class _MockProgress extends Mock implements Progress {} class _MockProcess extends Mock implements Process {} -class _MockCodePushClient extends Mock implements CodePushClient {} - class _MockShorebirdProcess extends Mock implements ShorebirdProcess {} class _MockIOSink extends Mock implements IOSink {} @@ -44,12 +35,9 @@ class _MockValidator extends Mock implements Validator {} void main() { group(RunCommand, () { late ArgResults argResults; - late http.Client httpClient; - late Auth auth; late Doctor doctor; late Logger logger; late Process process; - late CodePushClient codePushClient; late ShorebirdProcess shorebirdProcess; late IOSink ioSink; late ShorebirdValidator shorebirdValidator; @@ -60,7 +48,6 @@ void main() { return runScoped( body, values: { - authRef.overrideWith(() => auth), doctorRef.overrideWith(() => doctor), loggerRef.overrideWith(() => logger), processRef.overrideWith(() => shorebirdProcess), @@ -75,13 +62,10 @@ void main() { setUp(() { argResults = _MockArgResults(); - httpClient = _MockHttpClient(); - auth = _MockAuth(); doctor = _MockDoctor(); logger = _MockLogger(); process = _MockProcess(); shorebirdProcess = _MockShorebirdProcess(); - codePushClient = _MockCodePushClient(); ioSink = _MockIOSink(); shorebirdValidator = _MockShorebirdValidator(); validator = _MockValidator(); @@ -94,8 +78,6 @@ void main() { ), ).thenAnswer((_) async => process); when(() => argResults.rest).thenReturn([]); - when(() => auth.isAuthenticated).thenReturn(true); - when(() => auth.client).thenReturn(httpClient); when(() => doctor.allValidators).thenReturn([validator]); when(() => logger.progress(any())).thenReturn(_MockProgress()); when(() => ioSink.addStream(any())).thenAnswer((_) async {}); @@ -106,16 +88,7 @@ void main() { ), ).thenAnswer((_) async {}); - command = runWithOverrides( - () => RunCommand( - buildCodePushClient: ({ - required http.Client httpClient, - Uri? hostedUri, - }) { - return codePushClient; - }, - ), - )..testArgResults = argResults; + command = runWithOverrides(RunCommand.new)..testArgResults = argResults; }); test('command is hidden', () { diff --git a/packages/shorebird_cli/test/src/commands/upgrade_command_test.dart b/packages/shorebird_cli/test/src/commands/upgrade_command_test.dart index 36f1f09e..3a35a7e9 100644 --- a/packages/shorebird_cli/test/src/commands/upgrade_command_test.dart +++ b/packages/shorebird_cli/test/src/commands/upgrade_command_test.dart @@ -6,6 +6,7 @@ import 'package:scoped/scoped.dart'; import 'package:shorebird_cli/src/commands/commands.dart'; import 'package:shorebird_cli/src/logger.dart'; import 'package:shorebird_cli/src/process.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; import 'package:shorebird_cli/src/shorebird_version_manager.dart'; import 'package:test/test.dart'; @@ -17,6 +18,8 @@ class _MockProgress extends Mock implements Progress {} class _MockShorebirdProcess extends Mock implements ShorebirdProcess {} +class _MockShorebirdEnv extends Mock implements ShorebirdEnv {} + class _MockShorebirdVersionManager extends Mock implements ShorebirdVersionManager {} @@ -28,6 +31,7 @@ void main() { late Logger logger; late ShorebirdProcessResult pruneFlutterOriginResult; late ShorebirdProcess shorebirdProcess; + late ShorebirdEnv shorebirdEnv; late ShorebirdVersionManager shorebirdVersionManager; late UpgradeCommand command; @@ -37,8 +41,10 @@ void main() { values: { loggerRef.overrideWith(() => logger), processRef.overrideWith(() => shorebirdProcess), - shorebirdVersionManagerRef - .overrideWith(() => shorebirdVersionManager), + shorebirdEnvRef.overrideWith(() => shorebirdEnv), + shorebirdVersionManagerRef.overrideWith( + () => shorebirdVersionManager, + ), }, ); } @@ -50,9 +56,13 @@ void main() { logger = _MockLogger(); pruneFlutterOriginResult = _MockProcessResult(); shorebirdProcess = _MockShorebirdProcess(); + shorebirdEnv = _MockShorebirdEnv(); shorebirdVersionManager = _MockShorebirdVersionManager(); command = runWithOverrides(UpgradeCommand.new); + when( + () => shorebirdEnv.flutterDirectory, + ).thenReturn(Directory('flutter')); when( shorebirdVersionManager.fetchCurrentGitHash, ).thenAnswer((_) async => currentShorebirdRevision); diff --git a/packages/shorebird_cli/test/src/ios_deploy_test.dart b/packages/shorebird_cli/test/src/ios_deploy_test.dart index aee941b4..ebe18e16 100644 --- a/packages/shorebird_cli/test/src/ios_deploy_test.dart +++ b/packages/shorebird_cli/test/src/ios_deploy_test.dart @@ -11,6 +11,7 @@ import 'package:shorebird_cli/src/ios_deploy.dart'; import 'package:shorebird_cli/src/logger.dart'; import 'package:shorebird_cli/src/platform.dart'; import 'package:shorebird_cli/src/process.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; import 'package:test/test.dart'; class _MockLogger extends Mock implements Logger {} @@ -27,6 +28,8 @@ class _MockIOSink extends Mock implements IOSink {} class _MockProcessSignal extends Mock implements ProcessSignal {} +class _MockShorebirdEnv extends Mock implements ShorebirdEnv {} + void main() { group(IOSDeploy, () { late Logger logger; @@ -35,6 +38,7 @@ void main() { late ShorebirdProcess shorebirdProcess; late Process process; late IOSink ioSink; + late ShorebirdEnv shorebirdEnv; late IOSDeploy iosDeploy; R runWithOverrides(R Function() body) { @@ -44,6 +48,7 @@ void main() { loggerRef.overrideWith(() => logger), platformRef.overrideWith(() => platform), processRef.overrideWith(() => shorebirdProcess), + shorebirdEnvRef.overrideWith(() => shorebirdEnv), }, ); } @@ -55,14 +60,15 @@ void main() { process = _MockProcess(); progress = _MockProgress(); ioSink = _MockIOSink(); + shorebirdEnv = _MockShorebirdEnv(); iosDeploy = IOSDeploy(); final tempDir = Directory.systemTemp.createTempSync(); - final shorebirdScriptFile = File( - p.join(tempDir.path, 'bin', 'cache', 'shorebird.snapshot'), - )..create(recursive: true); - when(() => platform.script).thenReturn(shorebirdScriptFile.uri); + when(() => shorebirdEnv.shorebirdRoot).thenReturn(tempDir); + when(() => shorebirdEnv.flutterDirectory).thenReturn( + Directory(p.join(tempDir.path, 'bin', 'cache', 'flutter')), + ); when(() => logger.progress(any())).thenReturn(progress); when( () => shorebirdProcess.start(any(), any()), diff --git a/packages/shorebird_cli/test/src/shorebird_env_test.dart b/packages/shorebird_cli/test/src/shorebird_env_test.dart new file mode 100644 index 00000000..d178b6d2 --- /dev/null +++ b/packages/shorebird_cli/test/src/shorebird_env_test.dart @@ -0,0 +1,419 @@ +import 'dart:io'; + +import 'package:mocktail/mocktail.dart'; +import 'package:path/path.dart' as p; +import 'package:platform/platform.dart'; +import 'package:pubspec_parse/pubspec_parse.dart'; +import 'package:scoped/scoped.dart'; +import 'package:shorebird_cli/src/platform.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; +import 'package:test/test.dart'; + +class _MockPlatform extends Mock implements Platform {} + +void main() { + group(ShorebirdEnv, () { + late Platform platform; + late Directory shorebirdRoot; + late Uri platformScript; + late ShorebirdEnv shorebirdEnv; + + R runWithOverrides(R Function() body) { + return runScoped( + () => body(), + values: { + platformRef.overrideWith(() => platform), + }, + ); + } + + setUp(() { + shorebirdRoot = Directory.systemTemp.createTempSync(); + platformScript = Uri.file( + p.join(shorebirdRoot.path, 'bin', 'cache', 'shorebird.snapshot'), + ); + platform = _MockPlatform(); + shorebirdEnv = runWithOverrides(ShorebirdEnv.new); + + when(() => platform.environment).thenReturn(const {}); + when(() => platform.script).thenReturn(platformScript); + }); + + group('flutterBinaryFile', () { + test('returns correct path', () { + expect( + runWithOverrides(() => shorebirdEnv.flutterBinaryFile.path), + equals( + p.join( + shorebirdRoot.path, + 'bin', + 'cache', + 'flutter', + 'bin', + 'flutter', + ), + ), + ); + }); + }); + + group('genSnapshotFile', () { + test('returns correct path', () { + expect( + runWithOverrides(() => shorebirdEnv.genSnapshotFile.path), + equals( + p.join( + shorebirdRoot.path, + 'bin', + 'cache', + 'flutter', + 'bin', + 'cache', + 'artifacts', + 'engine', + 'ios-release', + 'gen_snapshot_arm64', + ), + ), + ); + }); + }); + + group('getPubspecYamlFile', () { + test('returns correct file', () { + final tempDir = Directory('temp'); + expect( + IOOverrides.runZoned( + () { + return runWithOverrides( + () => shorebirdEnv.getPubspecYamlFile().path, + ); + }, + getCurrentDirectory: () => tempDir, + ), + equals(p.join(tempDir.path, 'pubspec.yaml')), + ); + }); + }); + + group('getPubspecYaml', () { + test('returns null when pubspec.yaml does not exist', () { + final tempDir = Directory('temp'); + expect( + IOOverrides.runZoned( + () => runWithOverrides(() => shorebirdEnv.getPubspecYaml()), + getCurrentDirectory: () => tempDir, + ), + isNull, + ); + }); + + test('returns value when pubspec.yaml exists', () { + final tempDir = Directory.systemTemp.createTempSync(); + File( + p.join(tempDir.path, 'pubspec.yaml'), + ).writeAsStringSync('name: test'); + expect( + IOOverrides.runZoned( + () => runWithOverrides(() => shorebirdEnv.getPubspecYaml()), + getCurrentDirectory: () => tempDir, + ), + isA().having((p) => p.name, 'name', 'test'), + ); + }); + }); + + group('hasPubspecYaml', () { + test('returns false when pubspec.yaml does not exist', () { + final tempDir = Directory('temp'); + expect( + IOOverrides.runZoned( + () => runWithOverrides(() => shorebirdEnv.hasPubspecYaml), + getCurrentDirectory: () => tempDir, + ), + isFalse, + ); + }); + + test('returns true when pubspec.yaml does exist', () { + final tempDir = Directory.systemTemp.createTempSync(); + File( + p.join(tempDir.path, 'pubspec.yaml'), + ).writeAsStringSync('name: test'); + expect( + IOOverrides.runZoned( + () => runWithOverrides(() => shorebirdEnv.hasPubspecYaml), + getCurrentDirectory: () => tempDir, + ), + isTrue, + ); + }); + }); + + group('hasShorebirdYaml', () { + test('returns false when shorebird.yaml does not exist', () { + final tempDir = Directory('temp'); + expect( + IOOverrides.runZoned( + () => runWithOverrides(() => shorebirdEnv.hasShorebirdYaml), + getCurrentDirectory: () => tempDir, + ), + isFalse, + ); + }); + + test('returns true when shorebird.yaml does exist', () { + final tempDir = Directory.systemTemp.createTempSync(); + File( + p.join(tempDir.path, 'shorebird.yaml'), + ).writeAsStringSync('app_id: test-app-id'); + expect( + IOOverrides.runZoned( + () => runWithOverrides(() => shorebirdEnv.hasShorebirdYaml), + getCurrentDirectory: () => tempDir, + ), + isTrue, + ); + }); + }); + + group('pubspecContainsShorebirdYaml', () { + test( + 'returns false when pubspec.yaml does not ' + 'contain shorebird.yaml in assets', () { + final tempDir = Directory.systemTemp.createTempSync(); + File( + p.join(tempDir.path, 'pubspec.yaml'), + ).writeAsStringSync('name: test'); + expect( + IOOverrides.runZoned( + () => runWithOverrides( + () => shorebirdEnv.pubspecContainsShorebirdYaml, + ), + getCurrentDirectory: () => tempDir, + ), + isFalse, + ); + }); + + test( + 'returns true when pubspec.yaml does ' + 'contain shorebird.yaml in assets', () { + final tempDir = Directory.systemTemp.createTempSync(); + File( + p.join(tempDir.path, 'pubspec.yaml'), + ).writeAsStringSync(''' +name: test +flutter: + assets: + - shorebird.yaml +'''); + expect( + IOOverrides.runZoned( + () => runWithOverrides( + () => shorebirdEnv.pubspecContainsShorebirdYaml, + ), + getCurrentDirectory: () => tempDir, + ), + isTrue, + ); + }); + }); + + group('androidPackageName', () { + test('returns null when pubspec.yaml does not contain android module', + () { + final tempDir = Directory.systemTemp.createTempSync(); + File( + p.join(tempDir.path, 'pubspec.yaml'), + ).writeAsStringSync('name: test'); + expect( + IOOverrides.runZoned( + () => runWithOverrides(() => shorebirdEnv.androidPackageName), + getCurrentDirectory: () => tempDir, + ), + isNull, + ); + }); + + test( + 'returns correct package name when ' + 'pubspec.yaml contains android module', () { + final tempDir = Directory.systemTemp.createTempSync(); + File( + p.join(tempDir.path, 'pubspec.yaml'), + ).writeAsStringSync(''' +name: test +flutter: + module: + androidPackage: test-package +'''); + expect( + IOOverrides.runZoned( + () => runWithOverrides(() => shorebirdEnv.androidPackageName), + getCurrentDirectory: () => tempDir, + ), + equals('test-package'), + ); + }); + }); + + group('isShorebirdInitialized', () { + test('returns false when shorebird.yaml does not exist', () { + final tempDir = Directory('temp'); + expect( + IOOverrides.runZoned( + () => runWithOverrides(() => shorebirdEnv.isShorebirdInitialized), + getCurrentDirectory: () => tempDir, + ), + isFalse, + ); + }); + + test( + 'returns false when shorebird.yaml exists ' + 'but pubspec does not contain shorebird.yaml', () { + final tempDir = Directory.systemTemp.createTempSync(); + File( + p.join(tempDir.path, 'shorebird.yaml'), + ).writeAsStringSync('app_id: test-app-id'); + File( + p.join(tempDir.path, 'pubspec.yaml'), + ).writeAsStringSync('name: test'); + expect( + IOOverrides.runZoned( + () => runWithOverrides(() => shorebirdEnv.isShorebirdInitialized), + getCurrentDirectory: () => tempDir, + ), + isFalse, + ); + }); + + test( + 'returns false when shorebird.yaml does not exist ' + 'but pubspec contains shorebird.yaml', () { + final tempDir = Directory.systemTemp.createTempSync(); + File( + p.join(tempDir.path, 'pubspec.yaml'), + ).writeAsStringSync(''' +name: test +flutter: + assets: + - shorebird.yaml'''); + expect( + IOOverrides.runZoned( + () => runWithOverrides(() => shorebirdEnv.isShorebirdInitialized), + getCurrentDirectory: () => tempDir, + ), + isFalse, + ); + }); + + test( + 'returns true when shorebird.yaml exists ' + 'and pubspec contains shorebird.yaml', () { + final tempDir = Directory.systemTemp.createTempSync(); + File( + p.join(tempDir.path, 'shorebird.yaml'), + ).writeAsStringSync('app_id: test-app-id'); + File( + p.join(tempDir.path, 'pubspec.yaml'), + ).writeAsStringSync(''' +name: test +flutter: + assets: + - shorebird.yaml'''); + expect( + IOOverrides.runZoned( + () => runWithOverrides(() => shorebirdEnv.isShorebirdInitialized), + getCurrentDirectory: () => tempDir, + ), + isTrue, + ); + }); + }); + + group('flutterRevision', () { + test('returns correct revision', () { + const revision = 'test-revision'; + File(p.join(shorebirdRoot.path, 'bin', 'internal', 'flutter.version')) + ..createSync(recursive: true) + ..writeAsStringSync(revision, flush: true); + expect( + runWithOverrides(() => shorebirdEnv.flutterRevision), + equals(revision), + ); + }); + + test('trims revision file content', () { + const revision = ''' + +test-revision + +\r\n +'''; + File(p.join(shorebirdRoot.path, 'bin', 'internal', 'flutter.version')) + ..createSync(recursive: true) + ..writeAsStringSync(revision, flush: true); + + expect( + runWithOverrides(() => shorebirdEnv.flutterRevision), + 'test-revision', + ); + }); + }); + + group('shorebirdEngineRevision', () { + test('returns correct revision', () { + const revision = 'test-revision'; + File( + p.join( + shorebirdRoot.path, + 'bin', + 'cache', + 'flutter', + 'bin', + 'internal', + 'engine.version', + ), + ) + ..createSync(recursive: true) + ..writeAsStringSync(revision, flush: true); + expect( + runWithOverrides(() => shorebirdEnv.shorebirdEngineRevision), + equals(revision), + ); + }); + }); + + group('hostedUrl', () { + test('returns hosted url from env if available', () { + when(() => platform.environment).thenReturn({ + 'SHOREBIRD_HOSTED_URL': 'https://example.com', + }); + expect( + runWithOverrides(() => shorebirdEnv.hostedUri), + equals(Uri.parse('https://example.com')), + ); + }); + + test('falls back to shorebird.yaml', () { + final directory = Directory.systemTemp.createTempSync(); + File(p.join(directory.path, 'shorebird.yaml')).writeAsStringSync(''' +app_id: test-id +base_url: https://example.com'''); + expect( + IOOverrides.runZoned( + () => runWithOverrides(() => shorebirdEnv.hostedUri), + getCurrentDirectory: () => directory, + ), + equals(Uri.parse('https://example.com')), + ); + }); + + test('returns null when there is no env override or shorebird.yaml', () { + expect(runWithOverrides(() => shorebirdEnv.hostedUri), isNull); + }); + }); + }); +} diff --git a/packages/shorebird_cli/test/src/shorebird_environment_test.dart b/packages/shorebird_cli/test/src/shorebird_environment_test.dart deleted file mode 100644 index 8f948be8..00000000 --- a/packages/shorebird_cli/test/src/shorebird_environment_test.dart +++ /dev/null @@ -1,122 +0,0 @@ -import 'dart:io'; - -import 'package:mocktail/mocktail.dart'; -import 'package:path/path.dart' as p; -import 'package:platform/platform.dart'; -import 'package:scoped/scoped.dart'; -import 'package:shorebird_cli/src/platform.dart'; -import 'package:shorebird_cli/src/shorebird_environment.dart'; -import 'package:test/test.dart'; - -class _MockPlatform extends Mock implements Platform {} - -void main() { - group('ShorebirdEnvironment', () { - late Platform platform; - late Directory shorebirdRoot; - late Uri platformScript; - - R runWithOverrides(R Function() body) { - return runScoped( - () => body(), - values: { - platformRef.overrideWith(() => platform), - }, - ); - } - - setUp(() { - shorebirdRoot = Directory.systemTemp.createTempSync(); - platformScript = Uri.file( - p.join(shorebirdRoot.path, 'bin', 'cache', 'shorebird.snapshot'), - ); - platform = _MockPlatform(); - - when(() => platform.environment).thenReturn(const {}); - when(() => platform.script).thenReturn(platformScript); - }); - - group('flutterRevision', () { - test('returns correct revision', () { - const revision = 'test-revision'; - File(p.join(shorebirdRoot.path, 'bin', 'internal', 'flutter.version')) - ..createSync(recursive: true) - ..writeAsStringSync(revision, flush: true); - expect( - runWithOverrides(() => ShorebirdEnvironment.flutterRevision), - equals(revision), - ); - }); - - test('trims revision file content', () { - const revision = ''' - -test-revision - -\r\n -'''; - File(p.join(shorebirdRoot.path, 'bin', 'internal', 'flutter.version')) - ..createSync(recursive: true) - ..writeAsStringSync(revision, flush: true); - - expect( - runWithOverrides(() => ShorebirdEnvironment.flutterRevision), - 'test-revision', - ); - }); - }); - - group('shorebirdEngineRevision', () { - test('returns correct revision', () { - const revision = 'test-revision'; - File( - p.join( - shorebirdRoot.path, - 'bin', - 'cache', - 'flutter', - 'bin', - 'internal', - 'engine.version', - ), - ) - ..createSync(recursive: true) - ..writeAsStringSync(revision, flush: true); - expect( - runWithOverrides(() => ShorebirdEnvironment.shorebirdEngineRevision), - equals(revision), - ); - }); - }); - - group('hostedUrl', () { - test('returns hosted url from env if available', () { - when(() => platform.environment).thenReturn({ - 'SHOREBIRD_HOSTED_URL': 'https://example.com', - }); - expect( - runWithOverrides(() => ShorebirdEnvironment.hostedUri), - equals(Uri.parse('https://example.com')), - ); - }); - - test('falls back to shorebird.yaml', () { - final directory = Directory.systemTemp.createTempSync(); - File(p.join(directory.path, 'shorebird.yaml')).writeAsStringSync(''' -app_id: test-id -base_url: https://example.com'''); - expect( - IOOverrides.runZoned( - () => runWithOverrides(() => ShorebirdEnvironment.hostedUri), - getCurrentDirectory: () => directory, - ), - equals(Uri.parse('https://example.com')), - ); - }); - - test('returns null when there is no env override or shorebird.yaml', () { - expect(runWithOverrides(() => ShorebirdEnvironment.hostedUri), isNull); - }); - }); - }); -} diff --git a/packages/shorebird_cli/test/src/shorebird_process_test.dart b/packages/shorebird_cli/test/src/shorebird_process_test.dart index 2c1c9c50..f629c225 100644 --- a/packages/shorebird_cli/test/src/shorebird_process_test.dart +++ b/packages/shorebird_cli/test/src/shorebird_process_test.dart @@ -2,7 +2,9 @@ import 'dart:io'; import 'package:mocktail/mocktail.dart'; import 'package:path/path.dart' as p; +import 'package:scoped/scoped.dart'; import 'package:shorebird_cli/src/process.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; import 'package:test/test.dart'; class _MockProcess extends Mock implements Process {} @@ -11,6 +13,8 @@ class _MockProcessResult extends Mock implements ShorebirdProcessResult {} class _MockProcessWrapper extends Mock implements ProcessWrapper {} +class _MockShorebirdEnv extends Mock implements ShorebirdEnv {} + void main() { group('ShorebirdProcess', () { const flutterStorageBaseUrlEnv = { @@ -20,16 +24,32 @@ void main() { late ProcessWrapper processWrapper; late Process startProcess; late ShorebirdProcessResult runProcessResult; + late ShorebirdEnv shorebirdEnv; late ShorebirdProcess shorebirdProcess; + R runWithOverrides(R Function() body) { + return runScoped( + () => body(), + values: { + shorebirdEnvRef.overrideWith(() => shorebirdEnv), + }, + ); + } + setUp(() { processWrapper = _MockProcessWrapper(); runProcessResult = _MockProcessResult(); startProcess = _MockProcess(); - shorebirdProcess = ShorebirdProcess( - processWrapper: processWrapper, + shorebirdEnv = _MockShorebirdEnv(); + shorebirdProcess = runWithOverrides( + () => ShorebirdProcess(processWrapper: processWrapper), ); + when(() => shorebirdEnv.flutterBinaryFile).thenReturn( + File( + p.join('bin', 'cache', 'flutter', 'bin', 'flutter'), + ), + ); when( () => processWrapper.run( any(), @@ -78,11 +98,13 @@ void main() { }); test('replaces "flutter" with our local flutter', () async { - await shorebirdProcess.run( - 'flutter', - ['--version'], - runInShell: true, - workingDirectory: '~', + await runWithOverrides( + () => shorebirdProcess.run( + 'flutter', + ['--version'], + runInShell: true, + workingDirectory: '~', + ), ); verify( @@ -178,7 +200,7 @@ void main() { ), ); - await shorebirdProcess.run('flutter', []); + await runWithOverrides(() => shorebirdProcess.run('flutter', [])); verify( () => processWrapper.run( @@ -209,7 +231,9 @@ void main() { }); test('replaces "flutter" with our local flutter', () async { - await shorebirdProcess.start('flutter', ['run'], runInShell: true); + await runWithOverrides( + () => shorebirdProcess.start('flutter', ['run'], runInShell: true), + ); verify( () => processWrapper.start( @@ -247,11 +271,13 @@ void main() { }); test('Updates environment if useVendedFlutter is true', () async { - await shorebirdProcess.start( - 'flutter', - ['--version'], - runInShell: true, - environment: {'ENV_VAR': 'asdfasdf'}, + await runWithOverrides( + () => shorebirdProcess.start( + 'flutter', + ['--version'], + runInShell: true, + environment: {'ENV_VAR': 'asdfasdf'}, + ), ); verify( diff --git a/packages/shorebird_cli/test/src/shorebird_validator_test.dart b/packages/shorebird_cli/test/src/shorebird_validator_test.dart index 4b6b4621..24be3b7c 100644 --- a/packages/shorebird_cli/test/src/shorebird_validator_test.dart +++ b/packages/shorebird_cli/test/src/shorebird_validator_test.dart @@ -5,6 +5,7 @@ import 'package:scoped/scoped.dart'; import 'package:shorebird_cli/src/auth/auth.dart'; import 'package:shorebird_cli/src/logger.dart'; import 'package:shorebird_cli/src/platform.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; import 'package:shorebird_cli/src/shorebird_validator.dart'; import 'package:shorebird_cli/src/validators/validators.dart'; import 'package:test/test.dart'; @@ -15,6 +16,8 @@ class _MockLogger extends Mock implements Logger {} class _MockPlatform extends Mock implements Platform {} +class _MockShorebirdEnv extends Mock implements ShorebirdEnv {} + class _MockValidator extends Mock implements Validator {} void main() { @@ -23,6 +26,7 @@ void main() { late Logger logger; late Platform platform; late Validator validator; + late ShorebirdEnv shorebirdEnv; late ShorebirdValidator shorebirdValidator; R runWithOverrides(R Function() body) { @@ -32,6 +36,7 @@ void main() { authRef.overrideWith(() => auth), loggerRef.overrideWith(() => logger), platformRef.overrideWith(() => platform), + shorebirdEnvRef.overrideWith(() => shorebirdEnv), }, ); } @@ -40,6 +45,7 @@ void main() { auth = _MockAuth(); logger = _MockLogger(); platform = _MockPlatform(); + shorebirdEnv = _MockShorebirdEnv(); validator = _MockValidator(); shorebirdValidator = runWithOverrides(ShorebirdValidator.new); }); @@ -103,6 +109,7 @@ void main() { test( 'throws ShorebirdNotInitializedException ' 'when shorebird has not been initialized', () async { + when(() => shorebirdEnv.isShorebirdInitialized).thenReturn(false); await expectLater( runWithOverrides( () => shorebirdValidator.validatePreconditions( diff --git a/packages/shorebird_cli/test/src/validators/shorebird_flutter_validator_test.dart b/packages/shorebird_cli/test/src/validators/shorebird_flutter_validator_test.dart index 14b8c258..7dadd51b 100644 --- a/packages/shorebird_cli/test/src/validators/shorebird_flutter_validator_test.dart +++ b/packages/shorebird_cli/test/src/validators/shorebird_flutter_validator_test.dart @@ -6,7 +6,7 @@ import 'package:platform/platform.dart'; import 'package:scoped/scoped.dart'; import 'package:shorebird_cli/src/platform.dart'; import 'package:shorebird_cli/src/process.dart'; -import 'package:shorebird_cli/src/shorebird_environment.dart'; +import 'package:shorebird_cli/src/shorebird_env.dart'; import 'package:shorebird_cli/src/validators/validators.dart'; import 'package:test/test.dart'; @@ -14,6 +14,8 @@ class _MockProcessResult extends Mock implements ShorebirdProcessResult {} class _MockPlatform extends Mock implements Platform {} +class _MockShorebirdEnv extends Mock implements ShorebirdEnv {} + class _MockShorebirdProcess extends Mock implements ShorebirdProcess {} void main() { @@ -40,6 +42,7 @@ Tools • Dart 2.19.6 • DevTools 2.20.1 late ShorebirdProcessResult shorebirdFlutterVersionProcessResult; late ShorebirdProcessResult gitStatusProcessResult; late ShorebirdProcess shorebirdProcess; + late ShorebirdEnv shorebirdEnv; late Platform platform; R runWithOverrides(R Function() body) { @@ -48,6 +51,7 @@ Tools • Dart 2.19.6 • DevTools 2.20.1 values: { platformRef.overrideWith(() => platform), processRef.overrideWith(() => shorebirdProcess), + shorebirdEnvRef.overrideWith(() => shorebirdEnv), }, ); } @@ -68,8 +72,12 @@ Tools • Dart 2.19.6 • DevTools 2.20.1 setUp(() { tempDir = setupTempDirectory(); platform = _MockPlatform(); + shorebirdEnv = _MockShorebirdEnv(); - ShorebirdEnvironment.flutterRevision = flutterRevision; + when(() => shorebirdEnv.flutterRevision).thenReturn(flutterRevision); + when( + () => shorebirdEnv.flutterDirectory, + ).thenReturn(flutterDirectory(tempDir)); when(() => platform.script).thenReturn(shorebirdScriptFile(tempDir).uri); when(() => platform.environment).thenReturn({});