Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(shorebird_cli): shorebird build support --flavor and --target #409

Merged
merged 2 commits into from
May 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,18 @@ class BuildApkCommand extends ShorebirdCommand
required super.logger,
super.auth,
super.validators,
});
}) {
argParser
..addOption(
'target',
abbr: 't',
help: 'The main entrypoint file of the application.',
)
..addOption(
'flavor',
help: 'The product flavor to use when building the app.',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be helpful to give examples of flavors or otherwise hint how a developer might use them, either here or in the readme.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good call I'll update the README in one go in the next PR

);
}

@override
String get description => 'Build an Android APK file from your app.';
Expand All @@ -40,19 +51,25 @@ class BuildApkCommand extends ShorebirdCommand

await logValidationIssues();

final flavor = results['flavor'] as String?;
final target = results['target'] as String?;
final buildProgress = logger.progress('Building apk');
try {
await buildApk();
await buildApk(flavor: flavor, target: target);
} on ProcessException catch (error) {
buildProgress.fail('Failed to build: ${error.message}');
return ExitCode.software.code;
}

buildProgress.complete();

final apkPath = flavor != null
? './build/app/outputs/apk/$flavor/release/app-$flavor-release.apk'
: './build/app/outputs/apk/release/app-release.apk';

logger.info('''
📦 Generated an apk at:
${lightCyan.wrap("./build/app/outputs/apk/release/app-release.apk")}''');
${lightCyan.wrap(apkPath)}''');

return ExitCode.success.code;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,18 @@ class BuildAppBundleCommand extends ShorebirdCommand
required super.logger,
super.auth,
super.validators,
});
}) {
argParser
..addOption(
'target',
abbr: 't',
help: 'The main entrypoint file of the application.',
)
..addOption(
'flavor',
help: 'The product flavor to use when building the app.',
);
}

@override
String get description => 'Build an Android App Bundle file from your app.';
Expand All @@ -40,18 +51,24 @@ class BuildAppBundleCommand extends ShorebirdCommand

await logValidationIssues();

final flavor = results['flavor'] as String?;
final target = results['target'] as String?;
final buildProgress = logger.progress('Building appbundle');
try {
await buildAppBundle();
await buildAppBundle(flavor: flavor, target: target);
} on ProcessException catch (error) {
buildProgress.fail('Failed to build: ${error.message}');
return ExitCode.software.code;
}

final bundlePath = flavor != null
? './build/app/outputs/bundle/${flavor}Release/app-$flavor-release.aab'
: './build/app/outputs/bundle/release/app-release.aab';

buildProgress.complete();
logger.info('''
📦 Generated an app bundle at:
${lightCyan.wrap("./build/app/outputs/bundle/release/app-release.aab")}''');
${lightCyan.wrap(bundlePath)}''');

return ExitCode.success.code;
}
Expand Down
8 changes: 6 additions & 2 deletions packages/shorebird_cli/lib/src/shorebird_build_mixin.dart
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,14 @@ mixin ShorebirdBuildMixin on ShorebirdCommand {
return allAndroidArchitectures;
}

Future<void> buildAppBundle() async {
Future<void> buildAppBundle({String? flavor, String? target}) async {
const executable = 'flutter';
final arguments = [
'build',
'appbundle',
'--release',
if (flavor != null) '--flavor=$flavor',
if (target != null) '--target=$target',
...results.rest,
];

Expand All @@ -91,12 +93,14 @@ mixin ShorebirdBuildMixin on ShorebirdCommand {
}
}

Future<void> buildApk() async {
Future<void> buildApk({String? flavor, String? target}) async {
const executable = 'flutter';
final arguments = [
'build',
'apk',
'--release',
if (flavor != null) '--flavor=$flavor',
if (target != null) '--target=$target',
...results.rest,
];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,44 @@ ${lightCyan.wrap("./build/app/outputs/apk/release/app-release.apk")}''',
).called(1);
});

test(
'exits with code 0 when building apk succeeds '
'with flavor and target', () async {
const flavor = 'development';
const target = './lib/main_development.dart';
when(() => argResults['flavor']).thenReturn(flavor);
when(() => argResults['target']).thenReturn(target);
when(() => processResult.exitCode).thenReturn(ExitCode.success.code);
final tempDir = Directory.systemTemp.createTempSync();
final result = await IOOverrides.runZoned(
() async => command.run(),
getCurrentDirectory: () => tempDir,
);

expect(result, equals(ExitCode.success.code));

verify(
() => shorebirdProcess.run(
'flutter',
[
'build',
'apk',
'--release',
'--flavor=$flavor',
'--target=$target',
],
runInShell: true,
),
).called(1);
verify(
() => logger.info(
'''
📦 Generated an apk at:
${lightCyan.wrap("./build/app/outputs/apk/$flavor/release/app-$flavor-release.apk")}''',
),
).called(1);
});

test('prints flutter validation warnings', () async {
when(() => flutterValidator.validate(any())).thenAnswer(
(_) async => [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,44 @@ ${lightCyan.wrap("./build/app/outputs/bundle/release/app-release.aab")}''',
).called(1);
});

test(
'exits with code 0 when building appbundle succeeds '
'with flavor and target', () async {
const flavor = 'development';
const target = './lib/main_development.dart';
when(() => argResults['flavor']).thenReturn(flavor);
when(() => argResults['target']).thenReturn(target);
when(() => processResult.exitCode).thenReturn(ExitCode.success.code);
final tempDir = Directory.systemTemp.createTempSync();
final result = await IOOverrides.runZoned(
() async => command.run(),
getCurrentDirectory: () => tempDir,
);

expect(result, equals(ExitCode.success.code));
verify(
() => shorebirdProcess.run(
'flutter',
[
'build',
'appbundle',
'--release',
'--flavor=$flavor',
'--target=$target',
],
runInShell: true,
),
).called(1);

verify(
() => logger.info(
'''
📦 Generated an app bundle at:
${lightCyan.wrap("./build/app/outputs/bundle/${flavor}Release/app-$flavor-release.aab")}''',
),
).called(1);
});

test('local-engine and architectures', () async {
expect(command.architectures.length, greaterThan(1));

Expand Down