refactor(shorebird_cli): add buildAar to ArtifactBuilder (#2001)

This commit is contained in:
Bryan Oltman
2024-05-01 18:03:54 -04:00
committed by GitHub
parent b34763ab70
commit e9fa7b55db
4 changed files with 132 additions and 87 deletions
@@ -144,6 +144,36 @@ class ArtifactBuilder {
}
}
Future<void> buildAar({
required String buildNumber,
Iterable<Arch>? targetPlatforms,
List<String> argResultsRest = const [],
}) async {
return _runShorebirdBuildCommand(() async {
const executable = 'flutter';
final targetPlatformArgs = targetPlatforms?.targetPlatformArg;
final arguments = [
'build',
'aar',
'--no-debug',
'--no-profile',
'--build-number=$buildNumber',
if (targetPlatformArgs != null) '--target-platform=$targetPlatformArgs',
...argResultsRest,
];
final result = await process.run(
executable,
arguments,
runInShell: true,
);
if (result.exitCode != ExitCode.success.code) {
throw ArtifactBuildException('Failed to build: ${result.stderr}');
}
});
}
/// A wrapper around [command] (which runs a `flutter build` command with
/// Shorebird's fork of Flutter) with a try/finally that runs
/// `flutter pub get` with the system installation of Flutter to reset
@@ -1,11 +1,10 @@
import 'dart:async';
import 'dart:io';
import 'package:mason_logger/mason_logger.dart';
import 'package:path/path.dart' as p;
import 'package:shorebird_cli/src/artifact_builder.dart';
import 'package:shorebird_cli/src/command.dart';
import 'package:shorebird_cli/src/logger.dart';
import 'package:shorebird_cli/src/shorebird_build_mixin.dart';
import 'package:shorebird_cli/src/shorebird_env.dart';
import 'package:shorebird_cli/src/shorebird_validator.dart';
@@ -14,7 +13,7 @@ import 'package:shorebird_cli/src/shorebird_validator.dart';
/// `shorebird build aar`
/// Build an Android aar file from your app.
/// {@endtemplate}
class BuildAarCommand extends ShorebirdCommand with ShorebirdBuildMixin {
class BuildAarCommand extends ShorebirdCommand {
BuildAarCommand() {
// We would have a "target" option here, similar to what [BuildApkCommand]
// and [BuildAabCommand] have, but target cannot currently be configured in
@@ -54,8 +53,8 @@ class BuildAarCommand extends ShorebirdCommand with ShorebirdBuildMixin {
final buildNumber = results['build-number'] as String;
final buildProgress = logger.progress('Building aar');
try {
await buildAar(buildNumber: buildNumber);
} on ProcessException catch (error) {
await artifactBuilder.buildAar(buildNumber: buildNumber);
} on ArtifactBuildException catch (error) {
buildProgress.fail('Failed to build: ${error.message}');
return ExitCode.software.code;
}
@@ -415,5 +415,88 @@ Either run `flutter pub get` manually, or follow the steps in ${link(uri: Uri.pa
});
});
});
group('buildAar', () {
const buildNumber = '1.0';
test('invokes the correct flutter build command', () async {
await runWithOverrides(
() => builder.buildAar(buildNumber: buildNumber),
);
verify(
() => shorebirdProcess.run(
'flutter',
[
'build',
'aar',
'--no-debug',
'--no-profile',
'--build-number=1.0',
],
runInShell: any(named: 'runInShell'),
environment: any(named: 'environment'),
),
).called(1);
});
test('forward arguments to flutter build', () async {
await runWithOverrides(
() => builder.buildAar(
buildNumber: buildNumber,
targetPlatforms: [Arch.arm64],
argResultsRest: ['--foo', 'bar'],
),
);
verify(
() => shorebirdProcess.run(
'flutter',
[
'build',
'aar',
'--no-debug',
'--no-profile',
'--build-number=1.0',
'--target-platform=android-arm64',
'--foo',
'bar',
],
runInShell: any(named: 'runInShell'),
),
).called(1);
});
group('after a build', () {
group('when the build is successful', () {
setUp(() {
when(() => buildProcessResult.exitCode)
.thenReturn(ExitCode.success.code);
});
verifyCorrectFlutterPubGet(
() async => runWithOverrides(
() => builder.buildAar(buildNumber: buildNumber),
),
);
group('when the build fails', () {
setUp(() {
when(() => buildProcessResult.exitCode)
.thenReturn(ExitCode.software.code);
});
verifyCorrectFlutterPubGet(
() async => expectLater(
() async => runWithOverrides(
() => builder.buildAar(buildNumber: buildNumber),
),
throwsA(isA<ArtifactBuildException>()),
),
);
});
});
});
});
});
}
@@ -3,11 +3,10 @@ import 'package:mason_logger/mason_logger.dart';
import 'package:mocktail/mocktail.dart';
import 'package:path/path.dart' as p;
import 'package:scoped/scoped.dart';
import 'package:shorebird_cli/src/artifact_builder.dart';
import 'package:shorebird_cli/src/commands/build/build.dart';
import 'package:shorebird_cli/src/logger.dart';
import 'package:shorebird_cli/src/os/operating_system_interface.dart';
import 'package:shorebird_cli/src/shorebird_env.dart';
import 'package:shorebird_cli/src/shorebird_process.dart';
import 'package:shorebird_cli/src/shorebird_validator.dart';
import 'package:test/test.dart';
@@ -19,13 +18,10 @@ void main() {
const androidPackageName = 'com.example.my_flutter_module';
late ArgResults argResults;
late ArtifactBuilder artifactBuilder;
late Logger logger;
late OperatingSystemInterface operatingSystemInterface;
late Progress progress;
late ShorebirdEnv shorebirdEnv;
late ShorebirdProcess shorebirdProcess;
late ShorebirdProcessResult buildProcessResult;
late ShorebirdProcessResult flutterPubGetProcessResult;
late ShorebirdValidator shorebirdValidator;
late BuildAarCommand command;
@@ -33,9 +29,8 @@ void main() {
return runScoped(
body,
values: {
artifactBuilderRef.overrideWith(() => artifactBuilder),
loggerRef.overrideWith(() => logger),
osInterfaceRef.overrideWith(() => operatingSystemInterface),
processRef.overrideWith(() => shorebirdProcess),
shorebirdEnvRef.overrideWith(() => shorebirdEnv),
shorebirdValidatorRef.overrideWith(() => shorebirdValidator),
},
@@ -44,41 +39,21 @@ void main() {
setUp(() {
argResults = MockArgResults();
artifactBuilder = MockArtifactBuilder();
logger = MockLogger();
flutterPubGetProcessResult = MockProcessResult();
buildProcessResult = MockProcessResult();
operatingSystemInterface = MockOperatingSystemInterface();
progress = MockProgress();
shorebirdEnv = MockShorebirdEnv();
shorebirdProcess = MockShorebirdProcess();
shorebirdValidator = MockShorebirdValidator();
when(() => argResults['build-number']).thenReturn(buildNumber);
when(() => argResults.rest).thenReturn([]);
when(() => logger.progress(any())).thenReturn(progress);
when(
() => shorebirdProcess.run(
'flutter',
['--no-version-check', 'pub', 'get', '--offline'],
runInShell: any(named: 'runInShell'),
useVendedFlutter: false,
),
).thenAnswer((_) async => flutterPubGetProcessResult);
when(() => flutterPubGetProcessResult.exitCode)
.thenReturn(ExitCode.success.code);
() => artifactBuilder.buildAar(buildNumber: any(named: 'buildNumber')),
).thenAnswer((_) async => {});
when(
() => shorebirdProcess.run(
any(),
any(),
runInShell: any(named: 'runInShell'),
),
).thenAnswer((invocation) async {
return buildProcessResult;
});
when(() => operatingSystemInterface.which('flutter'))
.thenReturn('/path/to/flutter');
() => shorebirdEnv.androidPackageName,
).thenReturn(androidPackageName);
when(
() => shorebirdValidator.validatePreconditions(
checkUserIsAuthenticated: any(named: 'checkUserIsAuthenticated'),
@@ -86,11 +61,6 @@ void main() {
),
).thenAnswer((_) async {});
when(
() => shorebirdEnv.androidPackageName,
).thenReturn(androidPackageName);
when(() => shorebirdEnv.flutterRevision).thenReturn('1234');
command = runWithOverrides(BuildAarCommand.new)
..testArgResults = argResults;
});
@@ -126,49 +96,27 @@ void main() {
});
test('exits with code 70 when building aar fails', () async {
when(() => buildProcessResult.exitCode).thenReturn(1);
when(() => buildProcessResult.stderr).thenReturn('oops');
when(
() => artifactBuilder.buildAar(buildNumber: any(named: 'buildNumber')),
).thenThrow(ArtifactBuildException('Failed to build: error'));
final result = await runWithOverrides(command.run);
expect(result, equals(ExitCode.software.code));
verify(
() => shorebirdProcess.run(
'flutter',
[
'build',
'aar',
'--no-debug',
'--no-profile',
'--build-number=$buildNumber',
],
runInShell: any(named: 'runInShell'),
),
).called(1);
verify(() => artifactBuilder.buildAar(buildNumber: buildNumber))
.called(1);
verify(
() => progress.fail(any(that: contains('Failed to build'))),
).called(1);
});
test('exits with code 0 when building aar succeeds', () async {
when(() => buildProcessResult.exitCode).thenReturn(ExitCode.success.code);
final result = await runWithOverrides(command.run);
expect(result, equals(ExitCode.success.code));
verify(
() => shorebirdProcess.run(
'flutter',
[
'build',
'aar',
'--no-debug',
'--no-profile',
'--build-number=$buildNumber',
],
runInShell: any(named: 'runInShell'),
),
).called(1);
verify(() => artifactBuilder.buildAar(buildNumber: buildNumber))
.called(1);
verify(
() => logger.info(
'''
@@ -190,20 +138,5 @@ ${lightCyan.wrap(
),
).called(1);
});
test('runs flutter pub get with system flutter after successful build',
() async {
when(() => buildProcessResult.exitCode).thenReturn(ExitCode.success.code);
await runWithOverrides(command.run);
verify(
() => shorebirdProcess.run(
'flutter',
['--no-version-check', 'pub', 'get', '--offline'],
runInShell: any(named: 'runInShell'),
useVendedFlutter: false,
),
).called(1);
});
});
}