refactor(shorebird_cli): introduce ArtifactBuilder to move away from ShorebirdBuildMixin (#1998)
This commit is contained in:
@@ -3,6 +3,7 @@ import 'dart:io';
|
||||
import 'package:scoped/scoped.dart';
|
||||
import 'package:shorebird_cli/src/android_sdk.dart';
|
||||
import 'package:shorebird_cli/src/android_studio.dart';
|
||||
import 'package:shorebird_cli/src/artifact_builder.dart';
|
||||
import 'package:shorebird_cli/src/artifact_manager.dart';
|
||||
import 'package:shorebird_cli/src/auth/auth.dart';
|
||||
import 'package:shorebird_cli/src/cache.dart';
|
||||
@@ -35,6 +36,7 @@ Future<void> main(List<String> args) async {
|
||||
androidSdkRef,
|
||||
androidStudioRef,
|
||||
aotToolsRef,
|
||||
artifactBuilderRef,
|
||||
artifactManagerRef,
|
||||
authRef,
|
||||
bundletoolRef,
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:mason_logger/mason_logger.dart';
|
||||
import 'package:scoped/scoped.dart';
|
||||
import 'package:shorebird_cli/src/logger.dart';
|
||||
import 'package:shorebird_cli/src/os/operating_system_interface.dart';
|
||||
import 'package:shorebird_cli/src/platform/platform.dart';
|
||||
import 'package:shorebird_cli/src/shorebird_android_artifacts.dart';
|
||||
import 'package:shorebird_cli/src/shorebird_env.dart';
|
||||
import 'package:shorebird_cli/src/shorebird_process.dart';
|
||||
|
||||
/// Used to wrap code that invokes `flutter build` with Shorebird's fork of
|
||||
/// Flutter.
|
||||
typedef ShorebirdBuildCommand = Future<void> Function();
|
||||
|
||||
/// {@template artifact_build_exception}
|
||||
/// Thrown when a build fails.
|
||||
/// {@endtemplate}
|
||||
class ArtifactBuildException implements Exception {
|
||||
/// {@macro artifact_build_exception}
|
||||
ArtifactBuildException(this.message);
|
||||
|
||||
/// Information about the build failure.
|
||||
final String message;
|
||||
}
|
||||
|
||||
/// A reference to a [ArtifactBuilder] instance.
|
||||
final artifactBuilderRef = create(ArtifactBuilder.new);
|
||||
|
||||
/// The [ArtifactBuilder] instance available in the current zone.
|
||||
ArtifactBuilder get artifactBuilder => read(artifactBuilderRef);
|
||||
|
||||
/// @{template artifact_builder}
|
||||
/// Builds aabs, ipas, and other artifacts produced by `flutter build`.
|
||||
/// @{endtemplate}
|
||||
class ArtifactBuilder {
|
||||
/// Builds an aab using `flutter build appbundle`. Runs `flutter pub get` with
|
||||
/// the system installation of Flutter to reset
|
||||
/// `.dart_tool/package_config.json` after the build completes or fails.
|
||||
Future<File> buildAppBundle({
|
||||
String? flavor,
|
||||
String? target,
|
||||
Iterable<Arch>? targetPlatforms,
|
||||
List<String> argResultsRest = const [],
|
||||
}) async {
|
||||
await _runShorebirdBuildCommand(() async {
|
||||
const executable = 'flutter';
|
||||
final targetPlatformArgs = targetPlatforms?.targetPlatformArg;
|
||||
final arguments = [
|
||||
'build',
|
||||
'appbundle',
|
||||
'--release',
|
||||
if (flavor != null) '--flavor=$flavor',
|
||||
if (target != null) '--target=$target',
|
||||
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}',
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
final projectRoot = shorebirdEnv.getShorebirdProjectRoot()!;
|
||||
try {
|
||||
return shorebirdAndroidArtifacts.findAab(
|
||||
project: projectRoot,
|
||||
flavor: flavor,
|
||||
);
|
||||
} on MultipleArtifactsFoundException catch (error) {
|
||||
throw ArtifactBuildException(
|
||||
'Build succeeded, but it generated multiple AABs in the '
|
||||
'build directory. ${error.foundArtifacts.map((e) => e.path)}',
|
||||
);
|
||||
} on ArtifactNotFoundException catch (error) {
|
||||
throw ArtifactBuildException(
|
||||
'Build succeeded, but could not find the AAB in the build directory. '
|
||||
'Expected to find ${error.artifactName}',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// `.dart_tool/package_config.json` to the system Flutter.
|
||||
Future<void> _runShorebirdBuildCommand(ShorebirdBuildCommand command) async {
|
||||
try {
|
||||
await command();
|
||||
} finally {
|
||||
await _systemFlutterPubGet();
|
||||
}
|
||||
}
|
||||
|
||||
/// This is a hack to reset `.dart_tool/package_config.json` to point to the
|
||||
/// Flutter SDK on the user's PATH. This is necessary because Flutter commands
|
||||
/// run by shorebird update the package_config.json file to point to
|
||||
/// shorebird's version of Flutter, which confuses VS Code. See
|
||||
/// https://github.com/shorebirdtech/shorebird/issues/1101 for more info.
|
||||
Future<void> _systemFlutterPubGet() async {
|
||||
const executable = 'flutter';
|
||||
if (osInterface.which(executable) == null) {
|
||||
// If the user doesn't have Flutter on their PATH, then we can't run
|
||||
// `flutter pub get` with the system Flutter.
|
||||
return;
|
||||
}
|
||||
|
||||
final arguments = ['--no-version-check', 'pub', 'get', '--offline'];
|
||||
|
||||
final result = await process.run(
|
||||
executable,
|
||||
arguments,
|
||||
runInShell: true,
|
||||
useVendedFlutter: false,
|
||||
);
|
||||
|
||||
if (result.exitCode != ExitCode.success.code) {
|
||||
logger.warn(
|
||||
'''
|
||||
Build was successful, but `flutter pub get` failed to run after the build completed. You may see unexpected behavior in VS Code.
|
||||
|
||||
Either run `flutter pub get` manually, or follow the steps in ${link(uri: Uri.parse('https://docs.shorebird.dev/troubleshooting#i-installed-shorebird-and-now-i-cant-run-my-app-in-vs-code'))}.
|
||||
''',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
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/doctor.dart';
|
||||
import 'package:shorebird_cli/src/logger.dart';
|
||||
import 'package:shorebird_cli/src/shorebird_build_mixin.dart';
|
||||
import 'package:shorebird_cli/src/shorebird_validator.dart';
|
||||
|
||||
/// {@template build_app_bundle_command}
|
||||
@@ -11,7 +11,7 @@ import 'package:shorebird_cli/src/shorebird_validator.dart';
|
||||
/// `shorebird build appbundle`
|
||||
/// Build an Android App Bundle file from your app.
|
||||
/// {@endtemplate}
|
||||
class BuildAppBundleCommand extends ShorebirdCommand with ShorebirdBuildMixin {
|
||||
class BuildAppBundleCommand extends ShorebirdCommand {
|
||||
/// {@macro build_app_bundle_command}
|
||||
BuildAppBundleCommand() {
|
||||
argParser
|
||||
@@ -48,8 +48,8 @@ class BuildAppBundleCommand extends ShorebirdCommand with ShorebirdBuildMixin {
|
||||
final target = results['target'] as String?;
|
||||
final buildProgress = logger.progress('Building appbundle');
|
||||
try {
|
||||
await buildAppBundle(flavor: flavor, target: target);
|
||||
} on BuildException catch (error) {
|
||||
await artifactBuilder.buildAppBundle(flavor: flavor, target: target);
|
||||
} on ArtifactBuildException catch (error) {
|
||||
buildProgress.fail(error.message);
|
||||
return ExitCode.software.code;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,314 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:mason_logger/mason_logger.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
import 'package:scoped/scoped.dart';
|
||||
import 'package:shorebird_cli/src/artifact_builder.dart';
|
||||
import 'package:shorebird_cli/src/logger.dart';
|
||||
import 'package:shorebird_cli/src/os/operating_system_interface.dart';
|
||||
import 'package:shorebird_cli/src/platform/platform.dart';
|
||||
import 'package:shorebird_cli/src/shorebird_android_artifacts.dart';
|
||||
import 'package:shorebird_cli/src/shorebird_env.dart';
|
||||
import 'package:shorebird_cli/src/shorebird_process.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'fakes.dart';
|
||||
import 'mocks.dart';
|
||||
|
||||
void main() {
|
||||
group(ArtifactBuilder, () {
|
||||
late Logger logger;
|
||||
late OperatingSystemInterface operatingSystemInterface;
|
||||
late ShorebirdAndroidArtifacts shorebirdAndroidArtifacts;
|
||||
late ShorebirdEnv shorebirdEnv;
|
||||
late ShorebirdProcess shorebirdProcess;
|
||||
late ShorebirdProcessResult buildProcessResult;
|
||||
late ShorebirdProcessResult pubGetProcessResult;
|
||||
late ArtifactBuilder builder;
|
||||
|
||||
R runWithOverrides<R>(R Function() body) {
|
||||
return runScoped(
|
||||
body,
|
||||
values: {
|
||||
loggerRef.overrideWith(() => logger),
|
||||
osInterfaceRef.overrideWith(() => operatingSystemInterface),
|
||||
processRef.overrideWith(() => shorebirdProcess),
|
||||
shorebirdEnvRef.overrideWith(() => shorebirdEnv),
|
||||
shorebirdAndroidArtifactsRef
|
||||
.overrideWith(() => shorebirdAndroidArtifacts),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
setUpAll(() {
|
||||
registerFallbackValue(FakeShorebirdProcess());
|
||||
registerFallbackValue(Directory(''));
|
||||
});
|
||||
|
||||
setUp(() {
|
||||
buildProcessResult = MockProcessResult();
|
||||
logger = MockLogger();
|
||||
operatingSystemInterface = MockOperatingSystemInterface();
|
||||
pubGetProcessResult = MockProcessResult();
|
||||
shorebirdAndroidArtifacts = MockShorebirdAndroidArtifacts();
|
||||
shorebirdEnv = MockShorebirdEnv();
|
||||
shorebirdProcess = MockShorebirdProcess();
|
||||
|
||||
when(
|
||||
() => shorebirdProcess.run(
|
||||
'flutter',
|
||||
['--no-version-check', 'pub', 'get', '--offline'],
|
||||
runInShell: any(named: 'runInShell'),
|
||||
useVendedFlutter: false,
|
||||
),
|
||||
).thenAnswer((_) async => pubGetProcessResult);
|
||||
when(() => pubGetProcessResult.exitCode)
|
||||
.thenReturn(ExitCode.success.code);
|
||||
when(
|
||||
() => shorebirdProcess.run(
|
||||
any(),
|
||||
any(),
|
||||
runInShell: any(named: 'runInShell'),
|
||||
),
|
||||
).thenAnswer((_) async => buildProcessResult);
|
||||
when(() => buildProcessResult.exitCode).thenReturn(ExitCode.success.code);
|
||||
when(() => logger.progress(any())).thenReturn(MockProgress());
|
||||
when(() => logger.info(any())).thenReturn(null);
|
||||
when(() => operatingSystemInterface.which('flutter'))
|
||||
.thenReturn('/path/to/flutter');
|
||||
when(() => shorebirdEnv.flutterRevision).thenReturn('1234');
|
||||
when(shorebirdEnv.getShorebirdProjectRoot).thenReturn(Directory(''));
|
||||
when(
|
||||
() => shorebirdAndroidArtifacts.findAab(
|
||||
project: any(named: 'project'),
|
||||
flavor: any(named: 'flavor'),
|
||||
),
|
||||
).thenReturn(File('app-release.aab'));
|
||||
|
||||
builder = ArtifactBuilder();
|
||||
});
|
||||
|
||||
group('buildAppBundle', () {
|
||||
test('invokes the correct flutter build command', () async {
|
||||
await runWithOverrides(() => builder.buildAppBundle());
|
||||
|
||||
verify(
|
||||
() => shorebirdProcess.run(
|
||||
'flutter',
|
||||
['build', 'appbundle', '--release'],
|
||||
runInShell: any(named: 'runInShell'),
|
||||
environment: any(named: 'environment'),
|
||||
),
|
||||
).called(1);
|
||||
});
|
||||
|
||||
test('forward arguments to flutter build', () async {
|
||||
await runWithOverrides(
|
||||
() => builder.buildAppBundle(
|
||||
flavor: 'flavor',
|
||||
target: 'target',
|
||||
targetPlatforms: [Arch.arm64],
|
||||
argResultsRest: ['--foo', 'bar'],
|
||||
),
|
||||
);
|
||||
|
||||
verify(
|
||||
() => shorebirdProcess.run(
|
||||
'flutter',
|
||||
[
|
||||
'build',
|
||||
'appbundle',
|
||||
'--release',
|
||||
'--flavor=flavor',
|
||||
'--target=target',
|
||||
'--target-platform=android-arm64',
|
||||
'--foo',
|
||||
'bar',
|
||||
],
|
||||
runInShell: any(named: 'runInShell'),
|
||||
),
|
||||
).called(1);
|
||||
});
|
||||
|
||||
group('when multiple artifacts are found', () {
|
||||
setUp(() {
|
||||
when(
|
||||
() => shorebirdAndroidArtifacts.findAab(
|
||||
project: any(named: 'project'),
|
||||
flavor: any(named: 'flavor'),
|
||||
),
|
||||
).thenThrow(
|
||||
MultipleArtifactsFoundException(
|
||||
foundArtifacts: [File('a'), File('b')],
|
||||
buildDir: 'buildDir',
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
test('throws BuildException', () async {
|
||||
expect(
|
||||
() async => runWithOverrides(() => builder.buildAppBundle()),
|
||||
throwsA(
|
||||
isA<ArtifactBuildException>().having(
|
||||
(e) => e.message,
|
||||
'message',
|
||||
'''Build succeeded, but it generated multiple AABs in the build directory. (a, b)''',
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
group('when no artifacts are found', () {
|
||||
setUp(() {
|
||||
when(
|
||||
() => shorebirdAndroidArtifacts.findAab(
|
||||
project: any(named: 'project'),
|
||||
flavor: any(named: 'flavor'),
|
||||
),
|
||||
).thenThrow(
|
||||
ArtifactNotFoundException(
|
||||
artifactName: 'app-release.aab',
|
||||
buildDir: 'buildDir',
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
test('throws BuildException', () async {
|
||||
expect(
|
||||
() async => runWithOverrides(() => builder.buildAppBundle()),
|
||||
throwsA(
|
||||
isA<ArtifactBuildException>().having(
|
||||
(e) => e.message,
|
||||
'message',
|
||||
'''Build succeeded, but could not find the AAB in the build directory. Expected to find app-release.aab''',
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
group('after a build', () {
|
||||
group('when the build is successful', () {
|
||||
setUp(() {
|
||||
when(() => buildProcessResult.exitCode)
|
||||
.thenReturn(ExitCode.success.code);
|
||||
});
|
||||
|
||||
group('when flutter is installed', () {
|
||||
setUp(() {
|
||||
when(() => operatingSystemInterface.which('flutter'))
|
||||
.thenReturn('/path/to/flutter');
|
||||
});
|
||||
|
||||
test('runs flutter pub get with system flutter', () async {
|
||||
await runWithOverrides(() => builder.buildAppBundle());
|
||||
|
||||
verify(
|
||||
() => shorebirdProcess.run(
|
||||
'flutter',
|
||||
['--no-version-check', 'pub', 'get', '--offline'],
|
||||
runInShell: any(named: 'runInShell'),
|
||||
useVendedFlutter: false,
|
||||
),
|
||||
).called(1);
|
||||
});
|
||||
});
|
||||
|
||||
group('when flutter is not installed', () {
|
||||
setUp(() {
|
||||
when(() => operatingSystemInterface.which('flutter'))
|
||||
.thenReturn(null);
|
||||
});
|
||||
|
||||
test('does not attempt to run flutter pub get', () async {
|
||||
await runWithOverrides(() => builder.buildAppBundle());
|
||||
|
||||
verifyNever(
|
||||
() => shorebirdProcess.run(
|
||||
'flutter',
|
||||
['--no-version-check', 'pub', 'get', '--offline'],
|
||||
runInShell: any(named: 'runInShell'),
|
||||
useVendedFlutter: false,
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
group('when the build fails', () {
|
||||
setUp(() {
|
||||
when(() => buildProcessResult.exitCode)
|
||||
.thenReturn(ExitCode.software.code);
|
||||
});
|
||||
|
||||
group('when flutter is installed', () {
|
||||
setUp(() {
|
||||
when(() => operatingSystemInterface.which('flutter'))
|
||||
.thenReturn('/path/to/flutter');
|
||||
});
|
||||
|
||||
test('runs flutter pub get with system flutter', () async {
|
||||
await expectLater(
|
||||
() async => runWithOverrides(() => builder.buildAppBundle()),
|
||||
throwsA(isA<ArtifactBuildException>()),
|
||||
);
|
||||
|
||||
verify(
|
||||
() => shorebirdProcess.run(
|
||||
'flutter',
|
||||
['--no-version-check', 'pub', 'get', '--offline'],
|
||||
runInShell: any(named: 'runInShell'),
|
||||
useVendedFlutter: false,
|
||||
),
|
||||
).called(1);
|
||||
});
|
||||
|
||||
test('prints error message if system flutter pub get fails',
|
||||
() async {
|
||||
when(() => pubGetProcessResult.exitCode).thenReturn(1);
|
||||
|
||||
await expectLater(
|
||||
() async => runWithOverrides(() => builder.buildAppBundle()),
|
||||
throwsA(isA<ArtifactBuildException>()),
|
||||
);
|
||||
|
||||
verify(
|
||||
() => logger.warn(
|
||||
'''
|
||||
Build was successful, but `flutter pub get` failed to run after the build completed. You may see unexpected behavior in VS Code.
|
||||
|
||||
Either run `flutter pub get` manually, or follow the steps in ${link(uri: Uri.parse('https://docs.shorebird.dev/troubleshooting#i-installed-shorebird-and-now-i-cant-run-my-app-in-vs-code'))}.
|
||||
''',
|
||||
),
|
||||
).called(1);
|
||||
});
|
||||
});
|
||||
|
||||
group('when flutter is not installed', () {
|
||||
setUp(() {
|
||||
when(() => operatingSystemInterface.which('flutter'))
|
||||
.thenReturn(null);
|
||||
});
|
||||
|
||||
test('does not attempt to run flutter pub get', () async {
|
||||
await expectLater(
|
||||
() async => runWithOverrides(() => builder.buildAppBundle()),
|
||||
throwsA(isA<ArtifactBuildException>()),
|
||||
);
|
||||
|
||||
verifyNever(
|
||||
() => shorebirdProcess.run(
|
||||
'flutter',
|
||||
['--no-version-check', 'pub', 'get', '--offline'],
|
||||
runInShell: any(named: 'runInShell'),
|
||||
useVendedFlutter: false,
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -5,15 +5,12 @@ 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/doctor.dart';
|
||||
import 'package:shorebird_cli/src/engine_config.dart';
|
||||
import 'package:shorebird_cli/src/logger.dart';
|
||||
import 'package:shorebird_cli/src/os/operating_system_interface.dart';
|
||||
import 'package:shorebird_cli/src/platform/platform.dart';
|
||||
import 'package:shorebird_cli/src/shorebird_android_artifacts.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:shorebird_cli/src/validators/validators.dart';
|
||||
import 'package:test/test.dart';
|
||||
@@ -24,31 +21,22 @@ import '../../mocks.dart';
|
||||
void main() {
|
||||
group(BuildAppBundleCommand, () {
|
||||
late ArgResults argResults;
|
||||
late ArtifactBuilder artifactBuilder;
|
||||
late Doctor doctor;
|
||||
late Logger logger;
|
||||
late OperatingSystemInterface operatingSystemInterface;
|
||||
late ShorebirdProcessResult flutterPubGetProcessResult;
|
||||
late ShorebirdProcessResult buildProcessResult;
|
||||
late BuildAppBundleCommand command;
|
||||
late ShorebirdEnv shorebirdEnv;
|
||||
late ShorebirdFlutterValidator flutterValidator;
|
||||
late ShorebirdProcess shorebirdProcess;
|
||||
late ShorebirdValidator shorebirdValidator;
|
||||
late ShorebirdAndroidArtifacts shorebirdAndroidArtifacts;
|
||||
|
||||
R runWithOverrides<R>(R Function() body) {
|
||||
return runScoped(
|
||||
body,
|
||||
values: {
|
||||
artifactBuilderRef.overrideWith(() => artifactBuilder),
|
||||
doctorRef.overrideWith(() => doctor),
|
||||
engineConfigRef.overrideWith(() => const EngineConfig.empty()),
|
||||
loggerRef.overrideWith(() => logger),
|
||||
osInterfaceRef.overrideWith(() => operatingSystemInterface),
|
||||
processRef.overrideWith(() => shorebirdProcess),
|
||||
shorebirdEnvRef.overrideWith(() => shorebirdEnv),
|
||||
shorebirdValidatorRef.overrideWith(() => shorebirdValidator),
|
||||
shorebirdAndroidArtifactsRef
|
||||
.overrideWith(() => shorebirdAndroidArtifacts),
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -60,44 +48,18 @@ void main() {
|
||||
|
||||
setUp(() {
|
||||
argResults = MockArgResults();
|
||||
artifactBuilder = MockArtifactBuilder();
|
||||
doctor = MockDoctor();
|
||||
logger = MockLogger();
|
||||
operatingSystemInterface = MockOperatingSystemInterface();
|
||||
buildProcessResult = MockProcessResult();
|
||||
flutterPubGetProcessResult = MockProcessResult();
|
||||
flutterValidator = MockShorebirdFlutterValidator();
|
||||
shorebirdEnv = MockShorebirdEnv();
|
||||
shorebirdProcess = MockShorebirdProcess();
|
||||
shorebirdValidator = MockShorebirdValidator();
|
||||
shorebirdAndroidArtifacts = MockShorebirdAndroidArtifacts();
|
||||
|
||||
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);
|
||||
when(
|
||||
() => shorebirdProcess.run(
|
||||
any(),
|
||||
any(),
|
||||
runInShell: any(named: 'runInShell'),
|
||||
),
|
||||
).thenAnswer((_) async => buildProcessResult);
|
||||
when(() => argResults.rest).thenReturn([]);
|
||||
when(() => logger.progress(any())).thenReturn(MockProgress());
|
||||
when(() => logger.info(any())).thenReturn(null);
|
||||
when(() => operatingSystemInterface.which('flutter'))
|
||||
.thenReturn('/path/to/flutter');
|
||||
when(
|
||||
() => doctor.androidCommandValidators,
|
||||
).thenReturn([flutterValidator]);
|
||||
when(() => shorebirdEnv.flutterRevision).thenReturn('1234');
|
||||
when(shorebirdEnv.getShorebirdProjectRoot).thenReturn(Directory(''));
|
||||
when(
|
||||
() => shorebirdValidator.validatePreconditions(
|
||||
checkUserIsAuthenticated: any(named: 'checkUserIsAuthenticated'),
|
||||
@@ -106,11 +68,13 @@ void main() {
|
||||
),
|
||||
).thenAnswer((_) async {});
|
||||
when(
|
||||
() => shorebirdAndroidArtifacts.findAab(
|
||||
project: any(named: 'project'),
|
||||
() => artifactBuilder.buildAppBundle(
|
||||
flavor: any(named: 'flavor'),
|
||||
target: any(named: 'target'),
|
||||
),
|
||||
).thenReturn(File('app-release.aab'));
|
||||
).thenAnswer(
|
||||
(_) async => File(''),
|
||||
);
|
||||
|
||||
command = runWithOverrides(BuildAppBundleCommand.new)
|
||||
..testArgResults = argResults;
|
||||
@@ -143,33 +107,21 @@ void main() {
|
||||
});
|
||||
|
||||
test('exits with code 70 when building appbundle fails', () async {
|
||||
when(() => buildProcessResult.exitCode).thenReturn(1);
|
||||
when(() => buildProcessResult.stderr).thenReturn('oops');
|
||||
when(() => artifactBuilder.buildAppBundle()).thenThrow(
|
||||
ArtifactBuildException('Failed to build: oops'),
|
||||
);
|
||||
|
||||
final exitCode = await runWithOverrides(command.run);
|
||||
|
||||
expect(exitCode, equals(ExitCode.software.code));
|
||||
verify(
|
||||
() => shorebirdProcess.run(
|
||||
'flutter',
|
||||
['build', 'appbundle', '--release'],
|
||||
runInShell: any(named: 'runInShell'),
|
||||
),
|
||||
).called(1);
|
||||
verify(() => artifactBuilder.buildAppBundle()).called(1);
|
||||
});
|
||||
|
||||
test('exits with code 0 when building appbundle succeeds', () async {
|
||||
when(() => buildProcessResult.exitCode).thenReturn(ExitCode.success.code);
|
||||
final exitCode = await runWithOverrides(command.run);
|
||||
|
||||
expect(exitCode, equals(ExitCode.success.code));
|
||||
verify(
|
||||
() => shorebirdProcess.run(
|
||||
'flutter',
|
||||
['build', 'appbundle', '--release'],
|
||||
runInShell: true,
|
||||
),
|
||||
).called(1);
|
||||
verify(() => artifactBuilder.buildAppBundle()).called(1);
|
||||
|
||||
verify(
|
||||
() => logger.info(
|
||||
@@ -187,21 +139,13 @@ ${lightCyan.wrap(p.join('build', 'app', 'outputs', 'bundle', 'release', 'app-rel
|
||||
final target = p.join('lib', 'main_development.dart');
|
||||
when(() => argResults['flavor']).thenReturn(flavor);
|
||||
when(() => argResults['target']).thenReturn(target);
|
||||
when(() => buildProcessResult.exitCode).thenReturn(ExitCode.success.code);
|
||||
final exitCode = await runWithOverrides(command.run);
|
||||
|
||||
expect(exitCode, equals(ExitCode.success.code));
|
||||
verify(
|
||||
() => shorebirdProcess.run(
|
||||
'flutter',
|
||||
[
|
||||
'build',
|
||||
'appbundle',
|
||||
'--release',
|
||||
'--flavor=$flavor',
|
||||
'--target=$target',
|
||||
],
|
||||
runInShell: true,
|
||||
() => artifactBuilder.buildAppBundle(
|
||||
flavor: flavor,
|
||||
target: target,
|
||||
),
|
||||
).called(1);
|
||||
|
||||
@@ -253,21 +197,5 @@ ${lightCyan.wrap(p.join('build', 'app', 'outputs', 'bundle', '${flavor}Release',
|
||||
throwsException,
|
||||
);
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import 'package:shorebird_cli/src/android_sdk.dart';
|
||||
import 'package:shorebird_cli/src/android_studio.dart';
|
||||
import 'package:shorebird_cli/src/archive_analysis/archive_analysis.dart';
|
||||
import 'package:shorebird_cli/src/archive_analysis/archive_differ.dart';
|
||||
import 'package:shorebird_cli/src/artifact_builder.dart';
|
||||
import 'package:shorebird_cli/src/artifact_manager.dart';
|
||||
import 'package:shorebird_cli/src/auth/auth.dart';
|
||||
import 'package:shorebird_cli/src/cache.dart' show Cache;
|
||||
@@ -54,6 +55,8 @@ class MockArchiveDiffer extends Mock implements ArchiveDiffer {}
|
||||
|
||||
class MockArgResults extends Mock implements ArgResults {}
|
||||
|
||||
class MockArtifactBuilder extends Mock implements ArtifactBuilder {}
|
||||
|
||||
class MockArtifactManager extends Mock implements ArtifactManager {}
|
||||
|
||||
class MockAuth extends Mock implements Auth {}
|
||||
|
||||
Reference in New Issue
Block a user