refactor(shorebird_cli): use scoped auth (#662)

Co-authored-by: Bryan Oltman <bryanoltman@gmail.com>
This commit is contained in:
Felix Angelov
2023-06-15 08:56:01 -07:00
committed by GitHub
parent a11c9be59a
commit ef09b7826c
69 changed files with 842 additions and 510 deletions
+1 -1
View File
@@ -49,7 +49,7 @@ T read<T>(ScopedRef<T> ref, {T Function()? orElse}) {
if (orElse != null) return orElse();
throw StateError(
'''
read(...) was called in a scope which does not contain a corresponding value for the provided ref.
read(ScopedRef<$T>) was called in a scope which does not contain a corresponding value for the provided ref.
Did you forget to call: runScoped(() {...}, values: {value})?''',
);
}
+8 -1
View File
@@ -1,6 +1,8 @@
import 'dart:io';
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/command_runner.dart';
import 'package:shorebird_cli/src/logger.dart';
import 'package:shorebird_cli/src/platform.dart';
@@ -9,7 +11,12 @@ Future<void> main(List<String> args) async {
await _flushThenExit(
await runScoped(
() async => ShorebirdCliCommandRunner().run(args),
values: {loggerRef, platformRef},
values: {
authRef,
loggerRef,
platformRef,
codePushClientWrapperRef,
},
),
);
}
@@ -5,12 +5,19 @@ import 'package:cli_util/cli_util.dart';
import 'package:googleapis_auth/auth_io.dart' as oauth2;
import 'package:http/http.dart' as http;
import 'package:path/path.dart' as p;
import 'package:scoped/scoped.dart';
import 'package:shorebird_cli/src/auth/jwt.dart';
import 'package:shorebird_cli/src/command.dart';
import 'package:shorebird_cli/src/command_runner.dart';
import 'package:shorebird_cli/src/logger.dart';
import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
// A reference to a [Auth] instance.
final authRef = create(Auth.new);
// The [Auth] instance available in the current zone.
Auth get auth => read(authRef);
final _clientId = oauth2.ClientId(
/// Shorebird CLI's OAuth 2.0 identifier.
'523302233293-eia5antm0tgvek240t46orctktiabrek.apps.googleusercontent.com',
@@ -3,8 +3,11 @@ import 'package:crypto/crypto.dart';
import 'package:mason_logger/mason_logger.dart';
import 'package:meta/meta.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/logger.dart';
import 'package:shorebird_cli/src/shorebird_build_mixin.dart';
import 'package:shorebird_cli/src/shorebird_environment.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';
@@ -33,6 +36,20 @@ class PatchArtifactBundle {
final int size;
}
// A reference to a [CodePushClientWrapper] instance.
ScopedRef<CodePushClientWrapper> codePushClientWrapperRef = create(() {
return CodePushClientWrapper(
codePushClient: CodePushClient(
httpClient: auth.client,
hostedUri: ShorebirdEnvironment.hostedUri,
),
);
});
// The [CodePushClientWrapper] instance available in the current zone.
CodePushClientWrapper get codePushClientWrapper =>
read(codePushClientWrapperRef);
/// {@template code_push_client_wrapper}
/// Wraps [CodePushClient] interaction with logging and error handling to
/// reduce the amount of command and command test code.
+1 -49
View File
@@ -2,17 +2,10 @@ import 'dart:io';
import 'package:args/args.dart';
import 'package:args/command_runner.dart';
import 'package:checked_yaml/checked_yaml.dart';
import 'package:http/http.dart' as http;
import 'package:meta/meta.dart';
import 'package:path/path.dart' as p;
import 'package:pubspec_parse/pubspec_parse.dart';
import 'package:shorebird_cli/src/auth/auth.dart';
import 'package:shorebird_cli/src/cache.dart';
import 'package:shorebird_cli/src/code_push_client_wrapper.dart';
import 'package:shorebird_cli/src/command_runner.dart';
import 'package:shorebird_cli/src/config/config.dart';
import 'package:shorebird_cli/src/platform.dart';
import 'package:shorebird_cli/src/shorebird_process.dart';
import 'package:shorebird_cli/src/validators/validators.dart';
import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
@@ -40,28 +33,15 @@ List<Validator> _defaultValidators() => [
abstract class ShorebirdCommand extends Command<int> {
ShorebirdCommand({
Auth? auth,
Cache? cache,
CodePushClientBuilder? buildCodePushClient,
CodePushClientWrapper? codePushClientWrapper,
List<Validator>? validators, // For mocking.
}) : cache = cache ?? Cache(),
buildCodePushClient = buildCodePushClient ?? CodePushClient.new,
validators = validators ?? _defaultValidators() {
this.auth = auth ?? Auth();
this.codePushClientWrapper = codePushClientWrapper ??
CodePushClientWrapper(
codePushClient: CodePushClient(
httpClient: this.auth.client,
hostedUri: hostedUri,
),
);
}
validators = validators ?? _defaultValidators();
late final Auth auth;
final Cache cache;
final CodePushClientBuilder buildCodePushClient;
late final CodePushClientWrapper codePushClientWrapper;
// We don't currently have a test involving both a CommandRunner
// and a Command, so we can't test this getter.
@@ -98,32 +78,4 @@ abstract class ShorebirdCommand extends Command<int> {
/// [ArgResults] for the current command.
ArgResults get results => testArgResults ?? argResults!;
File getShorebirdYamlFile() {
return File(p.join(Directory.current.path, 'shorebird.yaml'));
}
ShorebirdYaml? getShorebirdYaml() {
final file = getShorebirdYamlFile();
if (!file.existsSync()) return null;
final yaml = file.readAsStringSync();
return checkedYamlDecode(yaml, (m) => ShorebirdYaml.fromJson(m!));
}
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);
}
Uri? get hostedUri {
try {
final baseUrl = platform.environment['SHOREBIRD_HOSTED_URL'] ??
getShorebirdYaml()?.baseUrl;
return baseUrl == null ? null : Uri.tryParse(baseUrl);
} catch (_) {
return null;
}
}
}
@@ -7,7 +7,7 @@ import 'package:shorebird_cli/src/commands/commands.dart';
/// {@endtemplate}
class AccountCommand extends ShorebirdCommand {
/// {@macro account_command}
AccountCommand({super.auth}) {
AccountCommand() {
addSubcommand(CreateAccountCommand());
addSubcommand(SubscribeAccountCommand());
}
@@ -12,9 +12,6 @@ import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
/// Create a new Shorebird account.
/// {@endtemplate}
class CreateAccountCommand extends ShorebirdCommand with ShorebirdConfigMixin {
/// {@macro create_account_command}
CreateAccountCommand({super.auth});
@override
String get description => 'Create a new Shorebird account.';
@@ -1,9 +1,11 @@
import 'dart:async';
import 'package:mason_logger/mason_logger.dart';
import 'package:shorebird_cli/src/auth/auth.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_environment.dart';
import 'package:shorebird_cli/src/shorebird_validation_mixin.dart';
import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
@@ -13,7 +15,7 @@ import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
class SubscribeAccountCommand extends ShorebirdCommand
with ShorebirdConfigMixin, ShorebirdValidationMixin {
/// {@macro subscribe_account_command}
SubscribeAccountCommand({super.auth, super.buildCodePushClient});
SubscribeAccountCommand({super.buildCodePushClient});
@override
String get name => 'subscribe';
@@ -41,7 +43,7 @@ Visit ${styleUnderlined.wrap(lightCyan.wrap('https://shorebird.dev'))} for more
final client = buildCodePushClient(
httpClient: auth.client,
hostedUri: hostedUri,
hostedUri: ShorebirdEnvironment.hostedUri,
);
final progress = logger.progress('Retrieving account information');
@@ -19,7 +19,7 @@ class CreateAppCommand extends ShorebirdCommand
ShorebirdValidationMixin,
ShorebirdCreateAppMixin {
/// {@macro create_app_command}
CreateAppCommand({super.buildCodePushClient, super.auth}) {
CreateAppCommand({super.buildCodePushClient}) {
argParser.addOption(
'app-name',
help: '''
@@ -1,9 +1,11 @@
import 'dart:async';
import 'package:mason_logger/mason_logger.dart';
import 'package:shorebird_cli/src/auth/auth.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_environment.dart';
import 'package:shorebird_cli/src/shorebird_validation_mixin.dart';
/// {@template delete_app_command}
@@ -14,7 +16,7 @@ import 'package:shorebird_cli/src/shorebird_validation_mixin.dart';
class DeleteAppCommand extends ShorebirdCommand
with ShorebirdConfigMixin, ShorebirdValidationMixin {
/// {@macro delete_app_command}
DeleteAppCommand({super.buildCodePushClient, super.auth}) {
DeleteAppCommand({super.buildCodePushClient}) {
argParser.addOption(
'app-id',
help: '''
@@ -45,7 +47,7 @@ Defaults to the app_id in "shorebird.yaml".''',
if (appIdArg == null) {
String? defaultAppId;
try {
defaultAppId = getShorebirdYaml()?.appId;
defaultAppId = ShorebirdEnvironment.getShorebirdYaml()?.appId;
} catch (_) {}
appId = logger.prompt(
@@ -58,7 +60,7 @@ Defaults to the app_id in "shorebird.yaml".''',
final client = buildCodePushClient(
httpClient: auth.client,
hostedUri: hostedUri,
hostedUri: ShorebirdEnvironment.hostedUri,
);
final confirm = logger.confirm('Deleting an app is permanent. Continue?');
@@ -2,9 +2,11 @@ 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/command.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_cli/src/shorebird_validation_mixin.dart';
import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
@@ -16,7 +18,7 @@ import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
class ListAppsCommand extends ShorebirdCommand
with ShorebirdConfigMixin, ShorebirdValidationMixin {
/// {@macro list_apps_command}
ListAppsCommand({super.buildCodePushClient, super.auth});
ListAppsCommand({super.buildCodePushClient});
@override
String get description => 'List all apps using Shorebird.';
@@ -39,7 +41,7 @@ class ListAppsCommand extends ShorebirdCommand
final client = buildCodePushClient(
httpClient: auth.client,
hostedUri: hostedUri,
hostedUri: ShorebirdEnvironment.hostedUri,
);
final List<AppMetadata> apps;
@@ -16,7 +16,7 @@ import 'package:shorebird_cli/src/shorebird_validation_mixin.dart';
/// {@endtemplate}
class BuildAarCommand extends ShorebirdCommand
with ShorebirdConfigMixin, ShorebirdValidationMixin, ShorebirdBuildMixin {
BuildAarCommand({super.auth, super.validators}) {
BuildAarCommand({super.validators}) {
// We would have a "target" option here, similar to what [BuildApkCommand]
// and [BuildAabCommand] have, but target cannot currently be configured in
// `flutter build aar` and is always assumed to be lib/main.dart.
@@ -16,7 +16,7 @@ import 'package:shorebird_cli/src/shorebird_validation_mixin.dart';
class BuildApkCommand extends ShorebirdCommand
with ShorebirdConfigMixin, ShorebirdValidationMixin, ShorebirdBuildMixin {
/// {@macro build_apk_command}
BuildApkCommand({super.auth, super.validators}) {
BuildApkCommand({super.validators}) {
argParser
..addOption(
'target',
@@ -16,7 +16,7 @@ import 'package:shorebird_cli/src/shorebird_validation_mixin.dart';
class BuildAppBundleCommand extends ShorebirdCommand
with ShorebirdConfigMixin, ShorebirdValidationMixin, ShorebirdBuildMixin {
/// {@macro build_app_bundle_command}
BuildAppBundleCommand({super.auth, super.validators}) {
BuildAppBundleCommand({super.validators}) {
argParser
..addOption(
'target',
@@ -16,7 +16,7 @@ import 'package:shorebird_cli/src/shorebird_validation_mixin.dart';
class BuildIpaCommand extends ShorebirdCommand
with ShorebirdConfigMixin, ShorebirdValidationMixin, ShorebirdBuildMixin {
/// {@macro build_ipa_command}
BuildIpaCommand({super.auth, super.validators}) {
BuildIpaCommand({super.validators}) {
argParser
..addOption(
'target',
@@ -1,9 +1,11 @@
import 'dart:async';
import 'package:mason_logger/mason_logger.dart';
import 'package:shorebird_cli/src/auth/auth.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_environment.dart';
import 'package:shorebird_cli/src/shorebird_validation_mixin.dart';
/// {@template add_collaborators_command}
@@ -13,7 +15,7 @@ import 'package:shorebird_cli/src/shorebird_validation_mixin.dart';
class AddCollaboratorsCommand extends ShorebirdCommand
with ShorebirdConfigMixin, ShorebirdValidationMixin {
/// {@macro add_collaborators_command}
AddCollaboratorsCommand({super.buildCodePushClient, super.auth}) {
AddCollaboratorsCommand({super.buildCodePushClient}) {
argParser
..addOption(
_appIdOption,
@@ -46,10 +48,11 @@ class AddCollaboratorsCommand extends ShorebirdCommand
final client = buildCodePushClient(
httpClient: auth.client,
hostedUri: hostedUri,
hostedUri: ShorebirdEnvironment.hostedUri,
);
final appId = results[_appIdOption] as String? ?? getShorebirdYaml()?.appId;
final appId = results[_appIdOption] as String? ??
ShorebirdEnvironment.getShorebirdYaml()?.appId;
if (appId == null) {
logger.err(
'''
@@ -2,9 +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/command.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_cli/src/shorebird_validation_mixin.dart';
import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
@@ -15,7 +17,7 @@ import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
class DeleteCollaboratorsCommand extends ShorebirdCommand
with ShorebirdConfigMixin, ShorebirdValidationMixin {
/// {@macro delete_collaborators_command}
DeleteCollaboratorsCommand({super.buildCodePushClient, super.auth}) {
DeleteCollaboratorsCommand({super.buildCodePushClient}) {
argParser
..addOption(
_appIdOption,
@@ -49,10 +51,11 @@ class DeleteCollaboratorsCommand extends ShorebirdCommand
final client = buildCodePushClient(
httpClient: auth.client,
hostedUri: hostedUri,
hostedUri: ShorebirdEnvironment.hostedUri,
);
final appId = results[_appIdOption] as String? ?? getShorebirdYaml()?.appId;
final appId = results[_appIdOption] as String? ??
ShorebirdEnvironment.getShorebirdYaml()?.appId;
if (appId == null) {
logger.err(
'''
@@ -2,9 +2,11 @@ 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/command.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_cli/src/shorebird_validation_mixin.dart';
import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
@@ -15,7 +17,7 @@ import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
class ListCollaboratorsCommand extends ShorebirdCommand
with ShorebirdConfigMixin, ShorebirdValidationMixin {
/// {@macro list_collaborators_command}
ListCollaboratorsCommand({super.buildCodePushClient, super.auth}) {
ListCollaboratorsCommand({super.buildCodePushClient}) {
argParser.addOption(
_appIdOption,
help: 'The app id to list collaborators for.',
@@ -45,10 +47,11 @@ class ListCollaboratorsCommand extends ShorebirdCommand
final client = buildCodePushClient(
httpClient: auth.client,
hostedUri: hostedUri,
hostedUri: ShorebirdEnvironment.hostedUri,
);
final appId = results[_appIdOption] as String? ?? getShorebirdYaml()?.appId;
final appId = results[_appIdOption] as String? ??
ShorebirdEnvironment.getShorebirdYaml()?.appId;
if (appId == null) {
logger.err(
'''
@@ -5,6 +5,7 @@ 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_environment.dart';
import 'package:shorebird_cli/src/shorebird_flavor_mixin.dart';
import 'package:shorebird_cli/src/shorebird_java_mixin.dart';
import 'package:shorebird_cli/src/shorebird_validation_mixin.dart';
@@ -22,7 +23,7 @@ class InitCommand extends ShorebirdCommand
ShorebirdJavaMixin,
ShorebirdFlavorMixin {
/// {@macro init_command}
InitCommand({super.auth, super.buildCodePushClient}) {
InitCommand({super.buildCodePushClient}) {
argParser.addFlag(
'force',
abbr: 'f',
@@ -62,7 +63,7 @@ Please make sure you are running "shorebird init" from the root of your Flutter
final force = results['force'] == true;
if (force && hasShorebirdYaml) {
getShorebirdYamlFile().deleteSync();
ShorebirdEnvironment.getShorebirdYamlFile().deleteSync();
}
if (hasShorebirdYaml) {
@@ -87,7 +88,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: getPubspecYaml()?.name,
defaultValue: ShorebirdEnvironment.getPubspecYaml()?.name,
);
if (productFlavors.isNotEmpty) {
@@ -8,9 +8,6 @@ import 'package:shorebird_cli/src/logger.dart';
/// Login as a new Shorebird user.
/// {@endtemplate}
class LoginCommand extends ShorebirdCommand {
/// {@macro login_command}
LoginCommand({super.auth});
@override
String get description => 'Login as a new Shorebird user.';
@@ -1,4 +1,5 @@
import 'package:mason_logger/mason_logger.dart';
import 'package:shorebird_cli/src/auth/auth.dart';
import 'package:shorebird_cli/src/command.dart';
import 'package:shorebird_cli/src/logger.dart';
@@ -8,9 +9,6 @@ import 'package:shorebird_cli/src/logger.dart';
/// Logout of the current Shorebird user.
/// {@endtemplate}
class LogoutCommand extends ShorebirdCommand {
/// {@macro logout_command}
LogoutCommand({super.auth});
@override
String get description => 'Logout of the current Shorebird user';
@@ -7,6 +7,7 @@ import 'package:http/http.dart' as http;
import 'package:mason_logger/mason_logger.dart';
import 'package:path/path.dart' as p;
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/command.dart';
import 'package:shorebird_cli/src/config/config.dart';
@@ -35,7 +36,6 @@ class PatchAarCommand extends ShorebirdCommand
ShorebirdArtifactMixin {
/// {@macro patch_aar_command}
PatchAarCommand({
super.auth,
super.buildCodePushClient,
super.cache,
super.validators,
@@ -138,10 +138,10 @@ of the Android app that is using this module.''',
return ExitCode.software.code;
}
final shorebirdYaml = getShorebirdYaml()!;
final shorebirdYaml = ShorebirdEnvironment.getShorebirdYaml()!;
final codePushClient = buildCodePushClient(
httpClient: auth.client,
hostedUri: hostedUri,
hostedUri: ShorebirdEnvironment.hostedUri,
);
final appId = shorebirdYaml.getAppId(flavor: flavor);
@@ -168,7 +168,7 @@ Did you forget to run "shorebird init"?''',
return ExitCode.success.code;
}
const platform = 'android';
const platformName = 'android';
final channelName = results['channel'] as String;
final Release? release;
@@ -234,7 +234,7 @@ https://github.com/shorebirdtech/shorebird/issues/472
final releaseArtifacts = await getReleaseArtifacts(
release: release,
architectures: architectures,
platform: platform,
platform: platformName,
);
if (releaseArtifacts == null) {
return ExitCode.software.code;
@@ -328,7 +328,7 @@ https://github.com/shorebirdtech/shorebird/issues/472
if (flavor != null) '🍧 Flavor: ${lightCyan.wrap(flavor)}',
'📦 Release Version: ${lightCyan.wrap(releaseVersion)}',
'📺 Channel: ${lightCyan.wrap(channelName)}',
'''🕹️ Platform: ${lightCyan.wrap(platform)} ${lightCyan.wrap('[${archMetadata.join(', ')}]')}''',
'''🕹️ Platform: ${lightCyan.wrap(platformName)} ${lightCyan.wrap('[${archMetadata.join(', ')}]')}''',
];
logger.info(
@@ -368,7 +368,7 @@ ${summary.join('\n')}
patchId: patch.id,
artifactPath: artifact.path,
arch: artifact.arch,
platform: platform,
platform: platformName,
hash: artifact.hash,
);
} catch (error) {
@@ -33,10 +33,8 @@ class PatchAndroidCommand extends ShorebirdCommand
ShorebirdReleaseVersionMixin {
/// {@macro patch_android_command}
PatchAndroidCommand({
super.auth,
super.cache,
super.validators,
super.codePushClientWrapper,
HashFunction? hashFn,
http.Client? httpClient,
AabDiffer? aabDiffer,
@@ -113,7 +111,7 @@ class PatchAndroidCommand extends ShorebirdCommand
await cache.updateAll();
const platform = 'android';
const platformName = 'android';
final channelName = results['channel'] as String;
final flavor = results['flavor'] as String?;
final target = results['target'] as String?;
@@ -126,7 +124,7 @@ class PatchAndroidCommand extends ShorebirdCommand
return ExitCode.software.code;
}
final shorebirdYaml = getShorebirdYaml()!;
final shorebirdYaml = ShorebirdEnvironment.getShorebirdYaml()!;
final appId = shorebirdYaml.getAppId(flavor: flavor);
final app = await codePushClientWrapper.getApp(appId: appId);
@@ -204,14 +202,14 @@ https://github.com/shorebirdtech/shorebird/issues/472
final releaseArtifacts = await codePushClientWrapper.getReleaseArtifacts(
releaseId: release.id,
architectures: architectures,
platform: platform,
platform: platformName,
);
final releaseAabArtifact =
await codePushClientWrapper.maybeGetReleaseArtifact(
releaseId: release.id,
arch: 'aab',
platform: platform,
platform: platformName,
);
final releaseArtifactPaths = <Arch, String>{};
@@ -332,7 +330,7 @@ If you believe you're seeing this in error, please reach out to us for support a
if (flavor != null) '🍧 Flavor: ${lightCyan.wrap(flavor)}',
'📦 Release Version: ${lightCyan.wrap(releaseVersion)}',
'📺 Channel: ${lightCyan.wrap(channelName)}',
'''🕹️ Platform: ${lightCyan.wrap(platform)} ${lightCyan.wrap('[${archMetadata.join(', ')}]')}''',
'''🕹️ Platform: ${lightCyan.wrap(platformName)} ${lightCyan.wrap('[${archMetadata.join(', ')}]')}''',
];
logger.info(
@@ -357,7 +355,7 @@ ${summary.join('\n')}
await codePushClientWrapper.publishPatch(
appId: appId,
releaseId: release.id,
platform: platform,
platform: platformName,
channelName: channelName,
patchArtifactBundles: patchArtifactBundles,
);
@@ -27,8 +27,6 @@ class PatchIosCommand extends ShorebirdCommand
ShorebirdArtifactMixin {
/// {@macro patch_ios_command}
PatchIosCommand({
super.auth,
super.codePushClientWrapper,
super.validators,
HashFunction? hashFn,
IpaReader? ipaReader,
@@ -89,7 +87,7 @@ class PatchIosCommand extends ShorebirdCommand
const arch = 'aarch64';
const channelName = 'stable';
const platform = 'ios';
const platformName = 'ios';
final force = results['force'] == true;
final dryRun = results['dry-run'] == true;
final flavor = results['flavor'] as String?;
@@ -100,7 +98,7 @@ class PatchIosCommand extends ShorebirdCommand
return ExitCode.usage.code;
}
final shorebirdYaml = getShorebirdYaml()!;
final shorebirdYaml = ShorebirdEnvironment.getShorebirdYaml()!;
final appId = shorebirdYaml.getAppId(flavor: flavor);
final app = await codePushClientWrapper.getApp(appId: appId);
@@ -129,7 +127,7 @@ class PatchIosCommand extends ShorebirdCommand
'Detecting release version',
);
try {
final pubspec = getPubspecYaml()!;
final pubspec = ShorebirdEnvironment.getPubspecYaml()!;
final ipa = _ipaReader.read(
p.join(
Directory.current.path,
@@ -207,7 +205,7 @@ https://github.com/shorebirdtech/shorebird/issues/472
if (flavor != null) '🍧 Flavor: ${lightCyan.wrap(flavor)}',
'📦 Release Version: ${lightCyan.wrap(releaseVersion)}',
'📺 Channel: ${lightCyan.wrap(channelName)}',
'''🕹️ Platform: ${lightCyan.wrap(platform)} ${lightCyan.wrap('[$arch (${formatBytes(aotFileSize)})]')}''',
'''🕹️ Platform: ${lightCyan.wrap(platformName)} ${lightCyan.wrap('[$arch (${formatBytes(aotFileSize)})]')}''',
];
logger.info(
@@ -234,7 +232,7 @@ ${summary.join('\n')}
await codePushClientWrapper.publishPatch(
appId: appId,
releaseId: release.id,
platform: platform,
platform: platformName,
channelName: channelName,
patchArtifactBundles: {
Arch.arm64: PatchArtifactBundle(
@@ -6,6 +6,7 @@ import 'package:collection/collection.dart';
import 'package:crypto/crypto.dart';
import 'package:mason_logger/mason_logger.dart';
import 'package:path/path.dart' as p;
import 'package:shorebird_cli/src/auth/auth.dart';
import 'package:shorebird_cli/src/command.dart';
import 'package:shorebird_cli/src/config/config.dart';
import 'package:shorebird_cli/src/logger.dart';
@@ -13,6 +14,7 @@ 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_create_app_mixin.dart';
import 'package:shorebird_cli/src/shorebird_environment.dart';
import 'package:shorebird_cli/src/shorebird_java_mixin.dart';
import 'package:shorebird_cli/src/shorebird_release_version_mixin.dart';
import 'package:shorebird_cli/src/shorebird_validation_mixin.dart';
@@ -33,7 +35,6 @@ class ReleaseAarCommand extends ShorebirdCommand
ShorebirdArtifactMixin {
/// {@macro release_aar_command}
ReleaseAarCommand({
super.auth,
super.buildCodePushClient,
super.validators,
HashFunction? hashFn,
@@ -110,10 +111,10 @@ make smaller updates to your app.
buildProgress.complete();
final shorebirdYaml = getShorebirdYaml()!;
final shorebirdYaml = ShorebirdEnvironment.getShorebirdYaml()!;
final codePushClient = buildCodePushClient(
httpClient: auth.client,
hostedUri: hostedUri,
hostedUri: ShorebirdEnvironment.hostedUri,
);
late final List<App> apps;
@@ -139,7 +140,7 @@ Did you forget to run "shorebird init"?''',
return ExitCode.software.code;
}
const platform = 'android';
const platformName = 'android';
final archNames = architectures.keys.map(
(arch) => arch.name,
);
@@ -147,7 +148,7 @@ Did you forget to run "shorebird init"?''',
'''📱 App: ${lightCyan.wrap(app.displayName)} ${lightCyan.wrap('(${app.id})')}''',
if (flavor != null) '🍧 Flavor: ${lightCyan.wrap(flavor)}',
'📦 Release Version: ${lightCyan.wrap(releaseVersion)}',
'''🕹️ Platform: ${lightCyan.wrap(platform)} ${lightCyan.wrap('(${archNames.join(', ')})')}''',
'''🕹️ Platform: ${lightCyan.wrap(platformName)} ${lightCyan.wrap('(${archNames.join(', ')})')}''',
];
logger.info('''
@@ -230,7 +231,7 @@ ${summary.join('\n')}
releaseId: release.id,
artifactPath: artifact.path,
arch: archMetadata.arch,
platform: platform,
platform: platformName,
hash: hash,
);
} on CodePushConflictException catch (_) {
@@ -256,7 +257,7 @@ ${archMetadata.arch} artifact already exists, continuing...''',
releaseId: release.id,
artifactPath: aarPath,
arch: 'aar',
platform: platform,
platform: platformName,
hash: _hashFn(await File(aarPath).readAsBytes()),
);
} on CodePushConflictException catch (_) {
@@ -2,12 +2,14 @@ import 'dart:io';
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/shorebird_yaml.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_create_app_mixin.dart';
import 'package:shorebird_cli/src/shorebird_environment.dart';
import 'package:shorebird_cli/src/shorebird_java_mixin.dart';
import 'package:shorebird_cli/src/shorebird_release_version_mixin.dart';
import 'package:shorebird_cli/src/shorebird_validation_mixin.dart';
@@ -26,9 +28,7 @@ class ReleaseAndroidCommand extends ShorebirdCommand
ShorebirdReleaseVersionMixin {
/// {@macro release_android_command}
ReleaseAndroidCommand({
super.auth,
super.cache,
super.codePushClientWrapper,
super.validators,
}) {
argParser
@@ -71,7 +71,7 @@ make smaller updates to your app.
return e.exitCode.code;
}
const platform = 'android';
const platformName = 'android';
final flavor = results['flavor'] as String?;
final target = results['target'] as String?;
final buildProgress = logger.progress('Building release');
@@ -83,7 +83,7 @@ make smaller updates to your app.
return ExitCode.software.code;
}
final shorebirdYaml = getShorebirdYaml()!;
final shorebirdYaml = ShorebirdEnvironment.getShorebirdYaml()!;
final appId = shorebirdYaml.getAppId(flavor: flavor);
final app = await codePushClientWrapper.getApp(appId: appId);
@@ -125,7 +125,7 @@ Please bump your version number and try again.''',
'''📱 App: ${lightCyan.wrap(app.displayName)} ${lightCyan.wrap('(${app.appId})')}''',
if (flavor != null) '🍧 Flavor: ${lightCyan.wrap(flavor)}',
'📦 Release Version: ${lightCyan.wrap(releaseVersion)}',
'''🕹️ Platform: ${lightCyan.wrap(platform)} ${lightCyan.wrap('(${archNames.join(', ')})')}''',
'''🕹️ Platform: ${lightCyan.wrap(platformName)} ${lightCyan.wrap('(${archNames.join(', ')})')}''',
];
logger.info('''
@@ -167,7 +167,7 @@ ${summary.join('\n')}
await codePushClientWrapper.createAndroidReleaseArtifacts(
releaseId: release.id,
aabPath: bundlePath,
platform: platform,
platform: platformName,
architectures: architectures,
flavor: flavor,
);
@@ -3,11 +3,13 @@ import 'dart:io';
import 'package:mason_logger/mason_logger.dart';
import 'package:path/path.dart' as p;
import 'package:shorebird_cli/src/archive_analysis/archive_analysis.dart';
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/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_validation_mixin.dart';
/// {@template release_ios_command}
@@ -18,8 +20,6 @@ class ReleaseIosCommand extends ShorebirdCommand
with ShorebirdBuildMixin, ShorebirdConfigMixin, ShorebirdValidationMixin {
/// {@macro release_ios_command}
ReleaseIosCommand({
super.auth,
super.codePushClientWrapper,
super.cache,
super.validators,
IpaReader? ipaReader,
@@ -68,9 +68,9 @@ make smaller updates to your app.
'''iOS support is in an experimental state and will not work without Flutter engine changes that have not yet been published.''',
);
const platform = 'ios';
const platformName = 'ios';
final flavor = results['flavor'] as String?;
final shorebirdYaml = getShorebirdYaml()!;
final shorebirdYaml = ShorebirdEnvironment.getShorebirdYaml()!;
final appId = shorebirdYaml.getAppId(flavor: flavor);
final app = await codePushClientWrapper.getApp(appId: appId);
@@ -89,7 +89,7 @@ make smaller updates to your app.
buildProgress.complete();
final releaseVersionProgress = logger.progress('Getting release version');
final pubspec = getPubspecYaml()!;
final pubspec = ShorebirdEnvironment.getPubspecYaml()!;
final ipaPath = p.join(
Directory.current.path,
'build',
@@ -128,7 +128,7 @@ Please bump your version number and try again.''',
'''📱 App: ${lightCyan.wrap(app.displayName)} ${lightCyan.wrap('($appId)')}''',
if (flavor != null) '🍧 Flavor: ${lightCyan.wrap(flavor)}',
'📦 Release Version: ${lightCyan.wrap(releaseVersion)}',
'''🕹️ Platform: ${lightCyan.wrap(platform)}''',
'''🕹️ Platform: ${lightCyan.wrap(platformName)}''',
];
logger.info('''
@@ -2,10 +2,12 @@ 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/command.dart';
import 'package:shorebird_cli/src/config/shorebird_yaml.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_cli/src/shorebird_validation_mixin.dart';
import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
@@ -17,7 +19,7 @@ import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
class DeleteReleasesCommand extends ShorebirdCommand
with ShorebirdConfigMixin, ShorebirdValidationMixin {
/// {@macro delete_releases_command}
DeleteReleasesCommand({super.auth, super.buildCodePushClient}) {
DeleteReleasesCommand({super.buildCodePushClient}) {
argParser
..addOption(
'version',
@@ -47,11 +49,12 @@ class DeleteReleasesCommand extends ShorebirdCommand
}
final flavor = results['flavor'] as String?;
final appId = getShorebirdYaml()!.getAppId(flavor: flavor);
final appId =
ShorebirdEnvironment.getShorebirdYaml()!.getAppId(flavor: flavor);
final codePushClient = buildCodePushClient(
httpClient: auth.client,
hostedUri: hostedUri,
hostedUri: ShorebirdEnvironment.hostedUri,
);
final List<Release> releases;
@@ -1,9 +1,11 @@
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/command.dart';
import 'package:shorebird_cli/src/config/shorebird_yaml.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_cli/src/shorebird_validation_mixin.dart';
import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
@@ -15,7 +17,7 @@ import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
class ListReleasesCommand extends ShorebirdCommand
with ShorebirdConfigMixin, ShorebirdValidationMixin {
/// {@macro list_releases_command}
ListReleasesCommand({super.auth, super.buildCodePushClient}) {
ListReleasesCommand({super.buildCodePushClient}) {
argParser.addOption(
'flavor',
help: 'The product flavor to use when listing releases.',
@@ -40,11 +42,12 @@ class ListReleasesCommand extends ShorebirdCommand
}
final flavor = results['flavor'] as String?;
final appId = getShorebirdYaml()!.getAppId(flavor: flavor);
final appId =
ShorebirdEnvironment.getShorebirdYaml()!.getAppId(flavor: flavor);
final codePushClient = buildCodePushClient(
httpClient: auth.client,
hostedUri: hostedUri,
hostedUri: ShorebirdEnvironment.hostedUri,
);
final List<Release> releases;
@@ -12,7 +12,7 @@ import 'package:shorebird_cli/src/shorebird_validation_mixin.dart';
class RunCommand extends ShorebirdCommand
with ShorebirdConfigMixin, ShorebirdValidationMixin {
/// {@macro run_command}
RunCommand({super.auth, super.buildCodePushClient, super.validators}) {
RunCommand({super.buildCodePushClient, super.validators}) {
argParser
..addOption(
'device-id',
@@ -2,15 +2,17 @@ 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/command.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_cli/src/shorebird_validation_mixin.dart';
import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
class CancelSubscriptionCommand extends ShorebirdCommand
with ShorebirdConfigMixin, ShorebirdValidationMixin {
CancelSubscriptionCommand({super.auth, super.buildCodePushClient});
CancelSubscriptionCommand({super.buildCodePushClient});
@override
String get name => 'cancel';
@@ -30,7 +32,7 @@ class CancelSubscriptionCommand extends ShorebirdCommand
final client = buildCodePushClient(
httpClient: auth.client,
hostedUri: hostedUri,
hostedUri: ShorebirdEnvironment.hostedUri,
);
final User user;
+1 -1
View File
@@ -2,7 +2,7 @@ import 'package:mason_logger/mason_logger.dart';
import 'package:scoped/scoped.dart';
// A reference to a [Logger] instance.
ScopedRef<Logger> loggerRef = create(Logger.new);
final loggerRef = create(Logger.new);
// The [Logger] instance available in the current zone.
Logger get logger => read(loggerRef);
@@ -1,14 +1,16 @@
import 'package:collection/collection.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_config_mixin.dart';
import 'package:shorebird_cli/src/shorebird_environment.dart';
import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
mixin ShorebirdCodePushClientMixin on ShorebirdConfigMixin {
Future<App?> getApp({required String appId, String? flavor}) async {
final codePushClient = buildCodePushClient(
httpClient: auth.client,
hostedUri: hostedUri,
hostedUri: ShorebirdEnvironment.hostedUri,
);
final List<App> apps;
@@ -32,7 +34,7 @@ mixin ShorebirdCodePushClientMixin on ShorebirdConfigMixin {
}) async {
final codePushClient = buildCodePushClient(
httpClient: auth.client,
hostedUri: hostedUri,
hostedUri: ShorebirdEnvironment.hostedUri,
);
final fetchChannelsProgress = logger.progress('Fetching channels');
try {
@@ -54,7 +56,7 @@ mixin ShorebirdCodePushClientMixin on ShorebirdConfigMixin {
}) async {
final codePushClient = buildCodePushClient(
httpClient: auth.client,
hostedUri: hostedUri,
hostedUri: ShorebirdEnvironment.hostedUri,
);
final createChannelProgress = logger.progress('Creating channel');
@@ -77,7 +79,7 @@ mixin ShorebirdCodePushClientMixin on ShorebirdConfigMixin {
}) async {
final codePushClient = buildCodePushClient(
httpClient: auth.client,
hostedUri: hostedUri,
hostedUri: ShorebirdEnvironment.hostedUri,
);
final List<Release> releases;
@@ -100,7 +102,7 @@ mixin ShorebirdCodePushClientMixin on ShorebirdConfigMixin {
}) async {
final codePushClient = buildCodePushClient(
httpClient: auth.client,
hostedUri: hostedUri,
hostedUri: ShorebirdEnvironment.hostedUri,
);
final releaseArtifacts = <Arch, ReleaseArtifact>{};
@@ -8,9 +8,10 @@ import 'package:yaml/yaml.dart';
import 'package:yaml_edit/yaml_edit.dart';
mixin ShorebirdConfigMixin on ShorebirdCommand {
bool get hasShorebirdYaml => getShorebirdYamlFile().existsSync();
bool get hasShorebirdYaml =>
ShorebirdEnvironment.getShorebirdYamlFile().existsSync();
bool get hasPubspecYaml => getPubspecYaml() != null;
bool get hasPubspecYaml => ShorebirdEnvironment.getPubspecYaml() != null;
bool get isShorebirdInitialized {
return hasShorebirdYaml && pubspecContainsShorebirdYaml;
@@ -29,7 +30,7 @@ mixin ShorebirdConfigMixin on ShorebirdCommand {
/// Returns the Android package name from the pubspec.yaml file of a Flutter
/// module.
String? get androidPackageName {
final pubspec = getPubspecYaml()!;
final pubspec = ShorebirdEnvironment.getPubspecYaml()!;
final module = pubspec.flutter?['module'] as Map?;
return module?['androidPackage'] as String?;
}
@@ -52,7 +53,8 @@ app_id:
if (flavors != null) editor.update(['flavors'], flavors);
getShorebirdYamlFile().writeAsStringSync(editor.toString());
ShorebirdEnvironment.getShorebirdYamlFile()
.writeAsStringSync(editor.toString());
return ShorebirdYaml(appId: appId);
}
@@ -1,6 +1,8 @@
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 {
@@ -9,7 +11,7 @@ mixin ShorebirdCreateAppMixin on ShorebirdConfigMixin {
if (appName == null) {
String? defaultAppName;
try {
defaultAppName = getPubspecYaml()?.name;
defaultAppName = ShorebirdEnvironment.getPubspecYaml()?.name;
} catch (_) {}
displayName = logger.prompt(
@@ -22,7 +24,7 @@ mixin ShorebirdCreateAppMixin on ShorebirdConfigMixin {
final client = buildCodePushClient(
httpClient: auth.client,
hostedUri: hostedUri,
hostedUri: ShorebirdEnvironment.hostedUri,
);
return client.createApp(displayName: displayName);
@@ -1,13 +1,14 @@
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';
abstract class ShorebirdEnvironment {
@visibleForTesting
static Platform platform = const LocalPlatform();
/// Environment variables from [Platform.environment].
static Map<String, String> get environment => platform.environment;
@@ -75,4 +76,45 @@ abstract class ShorebirdEnvironment {
'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!));
}
/// 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;
}
}
}
@@ -1,5 +1,6 @@
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/logger.dart';
import 'package:shorebird_cli/src/shorebird_config_mixin.dart';
import 'package:shorebird_cli/src/validators/validators.dart';
@@ -1,6 +1,7 @@
import 'dart:convert';
import 'dart:io';
import 'package:cli_util/cli_util.dart';
import 'package:googleapis_auth/googleapis_auth.dart';
import 'package:http/http.dart' as http;
import 'package:mason_logger/mason_logger.dart';
@@ -8,6 +9,7 @@ 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/command_runner.dart';
import 'package:shorebird_cli/src/logger.dart';
import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
import 'package:test/test.dart';
@@ -21,6 +23,16 @@ class _MockLogger extends Mock implements Logger {}
class _MockHttpClient extends Mock implements http.Client {}
void main() {
group('scoped', () {
test('creates instance with default constructor', () {
final instance = runScoped(() => auth, values: {authRef});
expect(
instance.credentialsFilePath,
p.join(applicationConfigHome(executableName), 'credentials.json'),
);
});
});
group('Auth', () {
const idToken =
'''eyJhbGciOiJSUzI1NiIsImN0eSI6IkpXVCJ9.eyJlbWFpbCI6InRlc3RAZW1haWwuY29tIn0.pD47BhF3MBLyIpfsgWCzP9twzC1HJxGukpcR36DqT6yfiOMHTLcjDbCjRLAnklWEHiT0BQTKTfhs8IousU90Fm5bVKObudfKu8pP5iZZ6Ls4ohDjTrXky9j3eZpZjwv8CnttBVgRfMJG-7YASTFRYFcOLUpnb4Zm5R6QdoCDUYg''';
+35 -15
View File
@@ -5,7 +5,9 @@ import 'package:http/http.dart' as http;
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/platform.dart';
import 'package:shorebird_cli/src/shorebird_environment.dart';
import 'package:test/test.dart';
@@ -32,6 +34,15 @@ void main() {
late Platform platform;
late Cache cache;
R runWithOverrides<R>(R Function() body) {
return runScoped(
() => body(),
values: {
platformRef.overrideWith(() => platform),
},
);
}
setUpAll(() {
registerFallbackValue(_FakeBaseRequest());
});
@@ -41,9 +52,9 @@ void main() {
platform = _MockPlatform();
shorebirdRoot = Directory.systemTemp.createTempSync();
ShorebirdEnvironment.platform = platform;
ShorebirdEnvironment.shorebirdEngineRevision = 'test-revision';
when(() => platform.environment).thenReturn({});
when(() => platform.isMacOS).thenReturn(true);
when(() => platform.isWindows).thenReturn(false);
when(() => platform.isLinux).thenReturn(false);
@@ -86,25 +97,32 @@ void main() {
group('clear', () {
test('deletes the cache directory', () async {
Cache.shorebirdCacheDirectory.createSync(recursive: true);
expect(Cache.shorebirdCacheDirectory.existsSync(), isTrue);
cache.clear();
expect(Cache.shorebirdCacheDirectory.existsSync(), isFalse);
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', () {
expect(Cache.shorebirdCacheDirectory.existsSync(), isFalse);
cache.clear();
expect(Cache.shorebirdCacheDirectory.existsSync(), isFalse);
final shorebirdCacheDirectory =
runWithOverrides(() => Cache.shorebirdCacheDirectory);
expect(shorebirdCacheDirectory.existsSync(), isFalse);
runWithOverrides(cache.clear);
expect(shorebirdCacheDirectory.existsSync(), isFalse);
});
});
group('updateAll', () {
group('patch', () {
test('downloads correct artifacts', () async {
expect(cache.getArtifactDirectory('patch').existsSync(), isFalse);
await expectLater(cache.updateAll(), completes);
expect(cache.getArtifactDirectory('patch').existsSync(), isTrue);
final patchArtifactDirectory = runWithOverrides(
() => cache.getArtifactDirectory('patch'),
);
expect(patchArtifactDirectory.existsSync(), isFalse);
await expectLater(runWithOverrides(cache.updateAll), completes);
expect(patchArtifactDirectory.existsSync(), isTrue);
});
test('pull correct artifact for MacOS', () async {
@@ -112,7 +130,8 @@ void main() {
when(() => platform.isWindows).thenReturn(false);
when(() => platform.isLinux).thenReturn(false);
await expectLater(cache.updateAll(), completes);
await expectLater(runWithOverrides(cache.updateAll), completes);
final request = verify(() => httpClient.send(captureAny()))
.captured
.first as http.BaseRequest;
@@ -132,7 +151,8 @@ void main() {
when(() => platform.isWindows).thenReturn(true);
when(() => platform.isLinux).thenReturn(false);
await expectLater(cache.updateAll(), completes);
await expectLater(runWithOverrides(cache.updateAll), completes);
final request = verify(() => httpClient.send(captureAny()))
.captured
.first as http.BaseRequest;
@@ -152,11 +172,11 @@ void main() {
when(() => platform.isWindows).thenReturn(false);
when(() => platform.isLinux).thenReturn(true);
await expectLater(cache.updateAll(), completes);
await expectLater(runWithOverrides(cache.updateAll), completes);
final request = verify(() => httpClient.send(captureAny()))
.captured
.first as http.BaseRequest;
expect(
request.url,
equals(
@@ -1,21 +1,64 @@
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/logger.dart';
import 'package:shorebird_cli/src/platform.dart';
import 'package:shorebird_cli/src/shorebird_build_mixin.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';
class _MockAuth extends Mock implements Auth {}
class _MockCodePushClient extends Mock implements CodePushClient {}
class _MockHttpClient extends Mock implements http.Client {}
class _MockLogger extends Mock implements Logger {}
class _MockPlatform extends Mock implements Platform {}
class _MockProgress extends Mock implements Progress {}
void main() {
group('scoped', () {
late Auth auth;
late http.Client httpClient;
late Platform platform;
setUp(() {
auth = _MockAuth();
httpClient = _MockHttpClient();
platform = _MockPlatform();
when(() => auth.client).thenReturn(httpClient);
when(() => platform.environment).thenReturn({
'SHOREBIRD_HOSTED_URL': 'http://example.com',
});
});
test('creates instance from scoped Auth and ShorebirdEnvironment', () {
final instance = runScoped(
() => codePushClientWrapper,
values: {
codePushClientWrapperRef,
authRef.overrideWith(() => auth),
platformRef.overrideWith(() => platform),
},
);
expect(
instance.codePushClient.hostedUri,
Uri.parse('http://example.com'),
);
verify(() => auth.client).called(1);
});
});
group(CodePushClientWrapper, () {
Matcher exitsWithCode(ExitCode exitcode) => throwsA(
isA<ProcessExit>().having(
@@ -31,7 +74,7 @@ void main() {
const patchId = 1;
const patchNumber = 2;
const patch = Patch(id: patchId, number: patchNumber);
const platform = 'ios';
const platformName = 'ios';
const releaseId = 123;
const arch = Arch.arm64;
const flutterRevision = '123';
@@ -62,7 +105,7 @@ void main() {
id: 1,
releaseId: releaseId,
arch: 'aarch64',
platform: platform,
platform: platformName,
hash: 'asdf',
size: 4,
url: 'url',
@@ -495,7 +538,7 @@ void main() {
() => codePushClientWrapper.getReleaseArtifacts(
releaseId: releaseId,
architectures: archMap,
platform: platform,
platform: platformName,
),
),
exitsWithCode(ExitCode.software),
@@ -517,7 +560,7 @@ void main() {
() => codePushClientWrapper.getReleaseArtifacts(
releaseId: releaseId,
architectures: archMap,
platform: platform,
platform: platformName,
),
);
@@ -542,7 +585,7 @@ void main() {
() => codePushClientWrapper.maybeGetReleaseArtifact(
releaseId: releaseId,
arch: arch.name,
platform: platform,
platform: platformName,
),
),
exitsWithCode(ExitCode.software),
@@ -564,7 +607,7 @@ void main() {
() => codePushClientWrapper.maybeGetReleaseArtifact(
releaseId: releaseId,
arch: arch.name,
platform: platform,
platform: platformName,
),
);
@@ -587,7 +630,7 @@ void main() {
() => codePushClientWrapper.maybeGetReleaseArtifact(
releaseId: releaseId,
arch: arch.name,
platform: platform,
platform: platformName,
),
);
@@ -652,7 +695,7 @@ void main() {
() async => runWithOverrides(
() async => codePushClientWrapper.createAndroidReleaseArtifacts(
releaseId: releaseId,
platform: platform,
platform: platformName,
aabPath: p.join(tempDir.path, aabPath),
architectures: ShorebirdBuildMixin.allAndroidArchitectures,
),
@@ -683,7 +726,7 @@ void main() {
() async => runWithOverrides(
() async => codePushClientWrapper.createAndroidReleaseArtifacts(
releaseId: releaseId,
platform: platform,
platform: platformName,
aabPath: p.join(tempDir.path, aabPath),
architectures: ShorebirdBuildMixin.allAndroidArchitectures,
),
@@ -714,7 +757,7 @@ void main() {
() async => IOOverrides.runZoned(
() async => codePushClientWrapper.createAndroidReleaseArtifacts(
releaseId: releaseId,
platform: platform,
platform: platformName,
aabPath: p.join(tempDir.path, aabPath),
architectures: ShorebirdBuildMixin.allAndroidArchitectures,
),
@@ -748,7 +791,7 @@ void main() {
() async => IOOverrides.runZoned(
() async => codePushClientWrapper.createAndroidReleaseArtifacts(
releaseId: releaseId,
platform: platform,
platform: platformName,
aabPath: p.join(tempDir.path, aabPath),
architectures: ShorebirdBuildMixin.allAndroidArchitectures,
),
@@ -780,7 +823,7 @@ void main() {
() async => IOOverrides.runZoned(
() async => codePushClientWrapper.createAndroidReleaseArtifacts(
releaseId: releaseId,
platform: platform,
platform: platformName,
aabPath: p.join(tempDir.path, aabPath),
architectures: ShorebirdBuildMixin.allAndroidArchitectures,
),
@@ -809,7 +852,7 @@ void main() {
() async => IOOverrides.runZoned(
() async => codePushClientWrapper.createAndroidReleaseArtifacts(
releaseId: releaseId,
platform: platform,
platform: platformName,
aabPath: p.join(tempDir.path, aabPath),
architectures: ShorebirdBuildMixin.allAndroidArchitectures,
flavor: flavorName,
@@ -824,7 +867,7 @@ void main() {
any(named: 'artifactPath', that: contains(flavorName)),
releaseId: releaseId,
arch: any(named: 'arch'),
platform: platform,
platform: platformName,
hash: any(named: 'hash'),
),
).called(ShorebirdBuildMixin.allAndroidArchitectures.length);
@@ -928,7 +971,7 @@ void main() {
() async => runWithOverrides(
() => codePushClientWrapper.createPatchArtifacts(
patch: patch,
platform: platform,
platform: platformName,
patchArtifactBundles: patchArtifactBundles,
),
),
@@ -953,7 +996,7 @@ void main() {
await runWithOverrides(
() => codePushClientWrapper.createPatchArtifacts(
patch: patch,
platform: platform,
platform: platformName,
patchArtifactBundles: patchArtifactBundles,
),
);
@@ -964,7 +1007,7 @@ void main() {
artifactPath: partchArtifactBundle.path,
patchId: patchId,
arch: arch.name,
platform: platform,
platform: platformName,
hash: partchArtifactBundle.hash,
),
).called(1);
@@ -1001,7 +1044,7 @@ void main() {
() => codePushClientWrapper.publishPatch(
appId: appId,
releaseId: releaseId,
platform: platform,
platform: platformName,
channelName: channelName,
patchArtifactBundles: patchArtifactBundles,
),
@@ -1015,7 +1058,7 @@ void main() {
artifactPath: partchArtifactBundle.path,
patchId: patchId,
arch: arch.name,
platform: platform,
platform: platformName,
hash: partchArtifactBundle.hash,
),
).called(1);
@@ -1050,7 +1093,7 @@ void main() {
() => codePushClientWrapper.publishPatch(
appId: appId,
releaseId: releaseId,
platform: platform,
platform: platformName,
channelName: channelName,
patchArtifactBundles: patchArtifactBundles,
),
@@ -1064,7 +1107,7 @@ void main() {
artifactPath: partchArtifactBundle.path,
patchId: patchId,
arch: arch.name,
platform: platform,
platform: platformName,
hash: partchArtifactBundle.hash,
),
).called(1);
@@ -1,7 +1,9 @@
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/shorebird_environment.dart';
@@ -9,33 +11,41 @@ import 'package:shorebird_cli/src/shorebird_process.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 _MockProcessResult extends Mock implements ShorebirdProcessResult {}
void main() {
group('ShorebirdCliCommandRunner', () {
group(ShorebirdCliCommandRunner, () {
late http.Client httpClient;
late Auth auth;
late Logger logger;
late ShorebirdProcessResult processResult;
late ShorebirdCliCommandRunner commandRunner;
R runWithOverrides<R>(R Function() body) {
return runScoped(body, values: {loggerRef.overrideWith(() => logger)});
}
ShorebirdCliCommandRunner buildRunner() {
return runScoped(
ShorebirdCliCommandRunner.new,
values: {loggerRef.overrideWith(() => logger)},
body,
values: {
authRef.overrideWith(() => auth),
loggerRef.overrideWith(() => logger)
},
);
}
setUp(() {
httpClient = _MockHttpClient();
auth = _MockAuth();
logger = _MockLogger();
ShorebirdEnvironment.shorebirdEngineRevision = 'test-revision';
processResult = _MockProcessResult();
when(() => auth.client).thenReturn(httpClient);
when(() => processResult.exitCode).thenReturn(ExitCode.success.code);
commandRunner = buildRunner();
commandRunner = runWithOverrides(ShorebirdCliCommandRunner.new);
});
test('handles FormatException', () async {
@@ -1,71 +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/command.dart';
import 'package:shorebird_cli/src/platform.dart';
import 'package:test/test.dart';
class _MockPlatform extends Mock implements Platform {}
class TestCommand extends ShorebirdCommand {
@override
String get description => 'test';
@override
String get name => 'test';
}
void main() {
group('ShorebirdCommand', () {
late Platform platform;
late ShorebirdCommand command;
R runWithOverrides<R>(R Function() body) {
return runScoped(
() => body(),
values: {
platformRef.overrideWith(() => platform),
},
);
}
setUp(() {
platform = _MockPlatform();
when(() => platform.environment).thenReturn(const {});
command = runWithOverrides(TestCommand.new);
});
group('hostedUrl', () {
test('returns hosted url from env if available', () {
when(() => platform.environment).thenReturn({
'SHOREBIRD_HOSTED_URL': 'https://example.com',
});
expect(
runWithOverrides(() => command.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(() => command.hostedUri),
getCurrentDirectory: () => directory,
),
equals(Uri.parse('https://example.com')),
);
});
test('returns null when there is no env override or shorebird.yaml', () {
expect(runWithOverrides(() => command.hostedUri), isNull);
});
});
});
}
@@ -29,7 +29,13 @@ void main() {
late CreateAccountCommand createAccountCommand;
R runWithOverrides<R>(R Function() body) {
return runScoped(body, values: {loggerRef.overrideWith(() => logger)});
return runScoped(
body,
values: {
authRef.overrideWith(() => auth),
loggerRef.overrideWith(() => logger)
},
);
}
setUp(() {
@@ -46,7 +52,7 @@ void main() {
when(() => user.displayName).thenReturn(userName);
when(() => user.email).thenReturn(email);
createAccountCommand = CreateAccountCommand(auth: auth);
createAccountCommand = runWithOverrides(CreateAccountCommand.new);
});
test('has a description', () {
@@ -34,7 +34,13 @@ void main() {
group(SubscribeAccountCommand, () {
R runWithOverrides<R>(R Function() body) {
return runScoped(body, values: {loggerRef.overrideWith(() => logger)});
return runScoped(
body,
values: {
authRef.overrideWith(() => auth),
loggerRef.overrideWith(() => logger)
},
);
}
setUp(() {
@@ -58,10 +64,12 @@ void main() {
when(() => user.hasActiveSubscription).thenReturn(false);
subscribeAccountCommand = SubscribeAccountCommand(
auth: auth,
buildCodePushClient: ({required httpClient, hostedUri}) =>
codePushClient,
subscribeAccountCommand = runWithOverrides(
() => SubscribeAccountCommand(
buildCodePushClient: ({required httpClient, hostedUri}) {
return codePushClient;
},
),
);
});
@@ -20,7 +20,7 @@ class _MockCodePushClient extends Mock implements CodePushClient {}
class _MockLogger extends Mock implements Logger {}
void main() {
group('create', () {
group(CreateAppCommand, () {
const appId = 'app-id';
const displayName = 'Example App';
@@ -32,7 +32,13 @@ void main() {
late CreateAppCommand command;
R runWithOverrides<R>(R Function() body) {
return runScoped(body, values: {loggerRef.overrideWith(() => logger)});
return runScoped(
body,
values: {
authRef.overrideWith(() => auth),
loggerRef.overrideWith(() => logger)
},
);
}
setUp(() {
@@ -45,14 +51,15 @@ void main() {
when(() => auth.client).thenReturn(httpClient);
when(() => auth.isAuthenticated).thenReturn(true);
command = CreateAppCommand(
auth: auth,
buildCodePushClient: ({
required http.Client httpClient,
Uri? hostedUri,
}) {
return codePushClient;
},
command = runWithOverrides(
() => CreateAppCommand(
buildCodePushClient: ({
required http.Client httpClient,
Uri? hostedUri,
}) {
return codePushClient;
},
),
)..testArgResults = argResults;
});
@@ -20,7 +20,7 @@ class _MockCodePushClient extends Mock implements CodePushClient {}
class _MockLogger extends Mock implements Logger {}
void main() {
group('delete', () {
group(DeleteAppCommand, () {
const appId = 'example';
late ArgResults argResults;
@@ -31,7 +31,13 @@ void main() {
late DeleteAppCommand command;
R runWithOverrides<R>(R Function() body) {
return runScoped(body, values: {loggerRef.overrideWith(() => logger)});
return runScoped(
body,
values: {
authRef.overrideWith(() => auth),
loggerRef.overrideWith(() => logger)
},
);
}
setUp(() {
@@ -44,14 +50,15 @@ void main() {
when(() => auth.isAuthenticated).thenReturn(true);
when(() => auth.client).thenReturn(httpClient);
command = DeleteAppCommand(
auth: auth,
buildCodePushClient: ({
required http.Client httpClient,
Uri? hostedUri,
}) {
return codePushClient;
},
command = runWithOverrides(
() => DeleteAppCommand(
buildCodePushClient: ({
required http.Client httpClient,
Uri? hostedUri,
}) {
return codePushClient;
},
),
)..testArgResults = argResults;
});
@@ -17,7 +17,7 @@ class _MockCodePushClient extends Mock implements CodePushClient {}
class _MockLogger extends Mock implements Logger {}
void main() {
group('list', () {
group(ListAppsCommand, () {
late http.Client httpClient;
late Auth auth;
late CodePushClient codePushClient;
@@ -25,7 +25,13 @@ void main() {
late ListAppsCommand command;
R runWithOverrides<R>(R Function() body) {
return runScoped(body, values: {loggerRef.overrideWith(() => logger)});
return runScoped(
body,
values: {
authRef.overrideWith(() => auth),
loggerRef.overrideWith(() => logger)
},
);
}
setUp(() {
@@ -37,14 +43,15 @@ void main() {
when(() => auth.isAuthenticated).thenReturn(true);
when(() => auth.client).thenReturn(httpClient);
command = ListAppsCommand(
auth: auth,
buildCodePushClient: ({
required http.Client httpClient,
Uri? hostedUri,
}) {
return codePushClient;
},
command = runWithOverrides(
() => ListAppsCommand(
buildCodePushClient: ({
required http.Client httpClient,
Uri? hostedUri,
}) {
return codePushClient;
},
),
);
});
@@ -64,7 +64,13 @@ flutter:
late BuildAarCommand command;
R runWithOverrides<R>(R Function() body) {
return runScoped(body, values: {loggerRef.overrideWith(() => logger)});
return runScoped(
body,
values: {
authRef.overrideWith(() => auth),
loggerRef.overrideWith(() => logger)
},
);
}
Directory setUpTempDir({bool includeModule = true}) {
@@ -105,7 +111,7 @@ flutter:
return processResult;
});
command = BuildAarCommand(auth: auth, validators: [])
command = runWithOverrides(() => BuildAarCommand(validators: []))
..testArgResults = argResults
..testProcess = shorebirdProcess
..testEngineConfig = const EngineConfig.empty();
@@ -42,7 +42,13 @@ void main() {
late ShorebirdProcess shorebirdProcess;
R runWithOverrides<R>(R Function() body) {
return runScoped(body, values: {loggerRef.overrideWith(() => logger)});
return runScoped(
body,
values: {
authRef.overrideWith(() => auth),
loggerRef.overrideWith(() => logger)
},
);
}
setUp(() {
@@ -70,9 +76,8 @@ void main() {
when(() => logger.info(any())).thenReturn(null);
when(() => flutterValidator.validate(any())).thenAnswer((_) async => []);
command = BuildApkCommand(
auth: auth,
validators: [flutterValidator],
command = runWithOverrides(
() => BuildApkCommand(validators: [flutterValidator]),
)
..testArgResults = argResults
..testProcess = shorebirdProcess
@@ -42,7 +42,13 @@ void main() {
late ShorebirdProcess shorebirdProcess;
R runWithOverrides<R>(R Function() body) {
return runScoped(body, values: {loggerRef.overrideWith(() => logger)});
return runScoped(
body,
values: {
authRef.overrideWith(() => auth),
loggerRef.overrideWith(() => logger)
},
);
}
setUp(() {
@@ -70,9 +76,8 @@ void main() {
when(() => logger.info(any())).thenReturn(null);
when(() => flutterValidator.validate(any())).thenAnswer((_) async => []);
command = BuildAppBundleCommand(
auth: auth,
validators: [flutterValidator],
command = runWithOverrides(
() => BuildAppBundleCommand(validators: [flutterValidator]),
)
..testArgResults = argResults
..testProcess = shorebirdProcess
@@ -43,7 +43,13 @@ void main() {
late ShorebirdProcess shorebirdProcess;
R runWithOverrides<R>(R Function() body) {
return runScoped(body, values: {loggerRef.overrideWith(() => logger)});
return runScoped(
body,
values: {
authRef.overrideWith(() => auth),
loggerRef.overrideWith(() => logger)
},
);
}
setUp(() {
@@ -72,9 +78,8 @@ void main() {
when(() => logger.info(any())).thenReturn(null);
when(() => flutterValidator.validate(any())).thenAnswer((_) async => []);
command = BuildIpaCommand(
auth: auth,
validators: [flutterValidator],
command = runWithOverrides(
() => BuildIpaCommand(validators: [flutterValidator]),
)
..testArgResults = argResults
..testProcess = shorebirdProcess
@@ -22,7 +22,7 @@ class _MockLogger extends Mock implements Logger {}
class _MockProgress extends Mock implements Progress {}
void main() {
group('create', () {
group(AddCollaboratorsCommand, () {
const appId = 'test-app-id';
const email = 'jane.doe@shorebird.dev';
@@ -35,7 +35,13 @@ void main() {
late AddCollaboratorsCommand command;
R runWithOverrides<R>(R Function() body) {
return runScoped(body, values: {loggerRef.overrideWith(() => logger)});
return runScoped(
body,
values: {
authRef.overrideWith(() => auth),
loggerRef.overrideWith(() => logger)
},
);
}
setUp(() {
@@ -53,14 +59,15 @@ void main() {
when(() => logger.confirm(any())).thenReturn(true);
when(() => logger.progress(any())).thenReturn(progress);
command = AddCollaboratorsCommand(
auth: auth,
buildCodePushClient: ({
required http.Client httpClient,
Uri? hostedUri,
}) {
return codePushClient;
},
command = runWithOverrides(
() => AddCollaboratorsCommand(
buildCodePushClient: ({
required http.Client httpClient,
Uri? hostedUri,
}) {
return codePushClient;
},
),
)..testArgResults = argResults;
});
@@ -25,7 +25,7 @@ class _MockLogger extends Mock implements Logger {}
class _MockProgress extends Mock implements Progress {}
void main() {
group('delete', () {
group(DeleteCollaboratorsCommand, () {
const appId = 'test-app-id';
const email = 'jane.doe@shorebird.dev';
const collaborator = Collaborator(userId: 0, email: email);
@@ -39,7 +39,13 @@ void main() {
late DeleteCollaboratorsCommand command;
R runWithOverrides<R>(R Function() body) {
return runScoped(body, values: {loggerRef.overrideWith(() => logger)});
return runScoped(
body,
values: {
authRef.overrideWith(() => auth),
loggerRef.overrideWith(() => logger)
},
);
}
setUp(() {
@@ -66,14 +72,15 @@ void main() {
),
).thenAnswer((_) async {});
command = DeleteCollaboratorsCommand(
auth: auth,
buildCodePushClient: ({
required http.Client httpClient,
Uri? hostedUri,
}) {
return codePushClient;
},
command = runWithOverrides(
() => DeleteCollaboratorsCommand(
buildCodePushClient: ({
required http.Client httpClient,
Uri? hostedUri,
}) {
return codePushClient;
},
),
)..testArgResults = argResults;
});
@@ -20,7 +20,7 @@ class _MockCodePushClient extends Mock implements CodePushClient {}
class _MockLogger extends Mock implements Logger {}
void main() {
group('collborators list', () {
group(ListCollaboratorsCommand, () {
const appId = 'test-app-id';
late ArgResults argResults;
@@ -31,7 +31,13 @@ void main() {
late ListCollaboratorsCommand command;
R runWithOverrides<R>(R Function() body) {
return runScoped(body, values: {loggerRef.overrideWith(() => logger)});
return runScoped(
body,
values: {
authRef.overrideWith(() => auth),
loggerRef.overrideWith(() => logger)
},
);
}
setUp(() {
@@ -45,14 +51,15 @@ void main() {
when(() => auth.client).thenReturn(httpClient);
when(() => auth.isAuthenticated).thenReturn(true);
command = ListCollaboratorsCommand(
auth: auth,
buildCodePushClient: ({
required http.Client httpClient,
Uri? hostedUri,
}) {
return codePushClient;
},
command = runWithOverrides(
() => ListCollaboratorsCommand(
buildCodePushClient: ({
required http.Client httpClient,
Uri? hostedUri,
}) {
return codePushClient;
},
),
)..testArgResults = argResults;
});
@@ -33,7 +33,7 @@ class _MockProcessResult extends Mock implements ShorebirdProcessResult {}
class _MockPlatform extends Mock implements Platform {}
void main() {
group('init', () {
group(InitCommand, () {
const version = '1.2.3';
const appId = 'test_app_id';
const appName = 'test_app_name';
@@ -56,7 +56,13 @@ environment:
late InitCommand command;
R runWithOverrides<R>(R Function() body) {
return runScoped(body, values: {loggerRef.overrideWith(() => logger)});
return runScoped(
body,
values: {
authRef.overrideWith(() => auth),
loggerRef.overrideWith(() => logger)
},
);
}
Directory setUpAppTempDir() {
@@ -106,14 +112,15 @@ environment:
when(() => result.exitCode).thenReturn(ExitCode.success.code);
when(() => result.stdout).thenReturn('');
command = InitCommand(
auth: auth,
buildCodePushClient: ({
required http.Client httpClient,
Uri? hostedUri,
}) {
return codePushClient;
},
command = runWithOverrides(
() => InitCommand(
buildCodePushClient: ({
required http.Client httpClient,
Uri? hostedUri,
}) {
return codePushClient;
},
),
)
..testProcess = process
..testArgResults = argResults;
@@ -17,7 +17,7 @@ class _MockHttpClient extends Mock implements http.Client {}
class _MockLogger extends Mock implements Logger {}
void main() {
group('login', () {
group(LoginCommand, () {
const email = 'test@email.com';
late Auth auth;
@@ -27,7 +27,13 @@ void main() {
late LoginCommand command;
R runWithOverrides<R>(R Function() body) {
return runScoped(body, values: {loggerRef.overrideWith(() => logger)});
return runScoped(
body,
values: {
authRef.overrideWith(() => auth),
loggerRef.overrideWith(() => logger)
},
);
}
setUp(() {
@@ -41,7 +47,7 @@ void main() {
p.join(applicationConfigHome.path, 'credentials.json'),
);
command = LoginCommand(auth: auth);
command = runWithOverrides(LoginCommand.new);
});
test('exits with code 0 when already logged in', () async {
@@ -16,14 +16,20 @@ class _MockHttpClient extends Mock implements http.Client {}
class _MockProgress extends Mock implements Progress {}
void main() {
group('logout', () {
group(LogoutCommand, () {
late Auth auth;
late Logger logger;
late http.Client httpClient;
late LogoutCommand command;
R runWithOverrides<R>(R Function() body) {
return runScoped(body, values: {loggerRef.overrideWith(() => logger)});
return runScoped(
body,
values: {
authRef.overrideWith(() => auth),
loggerRef.overrideWith(() => logger)
},
);
}
setUp(() {
@@ -34,7 +40,7 @@ void main() {
when(() => auth.client).thenReturn(httpClient);
when(() => logger.progress(any())).thenReturn(_MockProgress());
command = LogoutCommand(auth: auth);
command = runWithOverrides(LogoutCommand.new);
});
test('exits with code 0 when already logged out', () async {
@@ -12,6 +12,7 @@ import 'package:shorebird_cli/src/auth/auth.dart';
import 'package:shorebird_cli/src/cache.dart' show Cache;
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_build_mixin.dart';
import 'package:shorebird_cli/src/shorebird_environment.dart';
import 'package:shorebird_cli/src/shorebird_process.dart';
@@ -57,7 +58,7 @@ void main() {
const versionCode = '1';
const version = '$versionName+$versionCode';
const arch = 'aarch64';
const platform = 'android';
const platformName = 'android';
const channelName = 'stable';
const appDisplayName = 'Test App';
const appMetadata = AppMetadata(appId: appId, displayName: appDisplayName);
@@ -65,7 +66,7 @@ void main() {
id: 0,
patchId: 0,
arch: arch,
platform: platform,
platform: platformName,
hash: '#',
size: 42,
url: 'https://example.com',
@@ -74,7 +75,7 @@ void main() {
id: 0,
releaseId: 0,
arch: arch,
platform: platform,
platform: platformName,
hash: '#',
size: 42,
url: 'https://example.com/release.so',
@@ -83,7 +84,7 @@ void main() {
id: 0,
releaseId: 0,
arch: arch,
platform: platform,
platform: platformName,
hash: '#',
size: 42,
url: 'https://example.com/release.aar',
@@ -125,7 +126,7 @@ flutter:
late ArgResults argResults;
late Auth auth;
late Directory shorebirdRoot;
late Platform environmentPlatform;
late Platform platform;
late Progress progress;
late Logger logger;
late ShorebirdProcessResult flutterBuildProcessResult;
@@ -140,7 +141,14 @@ flutter:
late ShorebirdProcess shorebirdProcess;
R runWithOverrides<R>(R Function() body) {
return runScoped(body, values: {loggerRef.overrideWith(() => logger)});
return runScoped(
body,
values: {
authRef.overrideWith(() => auth),
loggerRef.overrideWith(() => logger),
platformRef.overrideWith(() => platform),
},
);
}
Directory setUpTempDir({bool includeModule = true}) {
@@ -193,7 +201,7 @@ flutter:
argResults = _MockArgResults();
auth = _MockAuth();
shorebirdRoot = Directory.systemTemp.createTempSync();
environmentPlatform = _MockPlatform();
platform = _MockPlatform();
progress = _MockProgress();
logger = _MockLogger();
flutterBuildProcessResult = _MockProcessResult();
@@ -205,8 +213,8 @@ flutter:
cache = _MockCache();
shorebirdProcess = _MockShorebirdProcess();
ShorebirdEnvironment.platform = environmentPlatform;
when(() => environmentPlatform.script).thenReturn(
when(() => platform.environment).thenReturn({});
when(() => platform.script).thenReturn(
Uri.file(
p.join(
shorebirdRoot.path,
@@ -328,20 +336,21 @@ flutter:
() => cache.getArtifactDirectory(any()),
).thenReturn(Directory.systemTemp.createTempSync());
command = PatchAarCommand(
aarDiffer: aarDiffer,
auth: auth,
buildCodePushClient: ({
required http.Client httpClient,
Uri? hostedUri,
}) {
capturedHostedUri = hostedUri;
return codePushClient;
},
cache: cache,
httpClient: httpClient,
validators: [flutterValidator],
unzipFn: (_, __) async {},
command = runWithOverrides(
() => PatchAarCommand(
aarDiffer: aarDiffer,
buildCodePushClient: ({
required http.Client httpClient,
Uri? hostedUri,
}) {
capturedHostedUri = hostedUri;
return codePushClient;
},
cache: cache,
httpClient: httpClient,
validators: [flutterValidator],
unzipFn: (_, __) async {},
),
)
..testArgResults = argResults
..testProcess = shorebirdProcess
@@ -471,13 +480,16 @@ Did you forget to run "shorebird init"?''',
const otherRevision = 'other-revision';
when(() => flutterRevisionProcessResult.stdout).thenReturn(otherRevision);
final tempDir = setUpTempDir();
final flutterDir =
runWithOverrides(() => ShorebirdEnvironment.flutterDirectory.path);
setUpTempArtifacts(tempDir);
final exitCode = await IOOverrides.runZoned(
() => runWithOverrides(command.run),
getCurrentDirectory: () => tempDir,
);
expect(exitCode, ExitCode.software.code);
final shorebirdFlutterPath = ShorebirdEnvironment.flutterDirectory.path;
verify(
() => logger.err('''
Flutter revision mismatch.
@@ -494,7 +506,7 @@ Either create a new release using:
${lightCyan.wrap('shorebird release aar')}
Or downgrade your Flutter version and try again using:
${lightCyan.wrap('cd $shorebirdFlutterPath')}
${lightCyan.wrap('cd $flutterDir')}
${lightCyan.wrap('git checkout ${release.flutterRevision}')}
Shorebird plans to support this automatically, let us know if it's important to you:
@@ -875,7 +887,7 @@ Please create a release using "shorebird release aar" and try again.
() => logger.info(
any(
that: contains(
'''🕹️ Platform: ${lightCyan.wrap(platform)} ${lightCyan.wrap('[arm64 (4 B), arm32 (4 B), x86_64 (4 B)]')}''',
'''🕹️ Platform: ${lightCyan.wrap(platformName)} ${lightCyan.wrap('[arm64 (4 B), arm32 (4 B), x86_64 (4 B)]')}''',
),
),
),
@@ -13,6 +13,7 @@ import 'package:shorebird_cli/src/cache.dart' show Cache;
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/logger.dart';
import 'package:shorebird_cli/src/platform.dart';
import 'package:shorebird_cli/src/shorebird_build_mixin.dart';
import 'package:shorebird_cli/src/shorebird_environment.dart';
import 'package:shorebird_cli/src/shorebird_process.dart';
@@ -58,7 +59,7 @@ void main() {
const versionCode = '1';
const version = '$versionName+$versionCode';
const arch = 'aarch64';
const platform = 'android';
const platformName = 'android';
const channelName = 'stable';
const appDisplayName = 'Test App';
const app = AppMetadata(appId: appId, displayName: appDisplayName);
@@ -66,7 +67,7 @@ void main() {
id: 0,
releaseId: 0,
arch: arch,
platform: platform,
platform: platformName,
hash: '#',
size: 42,
url: 'https://example.com',
@@ -75,7 +76,7 @@ void main() {
id: 0,
releaseId: 0,
arch: arch,
platform: platform,
platform: platformName,
hash: '#',
size: 42,
url: 'https://example.com/release.aab',
@@ -102,7 +103,7 @@ flutter:
late Auth auth;
late CodePushClientWrapper codePushClientWrapper;
late Directory shorebirdRoot;
late Platform environmentPlatform;
late Platform platform;
late Progress progress;
late Logger logger;
late ShorebirdProcessResult flutterBuildProcessResult;
@@ -117,7 +118,15 @@ flutter:
late ShorebirdProcess shorebirdProcess;
R runWithOverrides<R>(R Function() body) {
return runScoped(body, values: {loggerRef.overrideWith(() => logger)});
return runScoped(
body,
values: {
authRef.overrideWith(() => auth),
loggerRef.overrideWith(() => logger),
platformRef.overrideWith(() => platform),
codePushClientWrapperRef.overrideWith(() => codePushClientWrapper),
},
);
}
Directory setUpTempDir() {
@@ -161,7 +170,7 @@ flutter:
auth = _MockAuth();
codePushClientWrapper = _MockCodePushClientWrapper();
shorebirdRoot = Directory.systemTemp.createTempSync();
environmentPlatform = _MockPlatform();
platform = _MockPlatform();
progress = _MockProgress();
logger = _MockLogger();
flutterBuildProcessResult = _MockProcessResult();
@@ -173,20 +182,19 @@ flutter:
flutterValidator = _MockShorebirdFlutterValidator();
cache = _MockCache();
shorebirdProcess = _MockShorebirdProcess();
command = PatchAndroidCommand(
aabDiffer: aabDiffer,
auth: auth,
codePushClientWrapper: codePushClientWrapper,
cache: cache,
httpClient: httpClient,
validators: [flutterValidator],
command = runWithOverrides(
() => PatchAndroidCommand(
aabDiffer: aabDiffer,
cache: cache,
httpClient: httpClient,
validators: [flutterValidator],
),
)
..testArgResults = argResults
..testProcess = shorebirdProcess
..testEngineConfig = const EngineConfig.empty();
ShorebirdEnvironment.platform = environmentPlatform;
when(() => environmentPlatform.script).thenReturn(
when(() => platform.script).thenReturn(
Uri.file(
p.join(
shorebirdRoot.path,
@@ -421,7 +429,9 @@ flutter:
getCurrentDirectory: () => tempDir,
);
expect(exitCode, ExitCode.software.code);
final shorebirdFlutterPath = ShorebirdEnvironment.flutterDirectory.path;
final shorebirdFlutterPath = runWithOverrides(
() => ShorebirdEnvironment.flutterDirectory.path,
);
verify(
() => logger.err('''
Flutter revision mismatch.
@@ -723,7 +733,7 @@ https://github.com/shorebirdtech/shorebird/issues/472
() => logger.info(
any(
that: contains(
'''🕹️ Platform: ${lightCyan.wrap(platform)} ${lightCyan.wrap('[arm32 (4 B), arm64 (4 B), x86_64 (4 B)]')}''',
'''🕹️ Platform: ${lightCyan.wrap(platformName)} ${lightCyan.wrap('[arm32 (4 B), arm64 (4 B), x86_64 (4 B)]')}''',
),
),
),
@@ -5,6 +5,7 @@ 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:propertylistserialization/propertylistserialization.dart';
import 'package:scoped/scoped.dart';
import 'package:shorebird_cli/src/archive_analysis/archive_analysis.dart';
@@ -12,8 +13,10 @@ 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/logger.dart';
import 'package:shorebird_cli/src/platform.dart';
import 'package:shorebird_cli/src/shorebird_environment.dart';
import 'package:shorebird_cli/src/shorebird_process.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';
import 'package:test/test.dart';
@@ -33,6 +36,8 @@ class _MockIpa extends Mock implements Ipa {}
class _MockLogger extends Mock implements Logger {}
class _MockPlatform extends Mock implements Platform {}
class _MockProgress extends Mock implements Progress {}
class _MockProcessResult extends Mock implements ShorebirdProcessResult {}
@@ -54,7 +59,7 @@ void main() {
const version = '$versionName+$versionCode';
const arch = 'aarch64';
const appDisplayName = 'Test App';
const platform = 'ios';
const platformName = 'ios';
const elfAotSnapshotFileName = 'out.aot';
const pubspecYamlContent = '''
name: example
@@ -83,6 +88,7 @@ flutter:
late IpaReader ipaReader;
late Progress progress;
late Logger logger;
late Platform platform;
late ShorebirdProcessResult aotBuildProcessResult;
late ShorebirdProcessResult flutterBuildProcessResult;
late ShorebirdProcessResult flutterRevisionProcessResult;
@@ -92,7 +98,15 @@ flutter:
late PatchIosCommand command;
R runWithOverrides<R>(R Function() body) {
return runScoped(body, values: {loggerRef.overrideWith(() => logger)});
return runScoped(
body,
values: {
authRef.overrideWith(() => auth),
loggerRef.overrideWith(() => logger),
platformRef.overrideWith(() => platform),
codePushClientWrapperRef.overrideWith(() => codePushClientWrapper),
},
);
}
Directory setUpTempDir() {
@@ -138,6 +152,7 @@ flutter:
ipa = _MockIpa();
progress = _MockProgress();
logger = _MockLogger();
platform = _MockPlatform();
aotBuildProcessResult = _MockProcessResult();
flutterBuildProcessResult = _MockProcessResult();
flutterRevisionProcessResult = _MockProcessResult();
@@ -174,6 +189,17 @@ flutter:
when(() => flutterValidator.validate(any())).thenAnswer((_) async => []);
when(() => logger.confirm(any())).thenReturn(true);
when(() => logger.progress(any())).thenReturn(progress);
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)
@@ -206,11 +232,11 @@ flutter:
),
).thenAnswer((_) async => aotBuildProcessResult);
command = PatchIosCommand(
auth: auth,
codePushClientWrapper: codePushClientWrapper,
ipaReader: ipaReader,
validators: [flutterValidator],
command = runWithOverrides(
() => PatchIosCommand(
ipaReader: ipaReader,
validators: [flutterValidator],
),
)
..testArgResults = argResults
..testProcess = shorebirdProcess
@@ -303,7 +329,9 @@ flutter:
getCurrentDirectory: () => tempDir,
);
expect(exitCode, ExitCode.software.code);
final shorebirdFlutterPath = ShorebirdEnvironment.flutterDirectory.path;
final shorebirdFlutterPath = runWithOverrides(
() => ShorebirdEnvironment.flutterDirectory.path,
);
verify(
() => logger.err('''
Flutter revision mismatch.
@@ -426,7 +454,7 @@ https://github.com/shorebirdtech/shorebird/issues/472
() => logger.info(
any(
that: contains(
'''🕹️ Platform: ${lightCyan.wrap(platform)} ${lightCyan.wrap('[aarch64 (0 B)]')}''',
'''🕹️ Platform: ${lightCyan.wrap(platformName)} ${lightCyan.wrap('[aarch64 (0 B)]')}''',
),
),
),
@@ -54,12 +54,12 @@ void main() {
);
const arch = 'aarch64';
const platform = 'android';
const platformName = 'android';
const releaseArtifact = ReleaseArtifact(
id: 0,
releaseId: 0,
arch: arch,
platform: platform,
platform: platformName,
hash: '#',
size: 42,
url: 'https://example.com',
@@ -104,7 +104,13 @@ flutter:
late ShorebirdProcess shorebirdProcess;
R runWithOverrides<R>(R Function() body) {
return runScoped(body, values: {loggerRef.overrideWith(() => logger)});
return runScoped(
body,
values: {
authRef.overrideWith(() => auth),
loggerRef.overrideWith(() => logger)
},
);
}
Directory setUpTempDir({bool includeModule = true}) {
@@ -222,17 +228,18 @@ flutter:
when(() => flutterValidator.validate(any())).thenAnswer((_) async => []);
command = ReleaseAarCommand(
auth: auth,
buildCodePushClient: ({
required http.Client httpClient,
Uri? hostedUri,
}) {
capturedHostedUri = hostedUri;
return codePushClient;
},
unzipFn: (_, __) async {},
validators: [flutterValidator],
command = runWithOverrides(
() => ReleaseAarCommand(
buildCodePushClient: ({
required http.Client httpClient,
Uri? hostedUri,
}) {
capturedHostedUri = hostedUri;
return codePushClient;
},
unzipFn: (_, __) async {},
validators: [flutterValidator],
),
)
..testArgResults = argResults
..testProcess = shorebirdProcess
@@ -12,7 +12,7 @@ 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/logger.dart';
import 'package:shorebird_cli/src/shorebird_environment.dart';
import 'package:shorebird_cli/src/platform.dart';
import 'package:shorebird_cli/src/shorebird_process.dart';
import 'package:shorebird_cli/src/validators/validators.dart';
import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
@@ -51,7 +51,7 @@ void main() {
const version = '$versionName+$versionCode';
const appDisplayName = 'Test App';
const arch = 'aarch64';
const platform = 'android';
const platformName = 'android';
const appMetadata = AppMetadata(appId: appId, displayName: appDisplayName);
const release = Release(
id: 0,
@@ -75,7 +75,7 @@ flutter:
late http.Client httpClient;
late CodePushClientWrapper codePushClientWrapper;
late Directory shorebirdRoot;
late Platform environmentPlatform;
late Platform platform;
late Auth auth;
late Cache cache;
late Progress progress;
@@ -89,7 +89,15 @@ flutter:
late ShorebirdProcess shorebirdProcess;
R runWithOverrides<R>(R Function() body) {
return runScoped(body, values: {loggerRef.overrideWith(() => logger)});
return runScoped(
body,
values: {
authRef.overrideWith(() => auth),
loggerRef.overrideWith(() => logger),
platformRef.overrideWith(() => platform),
codePushClientWrapperRef.overrideWith(() => codePushClientWrapper),
},
);
}
Directory setUpTempDir() {
@@ -107,7 +115,7 @@ flutter:
argResults = _MockArgResults();
codePushClientWrapper = _MockCodePushClientWrapper();
httpClient = _MockHttpClient();
environmentPlatform = _MockPlatform();
platform = _MockPlatform();
shorebirdRoot = Directory.systemTemp.createTempSync();
auth = _MockAuth();
cache = _MockCache();
@@ -122,8 +130,7 @@ flutter:
registerFallbackValue(shorebirdProcess);
ShorebirdEnvironment.platform = environmentPlatform;
when(() => environmentPlatform.script).thenReturn(
when(() => platform.script).thenReturn(
Uri.file(
p.join(
shorebirdRoot.path,
@@ -163,7 +170,7 @@ flutter:
});
when(() => argResults.rest).thenReturn([]);
when(() => argResults['arch']).thenReturn(arch);
when(() => argResults['platform']).thenReturn(platform);
when(() => argResults['platform']).thenReturn(platformName);
when(() => auth.isAuthenticated).thenReturn(true);
when(() => auth.client).thenReturn(httpClient);
when(() => cache.updateAll()).thenAnswer((_) async => {});
@@ -223,11 +230,11 @@ flutter:
).thenAnswer((_) async {});
when(() => flutterValidator.validate(any())).thenAnswer((_) async => []);
command = ReleaseAndroidCommand(
auth: auth,
codePushClientWrapper: codePushClientWrapper,
cache: cache,
validators: [flutterValidator],
command = runWithOverrides(
() => ReleaseAndroidCommand(
cache: cache,
validators: [flutterValidator],
),
)
..testArgResults = argResults
..testProcess = shorebirdProcess
@@ -423,7 +430,7 @@ Please bump your version number and try again.'''),
verify(
() => codePushClientWrapper.createAndroidReleaseArtifacts(
releaseId: release.id,
platform: platform,
platform: platformName,
aabPath: any(named: 'aabPath'),
architectures: any(named: 'architectures'),
),
@@ -455,7 +462,7 @@ flavors:
verify(
() => codePushClientWrapper.createAndroidReleaseArtifacts(
releaseId: release.id,
platform: platform,
platform: platformName,
aabPath: any(named: 'aabPath'),
architectures: any(named: 'architectures'),
flavor: flavor,
@@ -13,7 +13,7 @@ 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_environment.dart';
import 'package:shorebird_cli/src/platform.dart';
import 'package:shorebird_cli/src/shorebird_process.dart';
import 'package:shorebird_cli/src/validators/validators.dart';
import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
@@ -54,7 +54,7 @@ void main() {
const version = '$versionName+$versionCode';
const appDisplayName = 'Test App';
const arch = 'armv7';
const platform = 'ios';
const platformName = 'ios';
const appMetadata = AppMetadata(appId: appId, displayName: appDisplayName);
const release = Release(
id: 0,
@@ -91,7 +91,15 @@ flutter:
late ShorebirdProcess shorebirdProcess;
R runWithOverrides<R>(R Function() body) {
return runScoped(body, values: {loggerRef.overrideWith(() => logger)});
return runScoped(
body,
values: {
authRef.overrideWith(() => auth),
loggerRef.overrideWith(() => logger),
platformRef.overrideWith(() => environmentPlatform),
codePushClientWrapperRef.overrideWith(() => codePushClientWrapper),
},
);
}
Directory setUpTempDir() {
@@ -124,7 +132,6 @@ flutter:
registerFallbackValue(shorebirdProcess);
ShorebirdEnvironment.platform = environmentPlatform;
when(() => environmentPlatform.script).thenReturn(
Uri.file(
p.join(
@@ -152,7 +159,7 @@ flutter:
).thenAnswer((_) async => flutterRevisionProcessResult);
when(() => argResults.rest).thenReturn([]);
when(() => argResults['arch']).thenReturn(arch);
when(() => argResults['platform']).thenReturn(platform);
when(() => argResults['platform']).thenReturn(platformName);
when(() => auth.isAuthenticated).thenReturn(true);
when(() => auth.client).thenReturn(httpClient);
when(() => ipaReader.read(any())).thenReturn(ipa);
@@ -189,11 +196,11 @@ flutter:
).thenAnswer((_) async => release);
when(() => flutterValidator.validate(any())).thenAnswer((_) async => []);
command = ReleaseIosCommand(
auth: auth,
codePushClientWrapper: codePushClientWrapper,
ipaReader: ipaReader,
validators: [flutterValidator],
command = runWithOverrides(
() => ReleaseIosCommand(
ipaReader: ipaReader,
validators: [flutterValidator],
),
)
..testArgResults = argResults
..testProcess = shorebirdProcess
@@ -50,7 +50,13 @@ flutter:
late DeleteReleasesCommand command;
R runWithOverrides<R>(R Function() body) {
return runScoped(body, values: {loggerRef.overrideWith(() => logger)});
return runScoped(
body,
values: {
authRef.overrideWith(() => auth),
loggerRef.overrideWith(() => logger)
},
);
}
Directory setUpTempDir() {
@@ -107,14 +113,15 @@ flutter:
when(() => logger.confirm(any())).thenReturn(true);
when(() => logger.progress(any())).thenReturn(progress);
command = DeleteReleasesCommand(
auth: auth,
buildCodePushClient: ({
required http.Client httpClient,
Uri? hostedUri,
}) {
return codePushClient;
},
command = runWithOverrides(
() => DeleteReleasesCommand(
buildCodePushClient: ({
required http.Client httpClient,
Uri? hostedUri,
}) {
return codePushClient;
},
),
)..testArgResults = argResults;
});
@@ -45,7 +45,13 @@ flutter:
- shorebird.yaml''';
R runWithOverrides<R>(R Function() body) {
return runScoped(body, values: {loggerRef.overrideWith(() => logger)});
return runScoped(
body,
values: {
authRef.overrideWith(() => auth),
loggerRef.overrideWith(() => logger)
},
);
}
Directory setUpTempDir() {
@@ -69,13 +75,12 @@ flutter:
when(() => auth.client).thenReturn(httpClient);
when(() => auth.isAuthenticated).thenReturn(true);
command = ListReleasesCommand(
auth: auth,
buildCodePushClient: ({
required httpClient,
hostedUri,
}) =>
codePushClient,
command = runWithOverrides(
() => ListReleasesCommand(
buildCodePushClient: ({required httpClient, hostedUri}) {
return codePushClient;
},
),
)..testArgResults = argResults;
});
@@ -51,7 +51,13 @@ void main() {
late RunCommand command;
R runWithOverrides<R>(R Function() body) {
return runScoped(body, values: {loggerRef.overrideWith(() => logger)});
return runScoped(
body,
values: {
authRef.overrideWith(() => auth),
loggerRef.overrideWith(() => logger)
},
);
}
setUp(() {
@@ -84,18 +90,19 @@ void main() {
).thenAnswer((_) async => []);
when(() => flutterValidator.validate(any())).thenAnswer((_) async => []);
command = RunCommand(
auth: auth,
buildCodePushClient: ({
required http.Client httpClient,
Uri? hostedUri,
}) {
return codePushClient;
},
validators: [
androidInternetPermissionValidator,
flutterValidator,
],
command = runWithOverrides(
() => RunCommand(
buildCodePushClient: ({
required http.Client httpClient,
Uri? hostedUri,
}) {
return codePushClient;
},
validators: [
androidInternetPermissionValidator,
flutterValidator,
],
),
)
..testArgResults = argResults
..testProcess = shorebirdProcess
@@ -19,7 +19,7 @@ class _MockLogger extends Mock implements Logger {}
class _MockProgress extends Mock implements Progress {}
void main() {
group('CancelSubscriptionCommand', () {
group(CancelSubscriptionCommand, () {
const noSubscriptionUser = User(id: 1, email: 'tester1@shorebird.dev');
const subscriptionUser = User(
id: 2,
@@ -35,7 +35,13 @@ void main() {
late CancelSubscriptionCommand command;
R runWithOverrides<R>(R Function() body) {
return runScoped(body, values: {loggerRef.overrideWith(() => logger)});
return runScoped(
body,
values: {
authRef.overrideWith(() => auth),
loggerRef.overrideWith(() => logger)
},
);
}
setUp(() {
@@ -49,13 +55,15 @@ void main() {
when(() => logger.progress(any())).thenReturn(progress);
command = CancelSubscriptionCommand(
auth: auth,
buildCodePushClient: ({
required http.Client httpClient,
Uri? hostedUri,
}) =>
codePushClient,
command = runWithOverrides(
() => CancelSubscriptionCommand(
buildCodePushClient: ({
required http.Client httpClient,
Uri? hostedUri,
}) {
return codePushClient;
},
),
);
});
@@ -3,6 +3,8 @@ 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';
@@ -14,14 +16,23 @@ void main() {
late Directory shorebirdRoot;
late Uri platformScript;
R runWithOverrides<R>(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();
ShorebirdEnvironment.platform = platform;
when(() => platform.environment).thenReturn(const {});
when(() => platform.script).thenReturn(platformScript);
});
@@ -31,7 +42,10 @@ void main() {
File(p.join(shorebirdRoot.path, 'bin', 'internal', 'flutter.version'))
..createSync(recursive: true)
..writeAsStringSync(revision, flush: true);
expect(ShorebirdEnvironment.flutterRevision, equals(revision));
expect(
runWithOverrides(() => ShorebirdEnvironment.flutterRevision),
equals(revision),
);
});
test('trims revision file content', () {
@@ -45,7 +59,10 @@ test-revision
..createSync(recursive: true)
..writeAsStringSync(revision, flush: true);
expect(ShorebirdEnvironment.flutterRevision, 'test-revision');
expect(
runWithOverrides(() => ShorebirdEnvironment.flutterRevision),
'test-revision',
);
});
});
@@ -65,7 +82,40 @@ test-revision
)
..createSync(recursive: true)
..writeAsStringSync(revision, flush: true);
expect(ShorebirdEnvironment.shorebirdEngineRevision, equals(revision));
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);
});
});
});
@@ -3,6 +3,8 @@ import 'dart:io' hide Platform;
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:shorebird_cli/src/shorebird_process.dart';
import 'package:shorebird_cli/src/validators/validators.dart';
@@ -42,6 +44,16 @@ Tools • Dart 2.19.6 • DevTools 2.20.1
late ShorebirdProcessResult shorebirdFlutterVersionProcessResult;
late ShorebirdProcessResult gitStatusProcessResult;
late ShorebirdProcess shorebirdProcess;
late Platform platform;
R runWithOverrides<R>(R Function() body) {
return runScoped(
() => body(),
values: {
platformRef.overrideWith(() => platform),
},
);
}
Directory flutterDirectory(Directory root) =>
Directory(p.join(root.path, 'bin', 'cache', 'flutter'));
@@ -58,12 +70,11 @@ Tools • Dart 2.19.6 • DevTools 2.20.1
setUp(() {
tempDir = setupTempDirectory();
platform = _MockPlatform();
ShorebirdEnvironment.platform = _MockPlatform();
ShorebirdEnvironment.flutterRevision = flutterRevision;
when(() => ShorebirdEnvironment.platform.script)
.thenReturn(shorebirdScriptFile(tempDir).uri);
when(() => ShorebirdEnvironment.platform.environment).thenReturn({});
when(() => platform.script).thenReturn(shorebirdScriptFile(tempDir).uri);
when(() => platform.environment).thenReturn({});
pathFlutterVersionProcessResult = _MockProcessResult();
shorebirdFlutterVersionProcessResult = _MockProcessResult();
@@ -106,7 +117,9 @@ Tools • Dart 2.19.6 • DevTools 2.20.1
});
test('returns no issues when the Flutter install is good', () async {
final results = await validator.validate(shorebirdProcess);
final results = await runWithOverrides(
() => validator.validate(shorebirdProcess),
);
expect(results, isEmpty);
});
@@ -125,7 +138,9 @@ Tools • Dart 2.19.6 • DevTools 2.20.1
when(() => gitStatusProcessResult.stdout)
.thenReturn('Changes not staged for commit');
final results = await validator.validate(shorebirdProcess);
final results = await runWithOverrides(
() => validator.validate(shorebirdProcess),
);
expect(results, hasLength(1));
expect(results.first.severity, ValidationIssueSeverity.warning);
@@ -140,7 +155,9 @@ Tools • Dart 2.19.6 • DevTools 2.20.1
pathFlutterVersionMessage.replaceAll('3.7.9', '3.7.10'),
);
final results = await validator.validate(shorebirdProcess);
final results = await runWithOverrides(
() => validator.validate(shorebirdProcess),
);
expect(results, isEmpty);
},
@@ -154,7 +171,9 @@ Tools • Dart 2.19.6 • DevTools 2.20.1
pathFlutterVersionMessage.replaceAll('3.7.9', '3.8.9'),
);
final results = await validator.validate(shorebirdProcess);
final results = await runWithOverrides(
() => validator.validate(shorebirdProcess),
);
expect(results, hasLength(1));
expect(results.first.severity, ValidationIssueSeverity.warning);
@@ -171,11 +190,13 @@ Tools • Dart 2.19.6 • DevTools 2.20.1
test(
'warns if FLUTTER_STORAGE_BASE_URL has a non-empty value',
() async {
when(() => ShorebirdEnvironment.platform.environment).thenReturn(
when(() => platform.environment).thenReturn(
{'FLUTTER_STORAGE_BASE_URL': 'https://storage.flutter-io.cn'},
);
final results = await validator.validate(shorebirdProcess);
final results = await runWithOverrides(
() => validator.validate(shorebirdProcess),
);
expect(results, hasLength(1));
expect(results.first.severity, ValidationIssueSeverity.warning);
@@ -194,7 +215,9 @@ Tools • Dart 2.19.6 • DevTools 2.20.1
when(() => pathFlutterVersionProcessResult.stdout)
.thenReturn('OH NO THERE IS NO FLUTTER VERSION HERE');
final results = await validator.validate(shorebirdProcess);
final results = await runWithOverrides(
() => validator.validate(shorebirdProcess),
);
expect(results, hasLength(1));
expect(
@@ -213,7 +236,9 @@ Tools • Dart 2.19.6 • DevTools 2.20.1
when(() => pathFlutterVersionProcessResult.stderr)
.thenReturn('error getting Flutter version');
final results = await validator.validate(shorebirdProcess);
final results = await runWithOverrides(
() => validator.validate(shorebirdProcess),
);
expect(results, hasLength(1));
expect(
@@ -231,7 +256,9 @@ Tools • Dart 2.19.6 • DevTools 2.20.1
when(() => shorebirdFlutterVersionProcessResult.stdout)
.thenReturn('OH NO THERE IS NO FLUTTER VERSION HERE');
final results = await validator.validate(shorebirdProcess);
final results = await runWithOverrides(
() => validator.validate(shorebirdProcess),
);
expect(results, hasLength(1));
expect(
@@ -250,7 +277,9 @@ Tools • Dart 2.19.6 • DevTools 2.20.1
when(() => shorebirdFlutterVersionProcessResult.stderr)
.thenReturn('error getting Flutter version');
final results = await validator.validate(shorebirdProcess);
final results = await runWithOverrides(
() => validator.validate(shorebirdProcess),
);
expect(results, hasLength(1));
expect(