feat(shorebird_cli): add new flavors to shorebird.yaml if detected by shorebird init (#1251)

This commit is contained in:
Bryan Oltman
2023-09-12 13:19:21 -04:00
committed by GitHub
parent 8c545281c9
commit bdd3db431f
2 changed files with 153 additions and 13 deletions
@@ -13,6 +13,7 @@ import 'package:shorebird_cli/src/platform.dart';
import 'package:shorebird_cli/src/shorebird_env.dart';
import 'package:shorebird_cli/src/shorebird_validator.dart';
import 'package:shorebird_cli/src/xcodebuild.dart';
import 'package:shorebird_code_push_client/shorebird_code_push_client.dart';
import 'package:yaml/yaml.dart';
import 'package:yaml_edit/yaml_edit.dart';
@@ -63,13 +64,6 @@ Please make sure you are running "shorebird init" from the root of your Flutter
final force = results['force'] == true;
if (!force && shorebirdEnv.hasShorebirdYaml) {
logger.err('''
A "shorebird.yaml" already exists.
If you want to reinitialize Shorebird, please run "shorebird init --force".''');
return ExitCode.software.code;
}
Set<String>? androidFlavors;
Set<String>? iosFlavors;
var productFlavors = <String>{};
@@ -92,6 +86,60 @@ If you want to reinitialize Shorebird, please run "shorebird init --force".''');
return ExitCode.software.code;
}
final shorebirdYaml = shorebirdEnv.getShorebirdYaml();
final existingFlavors = shorebirdYaml?.flavors;
Set<String> newFlavors;
if (existingFlavors != null) {
final existingFlavorNames = existingFlavors.keys.toSet();
newFlavors = productFlavors.difference(existingFlavorNames);
} else {
newFlavors = {};
}
// New flavors not being empty means that we have existing flavors, which
// means that there is already an existing app.
// If the --force flag is present, we will completely reinit the app and
// don't care about which flavors are new.
if (!force && newFlavors.isNotEmpty) {
logger.info('New flavors detected: ${newFlavors.join(', ')}');
final updateShorebirdYamlProgress =
logger.progress('Adding flavors to shorebird.yaml');
final AppMetadata existingApp;
try {
existingApp =
await codePushClientWrapper.getApp(appId: shorebirdYaml!.appId);
} catch (e) {
updateShorebirdYamlProgress.fail('Failed to get existing app info: $e');
return ExitCode.software.code;
}
final deflavoredAppName =
existingApp.displayName.replaceAll(RegExp(r'\(.*\)'), '').trim();
final flavorsToAppIds = shorebirdYaml.flavors!;
for (final flavor in newFlavors) {
final app = await codePushClientWrapper.createApp(
appName: '$deflavoredAppName ($flavor)',
);
flavorsToAppIds[flavor] = app.id;
}
_addShorebirdYamlToProject(
shorebirdYaml.appId,
flavors: flavorsToAppIds,
);
updateShorebirdYamlProgress.complete('Flavors added to shorebird.yaml');
return ExitCode.success.code;
}
if (!force && shorebirdEnv.hasShorebirdYaml) {
logger
..err('A "shorebird.yaml" file already exists and seems up-to-date.')
..info(
'''If you want to reinitialize Shorebird, please run ${lightCyan.wrap('shorebird init --force')}.''',
);
return ExitCode.software.code;
}
final String appId;
Map<String, String>? flavors;
try {
@@ -8,6 +8,7 @@ import 'package:platform/platform.dart';
import 'package:scoped/scoped.dart';
import 'package:shorebird_cli/src/code_push_client_wrapper.dart';
import 'package:shorebird_cli/src/commands/init_command.dart';
import 'package:shorebird_cli/src/config/config.dart';
import 'package:shorebird_cli/src/doctor.dart';
import 'package:shorebird_cli/src/gradlew.dart';
import 'package:shorebird_cli/src/logger.dart';
@@ -38,6 +39,8 @@ class _MockProgress extends Mock implements Progress {}
class _MockShorebirdEnv extends Mock implements ShorebirdEnv {}
class _MockShorebirdYaml extends Mock implements ShorebirdYaml {}
class _MockShorebirdValidator extends Mock implements ShorebirdValidator {}
class _MockXcodeBuild extends Mock implements XcodeBuild {}
@@ -59,6 +62,7 @@ environment:
late Gradlew gradlew;
late CodePushClientWrapper codePushClientWrapper;
late File shorebirdYamlFile;
late ShorebirdYaml shorebirdYaml;
late File pubspecYamlFile;
late Logger logger;
late Platform platform;
@@ -90,6 +94,7 @@ environment:
doctor = _MockDoctor();
gradlew = _MockGradlew();
codePushClientWrapper = _MockCodePushClientWrapper();
shorebirdYaml = _MockShorebirdYaml();
shorebirdYamlFile = _MockFile();
pubspecYamlFile = _MockFile();
logger = _MockLogger();
@@ -184,9 +189,11 @@ Please make sure you are running "shorebird init" from the root of your Flutter
final exitCode = await runWithOverrides(command.run);
verify(
() => logger.err(
'''
A "shorebird.yaml" already exists.
If you want to reinitialize Shorebird, please run "shorebird init --force".''',
'A "shorebird.yaml" file already exists and seems up-to-date.'),
).called(1);
verify(
() => logger.info(
'''If you want to reinitialize Shorebird, please run ${lightCyan.wrap('shorebird init --force')}.''',
),
).called(1);
expect(exitCode, ExitCode.software.code);
@@ -198,9 +205,7 @@ If you want to reinitialize Shorebird, please run "shorebird init --force".''',
final exitCode = await runWithOverrides(command.run);
verifyNever(
() => logger.err(
'''
A "shorebird.yaml" already exists.
If you want to reinitialize Shorebird, please run "shorebird init --force".''',
'A "shorebird.yaml" file already exists and seems up-to-date.',
),
);
expect(exitCode, ExitCode.success.code);
@@ -786,6 +791,93 @@ flavors:
),
]);
});
group('with new flavors added', () {
final existingFlavors = {
'a': 'test-appId-1',
'b': 'test-appId-2',
};
setUp(() {
const androidVariants = {'a', 'b', 'c', 'd'};
when(
() => gradlew.productFlavors(any()),
).thenAnswer((_) async => androidVariants);
when(() => shorebirdEnv.hasShorebirdYaml).thenReturn(true);
when(() => shorebirdEnv.getShorebirdYaml()).thenReturn(shorebirdYaml);
when(() => shorebirdYaml.appId).thenReturn(appId);
when(() => shorebirdYaml.flavors).thenReturn(existingFlavors);
});
test('exits with software error if retrieving existing app fails',
() async {
when(() => codePushClientWrapper.getApp(appId: any(named: 'appId')))
.thenThrow(Exception('oh no'));
final result = await runWithOverrides(command.run);
expect(result, ExitCode.software.code);
});
test('creates new flavor entries in shorebird.yaml', () async {
const newAppIds = [
'test-appId-3',
'test-appId-4',
];
const appName = 'my-app';
var index = 0;
when(() => codePushClientWrapper.getApp(appId: any(named: 'appId')))
.thenAnswer(
(_) async => const AppMetadata(appId: appId, displayName: appName),
);
when(
() =>
codePushClientWrapper.createApp(appName: any(named: 'appName')),
).thenAnswer((invocation) async {
final appName = invocation.namedArguments[#appName] as String?;
return App(id: newAppIds[index++], displayName: appName ?? '-');
});
await runWithOverrides(command.run);
verify(() => logger.info('New flavors detected: c, d')).called(1);
verifyNever(
() => codePushClientWrapper.createApp(
appName: '$appName (a)',
),
);
verifyNever(
() => codePushClientWrapper.createApp(
appName: '$appName (b)',
),
);
verify(
() => codePushClientWrapper.createApp(
appName: '$appName (c)',
),
).called(1);
verify(
() => codePushClientWrapper.createApp(
appName: '$appName (d)',
),
).called(1);
verify(
() => shorebirdYamlFile.writeAsStringSync(
any(
that: contains(
'''
app_id: test_app_id
flavors:
a: test-appId-1
b: test-appId-2
c: test-appId-3
d: test-appId-4''',
),
),
),
).called(1);
});
});
});
test('detects existing shorebird.yaml in pubspec.yaml assets', () async {