feat: add gradle steps to appbundle build progress (#2579)
This commit is contained in:
@@ -99,6 +99,7 @@ words:
|
|||||||
- udid # Unique Device Identifier
|
- udid # Unique Device Identifier
|
||||||
- unawaited
|
- unawaited
|
||||||
- unmockable
|
- unmockable
|
||||||
|
- unsets
|
||||||
- upvote
|
- upvote
|
||||||
- usbmuxd
|
- usbmuxd
|
||||||
- vmcode
|
- vmcode
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
// cspell:words endtemplate aabs ipas appbundle bryanoltman codesign xcarchive
|
// cspell:words endtemplate aabs ipas appbundle bryanoltman codesign xcarchive
|
||||||
// cspell:words xcframework
|
// cspell:words xcframework
|
||||||
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:mason_logger/mason_logger.dart';
|
import 'package:mason_logger/mason_logger.dart';
|
||||||
@@ -86,6 +87,7 @@ class ArtifactBuilder {
|
|||||||
Iterable<Arch>? targetPlatforms,
|
Iterable<Arch>? targetPlatforms,
|
||||||
List<String> args = const [],
|
List<String> args = const [],
|
||||||
String? base64PublicKey,
|
String? base64PublicKey,
|
||||||
|
DetailProgress? buildProgress,
|
||||||
}) async {
|
}) async {
|
||||||
await _runShorebirdBuildCommand(() async {
|
await _runShorebirdBuildCommand(() async {
|
||||||
const executable = 'flutter';
|
const executable = 'flutter';
|
||||||
@@ -100,20 +102,46 @@ class ArtifactBuilder {
|
|||||||
...args,
|
...args,
|
||||||
];
|
];
|
||||||
|
|
||||||
final result = await process.run(
|
final buildProcess = await process.start(
|
||||||
executable,
|
executable,
|
||||||
arguments,
|
arguments,
|
||||||
runInShell: true,
|
runInShell: true,
|
||||||
environment: base64PublicKey?.toPublicKeyEnv(),
|
environment: base64PublicKey?.toPublicKeyEnv(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (result.exitCode != ExitCode.success.code) {
|
// Android builds are a series of gradle tasks that are all logged in
|
||||||
throw ArtifactBuildException(
|
// this format. We can use the 'Task :' line to get the current task
|
||||||
'Failed to build: ${result.stderr}',
|
// being run.
|
||||||
);
|
final gradleTaskRegex = RegExp(r'^\[.*\] \> (Task :.*)$');
|
||||||
|
buildProcess.stdout
|
||||||
|
.transform(utf8.decoder)
|
||||||
|
.transform(const LineSplitter())
|
||||||
|
.listen((line) {
|
||||||
|
if (buildProgress == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final captured = gradleTaskRegex.firstMatch(line)?.group(1);
|
||||||
|
if (captured != null) {
|
||||||
|
buildProgress.updateDetailMessage(captured);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
final stderrLines = await buildProcess.stderr
|
||||||
|
.transform(utf8.decoder)
|
||||||
|
.transform(const LineSplitter())
|
||||||
|
.toList();
|
||||||
|
final stdErr = stderrLines.join('\n');
|
||||||
|
final exitCode = await buildProcess.exitCode;
|
||||||
|
if (exitCode != ExitCode.success.code) {
|
||||||
|
throw ArtifactBuildException('Failed to build: $stdErr');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// If we've been updating the progress with gradle tasks, reset it to the
|
||||||
|
// original base message so as not to leave the user with a confusing
|
||||||
|
// message.
|
||||||
|
buildProgress?.updateDetailMessage(null);
|
||||||
|
|
||||||
final projectRoot = shorebirdEnv.getShorebirdProjectRoot()!;
|
final projectRoot = shorebirdEnv.getShorebirdProjectRoot()!;
|
||||||
try {
|
try {
|
||||||
return shorebirdAndroidArtifacts.findAab(
|
return shorebirdAndroidArtifacts.findAab(
|
||||||
|
|||||||
@@ -71,8 +71,8 @@ class AndroidPatcher extends Patcher {
|
|||||||
Future<File> buildPatchArtifact({String? releaseVersion}) async {
|
Future<File> buildPatchArtifact({String? releaseVersion}) async {
|
||||||
final File aabFile;
|
final File aabFile;
|
||||||
final flutterVersionString = await shorebirdFlutter.getVersionAndRevision();
|
final flutterVersionString = await shorebirdFlutter.getVersionAndRevision();
|
||||||
final buildProgress =
|
final buildProgress = logger
|
||||||
logger.progress('Building patch with Flutter $flutterVersionString');
|
.detailProgress('Building patch with Flutter $flutterVersionString');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
aabFile = await artifactBuilder.buildAppBundle(
|
aabFile = await artifactBuilder.buildAppBundle(
|
||||||
@@ -81,6 +81,7 @@ class AndroidPatcher extends Patcher {
|
|||||||
args: argResults.forwardedArgs +
|
args: argResults.forwardedArgs +
|
||||||
buildNameAndNumberArgsFromReleaseVersion(releaseVersion),
|
buildNameAndNumberArgsFromReleaseVersion(releaseVersion),
|
||||||
base64PublicKey: argResults.encodedPublicKey,
|
base64PublicKey: argResults.encodedPublicKey,
|
||||||
|
buildProgress: buildProgress,
|
||||||
);
|
);
|
||||||
buildProgress.complete();
|
buildProgress.complete();
|
||||||
} on ArtifactBuildException catch (error) {
|
} on ArtifactBuildException catch (error) {
|
||||||
|
|||||||
@@ -100,8 +100,9 @@ Please comment and upvote ${link(uri: Uri.parse('https://github.com/shorebirdtec
|
|||||||
|
|
||||||
final flutterVersionString = await shorebirdFlutter.getVersionAndRevision();
|
final flutterVersionString = await shorebirdFlutter.getVersionAndRevision();
|
||||||
|
|
||||||
final buildAppBundleProgress = logger
|
final buildAppBundleProgress = logger.detailProgress(
|
||||||
.progress('Building app bundle with Flutter $flutterVersionString');
|
'Building app bundle with Flutter $flutterVersionString',
|
||||||
|
);
|
||||||
|
|
||||||
final File aab;
|
final File aab;
|
||||||
|
|
||||||
@@ -114,6 +115,7 @@ Please comment and upvote ${link(uri: Uri.parse('https://github.com/shorebirdtec
|
|||||||
targetPlatforms: architectures,
|
targetPlatforms: architectures,
|
||||||
args: argResults.forwardedArgs,
|
args: argResults.forwardedArgs,
|
||||||
base64PublicKey: base64PublicKey,
|
base64PublicKey: base64PublicKey,
|
||||||
|
buildProgress: buildAppBundleProgress,
|
||||||
);
|
);
|
||||||
} on ArtifactBuildException catch (e) {
|
} on ArtifactBuildException catch (e) {
|
||||||
buildAppBundleProgress.fail(e.message);
|
buildAppBundleProgress.fail(e.message);
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
import 'package:mason_logger/mason_logger.dart';
|
||||||
|
|
||||||
|
/// {@template detail_progress}
|
||||||
|
/// A [Progress] wrapper that allows for maintaining a base message (e.g., a
|
||||||
|
/// task title) while updating the message with more specific information,
|
||||||
|
/// rendered in dark gray.
|
||||||
|
/// {@endtemplate}
|
||||||
|
class DetailProgress implements Progress {
|
||||||
|
/// {@macro detail_progress}
|
||||||
|
DetailProgress._({
|
||||||
|
required Progress progress,
|
||||||
|
required String primaryMessage,
|
||||||
|
}) : _progress = progress,
|
||||||
|
_primaryMessage = primaryMessage;
|
||||||
|
|
||||||
|
String _primaryMessage;
|
||||||
|
String? _detailMessage;
|
||||||
|
final Progress _progress;
|
||||||
|
|
||||||
|
/// Updates the main message of the progress with the given [message]. This is
|
||||||
|
/// roughly equivalent to calling [Progress.update], except that this will
|
||||||
|
/// preserve the detail message if one exists.
|
||||||
|
void updatePrimaryMessage(String message) {
|
||||||
|
_primaryMessage = message;
|
||||||
|
_updateImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Updates the detail message of the progress with the given [message].
|
||||||
|
void updateDetailMessage(String? message) {
|
||||||
|
_detailMessage = message;
|
||||||
|
_updateImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void update(String update) {
|
||||||
|
_primaryMessage = update;
|
||||||
|
_detailMessage = null;
|
||||||
|
_updateImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _updateImpl() {
|
||||||
|
final detailMessage = _detailMessage;
|
||||||
|
if (detailMessage != null) {
|
||||||
|
_progress.update('$_primaryMessage ${darkGray.wrap(detailMessage)}');
|
||||||
|
} else {
|
||||||
|
_progress.update(_primaryMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void cancel() {
|
||||||
|
_progress.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void complete([String? update]) {
|
||||||
|
_progress.complete(update ?? _primaryMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void fail([String? update]) {
|
||||||
|
_progress.fail(update ?? _primaryMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// {@template detail_progress_logger}
|
||||||
|
/// Adds a method to [Logger] to create an [DetailProgress] instance.
|
||||||
|
/// {@endtemplate}
|
||||||
|
extension DetailProgressLogger on Logger {
|
||||||
|
/// {@macro detail_progress_logger}
|
||||||
|
DetailProgress detailProgress(String primaryMessage) {
|
||||||
|
return DetailProgress._(
|
||||||
|
progress: progress(primaryMessage),
|
||||||
|
primaryMessage: primaryMessage,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,2 +1,3 @@
|
|||||||
|
export 'detail_progress.dart';
|
||||||
export 'logging_stdout.dart';
|
export 'logging_stdout.dart';
|
||||||
export 'shorebird_logger.dart';
|
export 'shorebird_logger.dart';
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:mason_logger/mason_logger.dart';
|
import 'package:mason_logger/mason_logger.dart';
|
||||||
@@ -39,6 +40,7 @@ void main() {
|
|||||||
late ShorebirdProcessResult buildProcessResult;
|
late ShorebirdProcessResult buildProcessResult;
|
||||||
late ShorebirdProcessResult pubGetProcessResult;
|
late ShorebirdProcessResult pubGetProcessResult;
|
||||||
late ArtifactBuilder builder;
|
late ArtifactBuilder builder;
|
||||||
|
late Process buildProcess;
|
||||||
|
|
||||||
R runWithOverrides<R>(R Function() body) {
|
R runWithOverrides<R>(R Function() body) {
|
||||||
return runScoped(
|
return runScoped(
|
||||||
@@ -71,6 +73,7 @@ void main() {
|
|||||||
shorebirdArtifacts = MockShorebirdArtifacts();
|
shorebirdArtifacts = MockShorebirdArtifacts();
|
||||||
shorebirdEnv = MockShorebirdEnv();
|
shorebirdEnv = MockShorebirdEnv();
|
||||||
shorebirdProcess = MockShorebirdProcess();
|
shorebirdProcess = MockShorebirdProcess();
|
||||||
|
buildProcess = MockProcess();
|
||||||
|
|
||||||
when(
|
when(
|
||||||
() => shorebirdProcess.run(
|
() => shorebirdProcess.run(
|
||||||
@@ -89,6 +92,13 @@ void main() {
|
|||||||
runInShell: any(named: 'runInShell'),
|
runInShell: any(named: 'runInShell'),
|
||||||
),
|
),
|
||||||
).thenAnswer((_) async => buildProcessResult);
|
).thenAnswer((_) async => buildProcessResult);
|
||||||
|
when(
|
||||||
|
() => shorebirdProcess.start(
|
||||||
|
any(),
|
||||||
|
any(),
|
||||||
|
runInShell: any(named: 'runInShell'),
|
||||||
|
),
|
||||||
|
).thenAnswer((_) async => buildProcess);
|
||||||
when(() => buildProcessResult.exitCode).thenReturn(ExitCode.success.code);
|
when(() => buildProcessResult.exitCode).thenReturn(ExitCode.success.code);
|
||||||
when(() => buildProcessResult.stdout).thenReturn(
|
when(() => buildProcessResult.stdout).thenReturn(
|
||||||
'''
|
'''
|
||||||
@@ -175,13 +185,28 @@ Either run `flutter pub get` manually, or follow the steps in ${cannotRunInVSCod
|
|||||||
flavor: any(named: 'flavor'),
|
flavor: any(named: 'flavor'),
|
||||||
),
|
),
|
||||||
).thenReturn(File('app-release.aab'));
|
).thenReturn(File('app-release.aab'));
|
||||||
|
when(() => buildProcess.stdout).thenAnswer(
|
||||||
|
(_) => Stream.fromIterable(
|
||||||
|
[
|
||||||
|
'Some build output',
|
||||||
|
].map(utf8.encode),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
when(() => buildProcess.stderr).thenAnswer(
|
||||||
|
(_) => Stream.fromIterable(
|
||||||
|
[
|
||||||
|
'Some build output',
|
||||||
|
].map(utf8.encode),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
when(() => buildProcess.exitCode).thenAnswer((_) async => 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('invokes the correct flutter build command', () async {
|
test('invokes the correct flutter build command', () async {
|
||||||
await runWithOverrides(() => builder.buildAppBundle());
|
await runWithOverrides(() => builder.buildAppBundle());
|
||||||
|
|
||||||
verify(
|
verify(
|
||||||
() => shorebirdProcess.run(
|
() => shorebirdProcess.start(
|
||||||
'flutter',
|
'flutter',
|
||||||
['build', 'appbundle', '--release'],
|
['build', 'appbundle', '--release'],
|
||||||
runInShell: any(named: 'runInShell'),
|
runInShell: any(named: 'runInShell'),
|
||||||
@@ -201,7 +226,7 @@ Either run `flutter pub get` manually, or follow the steps in ${cannotRunInVSCod
|
|||||||
);
|
);
|
||||||
|
|
||||||
verify(
|
verify(
|
||||||
() => shorebirdProcess.run(
|
() => shorebirdProcess.start(
|
||||||
'flutter',
|
'flutter',
|
||||||
[
|
[
|
||||||
'build',
|
'build',
|
||||||
@@ -223,7 +248,7 @@ Either run `flutter pub get` manually, or follow the steps in ${cannotRunInVSCod
|
|||||||
|
|
||||||
setUp(() {
|
setUp(() {
|
||||||
when(
|
when(
|
||||||
() => shorebirdProcess.run(
|
() => shorebirdProcess.start(
|
||||||
'flutter',
|
'flutter',
|
||||||
[
|
[
|
||||||
'build',
|
'build',
|
||||||
@@ -238,7 +263,7 @@ Either run `flutter pub get` manually, or follow the steps in ${cannotRunInVSCod
|
|||||||
'SHOREBIRD_PUBLIC_KEY': base64PublicKey,
|
'SHOREBIRD_PUBLIC_KEY': base64PublicKey,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
).thenAnswer((_) async => buildProcessResult);
|
).thenAnswer((_) async => buildProcess);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('adds the SHOREBIRD_PUBLIC_KEY to the environment', () async {
|
test('adds the SHOREBIRD_PUBLIC_KEY to the environment', () async {
|
||||||
@@ -252,7 +277,7 @@ Either run `flutter pub get` manually, or follow the steps in ${cannotRunInVSCod
|
|||||||
);
|
);
|
||||||
|
|
||||||
verify(
|
verify(
|
||||||
() => shorebirdProcess.run(
|
() => shorebirdProcess.start(
|
||||||
'flutter',
|
'flutter',
|
||||||
[
|
[
|
||||||
'build',
|
'build',
|
||||||
@@ -329,11 +354,63 @@ Either run `flutter pub get` manually, or follow the steps in ${cannotRunInVSCod
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
group('when output contains gradle task names', () {
|
||||||
|
late DetailProgress progress;
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
progress = MockDetailProgress();
|
||||||
|
|
||||||
|
when(() => buildProcess.stdout).thenAnswer(
|
||||||
|
(_) => Stream.fromIterable(
|
||||||
|
[
|
||||||
|
'Some build output',
|
||||||
|
'[ ] > Task :app:bundleRelease',
|
||||||
|
'More build output',
|
||||||
|
'[ ] > Task :app:someOtherTask',
|
||||||
|
'Even more build output',
|
||||||
|
]
|
||||||
|
.map((line) => '$line${Platform.lineTerminator}')
|
||||||
|
.map(utf8.encode),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
when(() => buildProcess.stderr).thenAnswer(
|
||||||
|
(_) => Stream.fromIterable(
|
||||||
|
['Some build output'].map(utf8.encode),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('updates progress with gradle task names', () async {
|
||||||
|
await expectLater(
|
||||||
|
runWithOverrides(
|
||||||
|
() => builder.buildAppBundle(
|
||||||
|
buildProgress: progress,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
completes,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Required to trigger stdout stream events
|
||||||
|
await pumpEventQueue();
|
||||||
|
|
||||||
|
// Ensure we update the progress in the correct order and with the
|
||||||
|
// correct messages, and reset to the base message after the build
|
||||||
|
// completes.
|
||||||
|
verifyInOrder(
|
||||||
|
[
|
||||||
|
() => progress.updateDetailMessage('Task :app:bundleRelease'),
|
||||||
|
() => progress.updateDetailMessage('Task :app:someOtherTask'),
|
||||||
|
() => progress.updateDetailMessage(null),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
group('after a build', () {
|
group('after a build', () {
|
||||||
group('when the build is successful', () {
|
group('when the build is successful', () {
|
||||||
setUp(() {
|
setUp(() {
|
||||||
when(() => buildProcessResult.exitCode)
|
when(() => buildProcess.exitCode)
|
||||||
.thenReturn(ExitCode.success.code);
|
.thenAnswer((_) async => ExitCode.success.code);
|
||||||
});
|
});
|
||||||
|
|
||||||
verifyCorrectFlutterPubGet(
|
verifyCorrectFlutterPubGet(
|
||||||
@@ -342,8 +419,8 @@ Either run `flutter pub get` manually, or follow the steps in ${cannotRunInVSCod
|
|||||||
|
|
||||||
group('when the build fails', () {
|
group('when the build fails', () {
|
||||||
setUp(() {
|
setUp(() {
|
||||||
when(() => buildProcessResult.exitCode)
|
when(() => buildProcess.exitCode)
|
||||||
.thenReturn(ExitCode.software.code);
|
.thenAnswer((_) async => ExitCode.software.code);
|
||||||
});
|
});
|
||||||
|
|
||||||
verifyCorrectFlutterPubGet(
|
verifyCorrectFlutterPubGet(
|
||||||
|
|||||||
@@ -281,6 +281,7 @@ void main() {
|
|||||||
targetPlatforms: any(named: 'targetPlatforms'),
|
targetPlatforms: any(named: 'targetPlatforms'),
|
||||||
args: any(named: 'args'),
|
args: any(named: 'args'),
|
||||||
base64PublicKey: any(named: 'base64PublicKey'),
|
base64PublicKey: any(named: 'base64PublicKey'),
|
||||||
|
buildProgress: any(named: 'buildProgress'),
|
||||||
),
|
),
|
||||||
).thenAnswer((_) async => aabFile);
|
).thenAnswer((_) async => aabFile);
|
||||||
});
|
});
|
||||||
@@ -293,7 +294,10 @@ void main() {
|
|||||||
() => artifactBuilder.buildAppBundle(
|
() => artifactBuilder.buildAppBundle(
|
||||||
flavor: any(named: 'flavor'),
|
flavor: any(named: 'flavor'),
|
||||||
target: any(named: 'target'),
|
target: any(named: 'target'),
|
||||||
|
targetPlatforms: any(named: 'targetPlatforms'),
|
||||||
args: any(named: 'args'),
|
args: any(named: 'args'),
|
||||||
|
base64PublicKey: any(named: 'base64PublicKey'),
|
||||||
|
buildProgress: any(named: 'buildProgress'),
|
||||||
),
|
),
|
||||||
).thenThrow(exception);
|
).thenThrow(exception);
|
||||||
when(() => logger.progress(any())).thenReturn(progress);
|
when(() => logger.progress(any())).thenReturn(progress);
|
||||||
@@ -350,6 +354,7 @@ Looked in:
|
|||||||
named: 'args',
|
named: 'args',
|
||||||
that: containsAll(['--build-name=1.2.3', '--build-number=4']),
|
that: containsAll(['--build-name=1.2.3', '--build-number=4']),
|
||||||
),
|
),
|
||||||
|
buildProgress: any(named: 'buildProgress'),
|
||||||
),
|
),
|
||||||
).called(1);
|
).called(1);
|
||||||
});
|
});
|
||||||
@@ -369,6 +374,7 @@ Looked in:
|
|||||||
verify(
|
verify(
|
||||||
() => artifactBuilder.buildAppBundle(
|
() => artifactBuilder.buildAppBundle(
|
||||||
args: ['--verbose'],
|
args: ['--verbose'],
|
||||||
|
buildProgress: any(named: 'buildProgress'),
|
||||||
),
|
),
|
||||||
).called(1);
|
).called(1);
|
||||||
});
|
});
|
||||||
@@ -401,6 +407,7 @@ Looked in:
|
|||||||
flavor: any(named: 'flavor'),
|
flavor: any(named: 'flavor'),
|
||||||
target: any(named: 'target'),
|
target: any(named: 'target'),
|
||||||
base64PublicKey: 'public_key_encoded',
|
base64PublicKey: 'public_key_encoded',
|
||||||
|
buildProgress: any(named: 'buildProgress'),
|
||||||
),
|
),
|
||||||
).called(1);
|
).called(1);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -289,6 +289,7 @@ To change the version of this release, change your app's version in your pubspec
|
|||||||
target: any(named: 'target'),
|
target: any(named: 'target'),
|
||||||
targetPlatforms: any(named: 'targetPlatforms'),
|
targetPlatforms: any(named: 'targetPlatforms'),
|
||||||
args: any(named: 'args'),
|
args: any(named: 'args'),
|
||||||
|
buildProgress: any(named: 'buildProgress'),
|
||||||
),
|
),
|
||||||
).thenAnswer((_) async => aabFile);
|
).thenAnswer((_) async => aabFile);
|
||||||
when(
|
when(
|
||||||
@@ -320,6 +321,7 @@ To change the version of this release, change your app's version in your pubspec
|
|||||||
target: any(named: 'target'),
|
target: any(named: 'target'),
|
||||||
targetPlatforms: any(named: 'targetPlatforms'),
|
targetPlatforms: any(named: 'targetPlatforms'),
|
||||||
args: any(named: 'args'),
|
args: any(named: 'args'),
|
||||||
|
buildProgress: any(named: 'buildProgress'),
|
||||||
),
|
),
|
||||||
).thenThrow(ArtifactBuildException('Uh oh'));
|
).thenThrow(ArtifactBuildException('Uh oh'));
|
||||||
});
|
});
|
||||||
@@ -375,6 +377,7 @@ To change the version of this release, change your app's version in your pubspec
|
|||||||
() => artifactBuilder.buildAppBundle(
|
() => artifactBuilder.buildAppBundle(
|
||||||
targetPlatforms: Arch.values,
|
targetPlatforms: Arch.values,
|
||||||
args: ['--verbose'],
|
args: ['--verbose'],
|
||||||
|
buildProgress: any(named: 'buildProgress'),
|
||||||
),
|
),
|
||||||
).called(1);
|
).called(1);
|
||||||
});
|
});
|
||||||
@@ -389,6 +392,7 @@ To change the version of this release, change your app's version in your pubspec
|
|||||||
() => artifactBuilder.buildAppBundle(
|
() => artifactBuilder.buildAppBundle(
|
||||||
targetPlatforms: Arch.values,
|
targetPlatforms: Arch.values,
|
||||||
args: [],
|
args: [],
|
||||||
|
buildProgress: any(named: 'buildProgress'),
|
||||||
),
|
),
|
||||||
).called(1);
|
).called(1);
|
||||||
});
|
});
|
||||||
@@ -429,6 +433,7 @@ To change the version of this release, change your app's version in your pubspec
|
|||||||
target: target,
|
target: target,
|
||||||
targetPlatforms: Arch.values,
|
targetPlatforms: Arch.values,
|
||||||
args: [],
|
args: [],
|
||||||
|
buildProgress: any(named: 'buildProgress'),
|
||||||
),
|
),
|
||||||
).called(1);
|
).called(1);
|
||||||
verify(
|
verify(
|
||||||
@@ -462,6 +467,7 @@ To change the version of this release, change your app's version in your pubspec
|
|||||||
targetPlatforms: any(named: 'targetPlatforms'),
|
targetPlatforms: any(named: 'targetPlatforms'),
|
||||||
args: any(named: 'args'),
|
args: any(named: 'args'),
|
||||||
base64PublicKey: any(named: 'base64PublicKey'),
|
base64PublicKey: any(named: 'base64PublicKey'),
|
||||||
|
buildProgress: any(named: 'buildProgress'),
|
||||||
),
|
),
|
||||||
).thenAnswer((_) async => aabFile);
|
).thenAnswer((_) async => aabFile);
|
||||||
when(
|
when(
|
||||||
@@ -492,6 +498,7 @@ To change the version of this release, change your app's version in your pubspec
|
|||||||
targetPlatforms: any(named: 'targetPlatforms'),
|
targetPlatforms: any(named: 'targetPlatforms'),
|
||||||
args: any(named: 'args'),
|
args: any(named: 'args'),
|
||||||
base64PublicKey: base64PublicKey,
|
base64PublicKey: base64PublicKey,
|
||||||
|
buildProgress: any(named: 'buildProgress'),
|
||||||
),
|
),
|
||||||
).called(1);
|
).called(1);
|
||||||
},
|
},
|
||||||
@@ -516,6 +523,7 @@ To change the version of this release, change your app's version in your pubspec
|
|||||||
targetPlatforms: any(named: 'targetPlatforms'),
|
targetPlatforms: any(named: 'targetPlatforms'),
|
||||||
args: any(named: 'args'),
|
args: any(named: 'args'),
|
||||||
base64PublicKey: base64PublicKey,
|
base64PublicKey: base64PublicKey,
|
||||||
|
buildProgress: any(named: 'buildProgress'),
|
||||||
),
|
),
|
||||||
).called(1);
|
).called(1);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,122 @@
|
|||||||
|
import 'package:mason_logger/mason_logger.dart';
|
||||||
|
import 'package:mocktail/mocktail.dart';
|
||||||
|
import 'package:scoped_deps/scoped_deps.dart';
|
||||||
|
import 'package:shorebird_cli/src/logging/logging.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
import '../mocks.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group(DetailProgress, () {
|
||||||
|
late ShorebirdLogger logger;
|
||||||
|
late Progress progress;
|
||||||
|
late DetailProgress detailProgress;
|
||||||
|
|
||||||
|
R runWithOverrides<R>(R Function() body) {
|
||||||
|
return runScoped(
|
||||||
|
body,
|
||||||
|
values: {
|
||||||
|
loggerRef.overrideWith(() => logger),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
logger = MockShorebirdLogger();
|
||||||
|
progress = MockProgress();
|
||||||
|
|
||||||
|
when(() => logger.progress(any())).thenReturn(progress);
|
||||||
|
|
||||||
|
detailProgress = runWithOverrides(() => logger.detailProgress('title'));
|
||||||
|
});
|
||||||
|
|
||||||
|
group('updatePrimaryMessage', () {
|
||||||
|
group('when no detail message is set', () {
|
||||||
|
test('updates the primary message', () {
|
||||||
|
detailProgress.updatePrimaryMessage('new title');
|
||||||
|
verify(() => progress.update('new title')).called(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group('when a detail message is present', () {
|
||||||
|
setUp(() {
|
||||||
|
detailProgress.updateDetailMessage('detail');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('updates the primary message and detail message', () {
|
||||||
|
detailProgress.updatePrimaryMessage('new title');
|
||||||
|
verify(
|
||||||
|
() => progress.update('new title ${darkGray.wrap('detail')}'),
|
||||||
|
).called(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group('updateDetailMessage', () {
|
||||||
|
test('updates the detail message', () {
|
||||||
|
detailProgress.updateDetailMessage('new detail');
|
||||||
|
verify(
|
||||||
|
() => progress.update('title ${darkGray.wrap('new detail')}'),
|
||||||
|
).called(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('can unset the detail message', () {
|
||||||
|
detailProgress.updateDetailMessage(null);
|
||||||
|
verify(() => progress.update('title')).called(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group('update', () {
|
||||||
|
test('updates the primary message', () {
|
||||||
|
detailProgress.update('new title');
|
||||||
|
verify(() => progress.update('new title')).called(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
group('when a detail message is set', () {
|
||||||
|
setUp(() {
|
||||||
|
detailProgress.updateDetailMessage('detail');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('unsets the detail message', () {
|
||||||
|
detailProgress.update('new title');
|
||||||
|
verify(() => progress.update('new title')).called(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group('cancel', () {
|
||||||
|
test('cancels the progress', () {
|
||||||
|
detailProgress.cancel();
|
||||||
|
verify(() => progress.cancel()).called(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group('complete', () {
|
||||||
|
test('completes the progress', () {
|
||||||
|
detailProgress.complete();
|
||||||
|
verify(() => progress.complete('title')).called(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('completes the progress with an update', () {
|
||||||
|
detailProgress.complete('update');
|
||||||
|
verify(() => progress.complete('update')).called(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group('fail', () {
|
||||||
|
test('fails the progress', () {
|
||||||
|
detailProgress.fail();
|
||||||
|
verify(() => progress.fail('title')).called(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('fails the progress with an update', () {
|
||||||
|
detailProgress.fail('update');
|
||||||
|
verify(() => progress.fail('update')).called(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('implements Progress', () {
|
||||||
|
expect(detailProgress, isA<Progress>());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -81,6 +81,8 @@ class MockCodePushClientWrapper extends Mock implements CodePushClientWrapper {}
|
|||||||
|
|
||||||
class MockCodeSigner extends Mock implements CodeSigner {}
|
class MockCodeSigner extends Mock implements CodeSigner {}
|
||||||
|
|
||||||
|
class MockDetailProgress extends Mock implements DetailProgress {}
|
||||||
|
|
||||||
class MockDevicectl extends Mock implements Devicectl {}
|
class MockDevicectl extends Mock implements Devicectl {}
|
||||||
|
|
||||||
class MockDirectory extends Mock implements Directory {}
|
class MockDirectory extends Mock implements Directory {}
|
||||||
|
|||||||
Reference in New Issue
Block a user