feat: add more updates to "shorebird release ios" progress (#2623)

This commit is contained in:
Bryan Oltman
2024-11-15 15:46:23 -05:00
committed by GitHub
parent 3dd721b00a
commit a1ef672b51
6 changed files with 334 additions and 189 deletions
@@ -3,9 +3,10 @@
import 'dart:convert';
import 'dart:io';
import 'package:collection/collection.dart';
import 'package:mason_logger/mason_logger.dart';
import 'package:meta/meta.dart';
import 'package:scoped_deps/scoped_deps.dart';
import 'package:shorebird_cli/src/extensions/shorebird_process_result.dart';
import 'package:shorebird_cli/src/logging/logging.dart';
import 'package:shorebird_cli/src/os/operating_system_interface.dart';
import 'package:shorebird_cli/src/platform/platform.dart';
@@ -263,6 +264,7 @@ class ArtifactBuilder {
String? target,
List<String> args = const [],
String? base64PublicKey,
DetailProgress? buildProgress,
}) async {
String? appDillPath;
await _runShorebirdBuildCommand(() async {
@@ -277,30 +279,54 @@ class ArtifactBuilder {
...args,
];
final result = await process.run(
final buildProcess = await process.start(
executable,
arguments,
runInShell: true,
environment: base64PublicKey?.toPublicKeyEnv(),
);
if (result.exitCode != ExitCode.success.code) {
throw ArtifactBuildException('Failed to build: ${result.stderr}');
final stdoutLines = <String>[];
buildProcess.stdout
.transform(utf8.decoder)
.transform(const LineSplitter())
.listen((line) {
stdoutLines.add(line);
if (buildProgress == null) {
return;
}
final update = _progressUpdateFromIpaBuildLog(line);
if (update != null) {
buildProgress.updateDetailMessage(update);
}
});
final stderrLines = await buildProcess.stderr
.transform(utf8.decoder)
.transform(const LineSplitter())
.toList();
final stderr = stderrLines.join('\n');
final stdout = stdoutLines.join('\n');
final exitCode = await buildProcess.exitCode;
// If we've been updating the progress, reset it to the original base
// message so as not to leave the user with a confusing message.
buildProgress?.updateDetailMessage(null);
if (exitCode != ExitCode.success.code) {
throw ArtifactBuildException('Failed to build: $stderr');
}
if (result.stderr
.toString()
.contains('Encountered error while creating the IPA')) {
final errorMessage = _failedToCreateIpaErrorMessage(
stderr: result.stderr.toString(),
);
if (stderr.contains('Encountered error while creating the IPA')) {
final errorMessage = _failedToCreateIpaErrorMessage(stderr: stderr);
throw ArtifactBuildException('''
Failed to build:
$errorMessage''');
}
appDillPath = result.findAppDill();
appDillPath = findAppDill(stdout: stdout);
});
if (appDillPath == null) {
@@ -338,7 +364,7 @@ Please file a bug at https://github.com/shorebirdtech/shorebird/issues/new with
throw ArtifactBuildException('Failed to build: ${result.stderr}');
}
appDillPath = result.findAppDill();
appDillPath = findAppDill(stdout: result.stdout.toString());
});
if (appDillPath == null) {
@@ -454,4 +480,63 @@ Either run `flutter pub get` manually, or follow the steps in ${cannotRunInVSCod
return File(outFilePath);
}
/// Given a log of verbose output from `flutter build ipa`, returns a
/// progress update message to display to the user if the line contains
/// a known progress update step. Returns null (no update) otherwise.
String? _progressUpdateFromIpaBuildLog(String line) {
// xcodebuild -list is a command run early in `flutter build ipa` to read
// build settings and schemes. Most users aren't familiar with this command,
// so we translate it to "Collecting schemes" below.
final collectingSchemesRegex =
RegExp(r'\[.*\] executing:.*xcrun xcodebuild -list$');
final archivingRegex = RegExp(r'^\[.*\] (Archiving .+$)');
final runningXcodeBuildRegex = RegExp(r'^\[.*\] (Running Xcode build).*$');
final compilingLinkingSigningRegex =
RegExp(r'^\[.*\]\s+└─(Compiling, linking and signing).*$');
final buildingAppStoreIpaRegex =
RegExp(r'^\[.*\] (Building App Store IPA).*$');
final builtAppStoreIpaRegex = RegExp(r'^\[.*\] ✓ (Built IPA to \S+).*$');
final regexes = [
archivingRegex,
collectingSchemesRegex,
runningXcodeBuildRegex,
compilingLinkingSigningRegex,
buildingAppStoreIpaRegex,
builtAppStoreIpaRegex,
];
for (final regex in regexes) {
final match = regex.firstMatch(line);
if (match == null) continue;
// See the note above about the collectingSchemesRegex.
if (regex == collectingSchemesRegex) {
return 'Collecting schemes';
}
return match.group(1);
}
return null;
}
/// Given the full stdout from a `flutter build ipa` command, finds the path
/// to the app.dill file that was built.
@visibleForTesting
String? findAppDill({required String stdout}) {
final appDillLine = stdout.split('\n').firstWhereOrNull(
(l) => l.contains('gen_snapshot') && l.endsWith('app.dill'),
);
if (appDillLine == null) return null;
// The last argument in the line is the path to app.dill. Because
// 1) paths can contain spaces and
// 2) the path to the app.dill is absolute (i.e., it starts with a '/')
// we can grab the last space-separated part of the line that starts with
// a '/' and assume everything after it is the path to app.dill.
return '/${appDillLine.split(' /').last}';
}
}
@@ -107,8 +107,9 @@ For more information see: ${supportedFlutterVersionsUrl.toLink()}''',
}
final flutterVersionString = await shorebirdFlutter.getVersionAndRevision();
final buildProgress =
logger.progress('Building ipa with Flutter $flutterVersionString');
final buildProgress = logger.detailProgress(
'Building app bundle with Flutter $flutterVersionString',
);
try {
await artifactBuilder.buildIpa(
@@ -117,6 +118,7 @@ For more information see: ${supportedFlutterVersionsUrl.toLink()}''',
target: target,
args: argResults.forwardedArgs,
base64PublicKey: argResults.encodedPublicKey,
buildProgress: buildProgress,
);
buildProgress.complete();
} on ArtifactBuildException catch (error) {
@@ -1,27 +0,0 @@
import 'package:collection/collection.dart';
import 'package:shorebird_cli/src/shorebird_process.dart';
/// Extensions for finding `app.dill` in a [ShorebirdProcessResult].
extension FindAppDill on ShorebirdProcessResult {
/// Finds a line in stdout that invokes gen_snapshot with app.dill as an
/// argument. The path to the app.dill file is the last argument in the line.
///
/// Example matching line from `flutter build ipa`:
/// [ ] executing: /Users/bryanoltman/shorebirdtech/_shorebird/shorebird/bin/cache/flutter/985ec84cb99d3c60341e2c78be9826e0a88cc697/bin/cache/artifacts/engine/ios-release/gen_snapshot_arm64 --deterministic --snapshot_kind=app-aot-assembly --assembly=/Users/bryanoltman/Documents/sandbox/ios_signing/.dart_tool/flutter_build/804399dd5f8e05d7b9ec7e0bb4ceb22c/arm64/snapshot_assembly.S /Users/bryanoltman/Documents/sandbox/ios_signing/.dart_tool/flutter_build/804399dd5f8e05d7b9ec7e0bb4ceb22c/app.dill
///
/// Returns null if no matching line is found.
String? findAppDill() {
final appDillLine = stdout.toString().split('\n').firstWhereOrNull(
(l) => l.contains('gen_snapshot') && l.endsWith('app.dill'),
);
if (appDillLine == null) return null;
// The last argument in the line is the path to app.dill. Because
// 1) paths can contain spaces and
// 2) the path to the app.dill is absolute (i.e., it starts with a '/')
// we can grab the last space-separated part of the line that starts with
// a '/' and assume everything after it is the path to app.dill.
return '/${appDillLine.split(' /').last}';
}
}
@@ -91,21 +91,32 @@ void main() {
runInShell: any(named: 'runInShell'),
),
).thenAnswer((_) async => buildProcessResult);
when(() => buildProcessResult.exitCode).thenReturn(ExitCode.success.code);
when(() => buildProcessResult.stdout).thenReturn('some stdout');
when(
() => shorebirdProcess.start(
any(),
any(),
runInShell: any(named: 'runInShell'),
environment: any(named: 'environment'),
),
).thenAnswer((_) async => buildProcess);
when(() => buildProcessResult.exitCode).thenReturn(ExitCode.success.code);
when(() => buildProcessResult.stdout).thenReturn(
'''
[ ] Will strip AOT snapshot manually after build and dSYM generation.
[ ] executing: /bin/cache/artifacts/engine/ios-release/gen_snapshot_arm64 --deterministic --snapshot_kind=app-aot-assembly --assembly=snapshot_assembly.S /path/to/app.dill
[+3688 ms] executing: sysctl hw.optional.arm64
''',
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);
when(() => logger.progress(any())).thenReturn(MockProgress());
when(() => logger.info(any())).thenReturn(null);
when(() => operatingSystemInterface.which('flutter'))
@@ -184,21 +195,6 @@ Either run `flutter pub get` manually, or follow the steps in ${cannotRunInVSCod
flavor: any(named: 'flavor'),
),
).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 {
@@ -709,12 +705,26 @@ Either run `flutter pub get` manually, or follow the steps in ${cannotRunInVSCod
group(
'buildIpa',
() {
setUp(() {
when(() => buildProcess.stdout).thenAnswer(
(_) => Stream.fromIterable(
[
'''
[ ] Will strip AOT snapshot manually after build and dSYM generation.
[ ] executing: /bin/cache/artifacts/engine/ios-release/gen_snapshot_arm64 --deterministic --snapshot_kind=app-aot-assembly --assembly=snapshot_assembly.S /path/to/app.dill
[+3688 ms] executing: sysctl hw.optional.arm64
''',
].map(utf8.encode),
),
);
});
group('with default arguments', () {
test('invokes flutter build with an export options plist', () async {
final result = await runWithOverrides(builder.buildIpa);
verify(
() => shorebirdProcess.run(
() => shorebirdProcess.start(
'flutter',
[
'build',
@@ -734,7 +744,7 @@ Either run `flutter pub get` manually, or follow the steps in ${cannotRunInVSCod
setUp(() {
when(
() => shorebirdProcess.run(
() => shorebirdProcess.start(
'flutter',
[
'build',
@@ -746,7 +756,7 @@ Either run `flutter pub get` manually, or follow the steps in ${cannotRunInVSCod
'SHOREBIRD_PUBLIC_KEY': base64PublicKey,
},
),
).thenAnswer((_) async => buildProcessResult);
).thenAnswer((_) async => buildProcess);
});
test('adds the SHOREBIRD_PUBLIC_KEY to the environment', () async {
@@ -757,7 +767,7 @@ Either run `flutter pub get` manually, or follow the steps in ${cannotRunInVSCod
);
verify(
() => shorebirdProcess.run(
() => shorebirdProcess.start(
'flutter',
[
'build',
@@ -784,7 +794,7 @@ Either run `flutter pub get` manually, or follow the steps in ${cannotRunInVSCod
);
verify(
() => shorebirdProcess.run(
() => shorebirdProcess.start(
'flutter',
[
'build',
@@ -801,11 +811,77 @@ Either run `flutter pub get` manually, or follow the steps in ${cannotRunInVSCod
).called(1);
});
group('when progress contains known build steps', () {
late DetailProgress progress;
setUp(() {
progress = MockDetailProgress();
when(() => buildProcess.stdout).thenAnswer(
(_) => Stream.fromIterable(
[
// cSpell:disable
'''
[ ] Will strip AOT snapshot manually after build and dSYM generation.
[ ] executing: /bin/cache/artifacts/engine/ios-release/gen_snapshot_arm64 --deterministic --snapshot_kind=app-aot-assembly --assembly=snapshot_assembly.S /path/to/app.dill
[+3688 ms] executing: sysctl hw.optional.arm64''',
'[ +10 ms] Generating /Users/bryanoltman/Documents/sandbox/notification_extension/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java',
'[ +50 ms] executing: [/Users/bryanoltman/Documents/sandbox/notification_extension/ios/] /usr/bin/arch -arm64e xcrun xcodebuild -list',
'[+32333 ms] Command line invocation:',
'[ +6 ms] Exit code 0 from: mkfifo /var/folders/64/dj6krpq1093dmx08dy4r1cwh0000gn/T/flutter_tools.WDvaE9/flutter_ios_build_temp_dirUAyStV/pipe_to_stdout',
'[ +1 ms] Running Xcode build...',
'[ ] executing: [/Users/bryanoltman/Documents/sandbox/notification_extension/ios/] /usr/bin/arch -arm64e xcrun xcodebuild -configuration Release VERBOSE_SCRIPT_LOGGING=YES -workspace Runner.xcworkspace -scheme Runner -sdk iphoneos -destination generic/platform=iOS SCRIPT_OUTPUT_STREAM_FILE=/var/folders/64/dj6krpq1093dmx08dy4r1cwh0000gn/T/flutter_tools.WDvaE9/flutter_ios_build_temp_dirUAyStV/pipe_to_stdout -resultBundlePath /var/folders/64/dj6krpq1093dmx08dy4r1cwh0000gn/T/flutter_tools.WDvaE9/flutter_ios_build_temp_dirUAyStV/temporary_xcresult_bundle -resultBundleVersion 3 FLUTTER_SUPPRESS_ANALYTICS=true COMPILER_INDEX_STORE_ENABLE=NO -archivePath /Users/bryanoltman/Documents/sandbox/notification_extension/build/ios/archive/Runner archive',
'[+62601 ms] Running Xcode build... (completed in 62.6s)',
'[ ] └─Compiling, linking and signing...',
'[+5925 ms] Command line invocation:',
'/Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild -configuration Release VERBOSE_SCRIPT_LOGGING=YES -workspace Runner.xcworkspace -scheme Runner -sdk iphoneos -destination generic/platform=iOS SCRIPT_OUTPUT_STREAM_FILE=/var/folders/64/dj6krpq1093dmx08dy4r1cwh0000gn/T/flutter_tools.WDvaE9/flutter_ios_build_temp_dirUAyStV/pipe_to_stdout -resultBundlePath /var/folders/64/dj6krpq1093dmx08dy4r1cwh0000gn/T/flutter_tools.WDvaE9/flutter_ios_build_temp_dirUAyStV/temporary_xcresult_bundle -resultBundleVersion 3 FLUTTER_SUPPRESS_ANALYTICS=true COMPILER_INDEX_STORE_ENABLE=NO -archivePath /Users/bryanoltman/Documents/sandbox/notification_extension/build/ios/archive/Runner archive',
// cSpell:enable
]
.map((line) => '$line${Platform.lineTerminator}')
.map(utf8.encode),
),
);
when(() => buildProcess.stderr).thenAnswer(
(_) => Stream.fromIterable(
['Some build output'].map(utf8.encode),
),
);
});
test('updates progress with known build steps', () async {
await expectLater(
runWithOverrides(
() => builder.buildIpa(
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('Collecting schemes'),
() => progress.updateDetailMessage('Running Xcode build'),
() => progress.updateDetailMessage('Running Xcode build'),
() => progress.updateDetailMessage(
'Compiling, linking and signing',
),
],
);
});
});
group('when the build fails', () {
group('with non-zero exit code', () {
setUp(() {
when(() => buildProcessResult.exitCode)
.thenReturn(ExitCode.software.code);
when(() => buildProcess.exitCode)
.thenAnswer((_) async => ExitCode.software.code);
});
test('throws ArtifactBuildException', () {
@@ -818,10 +894,12 @@ Either run `flutter pub get` manually, or follow the steps in ${cannotRunInVSCod
group('with error message in stderr (Xcode <= 15.x)', () {
setUp(() {
when(() => buildProcessResult.exitCode)
.thenReturn(ExitCode.success.code);
when(() => buildProcessResult.stderr).thenReturn(
'''
when(() => buildProcess.exitCode)
.thenAnswer((_) async => ExitCode.success.code);
when(() => buildProcess.stderr).thenAnswer(
(_) => Stream.fromIterable(
[
'''
Encountered error while creating the IPA:
error: exportArchive: Communication with Apple failed
error: exportArchive: No signing certificate "iOS Distribution" found
@@ -835,6 +913,8 @@ error: exportArchive: Communication with Apple failed
error: exportArchive: No signing certificate "iOS Distribution" found
error: exportArchive: Communication with Apple failed
error: exportArchive: No signing certificate "iOS Distribution" found''',
].map(utf8.encode),
),
);
});
@@ -859,10 +939,12 @@ Failed to build:
group('with error message in stderr (Xcode >= 16.x)', () {
setUp(() {
when(() => buildProcessResult.exitCode)
.thenReturn(ExitCode.success.code);
when(() => buildProcessResult.stderr).thenReturn(
'''
when(() => buildProcess.exitCode)
.thenAnswer((_) async => ExitCode.success.code);
when(() => buildProcess.stderr).thenAnswer(
(_) => Stream.fromIterable(
[
'''
Encountered error while creating the IPA:
error: exportArchive Communication with Apple failed
error: exportArchive No signing certificate "iOS Distribution" found
@@ -876,6 +958,8 @@ error: exportArchive Communication with Apple failed
error: exportArchive No signing certificate "iOS Distribution" found
error: exportArchive Communication with Apple failed
error: exportArchive No signing certificate "iOS Distribution" found''',
].map(utf8.encode),
),
);
});
@@ -901,7 +985,13 @@ Failed to build:
group('when an app.dill file is not found in build stdout', () {
setUp(() {
when(() => buildProcessResult.stdout).thenReturn('no app.dill');
when(() => buildProcess.stdout).thenAnswer(
(_) => Stream.fromIterable(
[
'no app.dill',
].map(utf8.encode),
),
);
});
test('throws ArtifactBuildException', () {
@@ -924,8 +1014,9 @@ Please file a bug at https://github.com/shorebirdtech/shorebird/issues/new with
group('after a build', () {
group('when the build is successful', () {
setUp(() {
when(() => buildProcessResult.exitCode)
.thenReturn(ExitCode.success.code);
when(
() => buildProcess.exitCode,
).thenAnswer((_) async => ExitCode.success.code);
});
verifyCorrectFlutterPubGet(
@@ -936,8 +1027,9 @@ Please file a bug at https://github.com/shorebirdtech/shorebird/issues/new with
group('when the build fails', () {
setUp(() {
when(() => buildProcessResult.exitCode)
.thenReturn(ExitCode.software.code);
when(
() => buildProcess.exitCode,
).thenAnswer((_) async => ExitCode.software.code);
});
verifyCorrectFlutterPubGet(
@@ -958,6 +1050,16 @@ Please file a bug at https://github.com/shorebirdtech/shorebird/issues/new with
group(
'buildIosFramework',
() {
setUp(() {
when(() => buildProcessResult.stdout).thenReturn(
'''
[ ] Will strip AOT snapshot manually after build and dSYM generation.
[ ] executing: /bin/cache/artifacts/engine/ios-release/gen_snapshot_arm64 --deterministic --snapshot_kind=app-aot-assembly --assembly=snapshot_assembly.S /path/to/app.dill
[+3688 ms] executing: sysctl hw.optional.arm64
''',
);
});
test('invokes the correct flutter build command', () async {
final result = await runWithOverrides(builder.buildIosFramework);
@@ -1116,5 +1218,68 @@ Please file a bug at https://github.com/shorebirdtech/shorebird/issues/new with
},
testOn: 'mac-os',
);
group('findAppDill', () {
group('when gen_snapshot is invoked with app.dill', () {
test('returns the path to app.dill', () {
const result = '''
[ ] Will strip AOT snapshot manually after build and dSYM generation.
[ ] executing: /Users/bryanoltman/shorebirdtech/_shorebird/shorebird/bin/cache/flutter/985ec84cb99d3c60341e2c78be9826e0a88cc697/bin/cache/artifacts/engine/ios-release/gen_snapshot_arm64 --deterministic --snapshot_kind=app-aot-assembly --assembly=/Users/bryanoltman/Documents/sandbox/ios_signing/.dart_tool/flutter_build/804399dd5f8e05d7b9ec7e0bb4ceb22c/arm64/snapshot_assembly.S /Users/bryanoltman/Documents/sandbox/ios_signing/.dart_tool/flutter_build/804399dd5f8e05d7b9ec7e0bb4ceb22c/app.dill
[+3688 ms] executing: sysctl hw.optional.arm64
''';
expect(
builder.findAppDill(stdout: result),
equals(
'/Users/bryanoltman/Documents/sandbox/ios_signing/.dart_tool/flutter_build/804399dd5f8e05d7b9ec7e0bb4ceb22c/app.dill',
),
);
});
test('returns the path to app.dill (local engine)', () {
const result = '''
[ ] Will strip AOT snapshot manually after build and dSYM generation.
[ ] executing: /Users/felix/Development/github.com/shorebirdtech/engine/src/out/ios_release/clang_x64/gen_snapshot_arm64 --deterministic --snapshot_kind=app-aot-assembly --assembly=/Users/felix/Development/github.com/felangel/flutter_and_friends/.dart_tool/flutter_build/ae2d368b5940aefb0c55ff62186de056/arm64/snapshot_assembly.S /Users/felix/Development/github.com/felangel/flutter_and_friends/.dart_tool/flutter_build/ae2d368b5940aefb0c55ff62186de056/app.dill
[+5435 ms] executing: sysctl hw.optional.arm64
''';
expect(
builder.findAppDill(stdout: result),
equals(
'/Users/felix/Development/github.com/felangel/flutter_and_friends/.dart_tool/flutter_build/ae2d368b5940aefb0c55ff62186de056/app.dill',
),
);
});
group('when path to app.dill contains a space', () {
test('returns full path to app.dill, including the space(s)', () {
const result = '''
[ +3 ms] targetingApplePlatform = true
[ ] extractAppleDebugSymbols = true
[ ] Will strip AOT snapshot manually after build and dSYM generation.
[ ] executing: /Users/bryanoltman/shorebirdtech/_shorebird/shorebird/bin/cache/flutter/9015e1b42a1ba41d97176e22b502b0e0e8ad28af/bin/cache/artifacts/engine/ios-release/gen_snapshot_arm64 --deterministic --snapshot_kind=app-aot-assembly --assembly=/Users/bryanoltman/Documents/sandbox/folder with space/ios_patcher/.dart_tool/flutter_build/cd4f4aa272817365910648606e3e4164/arm64/snapshot_assembly.S /Users/bryanoltman/Documents/sandbox/folder with space/ios_patcher/.dart_tool/flutter_build/cd4f4aa272817365910648606e3e4164/app.dill
[+3395 ms] executing: sysctl hw.optional.arm64
[ +3 ms] Exit code 0 from: sysctl hw.optional.arm64
''';
expect(
builder.findAppDill(stdout: result),
equals(
'/Users/bryanoltman/Documents/sandbox/folder with space/ios_patcher/.dart_tool/flutter_build/cd4f4aa272817365910648606e3e4164/app.dill',
),
);
});
});
});
group('when gen_snapshot is not invoked with app.dill', () {
test('returns null', () {
const result =
'executing: .../gen_snapshot_arm64 .../snapshot_assembly.S';
expect(builder.findAppDill(stdout: result), isNull);
});
});
});
});
}
@@ -335,6 +335,7 @@ To change the version of this release, change your app's version in your pubspec
flavor: any(named: 'flavor'),
target: any(named: 'target'),
args: any(named: 'args'),
buildProgress: any(named: 'buildProgress'),
),
).thenAnswer(
(_) async => IpaBuildResult(
@@ -378,6 +379,7 @@ To change the version of this release, change your app's version in your pubspec
target: any(named: 'target'),
args: any(named: 'args'),
base64PublicKey: any(named: 'base64PublicKey'),
buildProgress: any(named: 'buildProgress'),
),
).thenAnswer(
(_) async => IpaBuildResult(
@@ -400,6 +402,7 @@ To change the version of this release, change your app's version in your pubspec
target: any(named: 'target'),
args: any(named: 'args'),
base64PublicKey: base64PublicKey,
buildProgress: any(named: 'buildProgress'),
),
).called(1);
},
@@ -435,6 +438,7 @@ To change the version of this release, change your app's version in your pubspec
flavor: any(named: 'flavor'),
target: any(named: 'target'),
args: any(named: 'args'),
buildProgress: any(named: 'buildProgress'),
),
).thenThrow(ArtifactBuildException('Failed to build'));
});
@@ -452,31 +456,30 @@ To change the version of this release, change your app's version in your pubspec
});
group('when build succeeds', () {
group('when build succeeds', () {
group('when platform was specified via arg results rest', () {
setUp(() {
when(() => argResults.rest).thenReturn(['ios', '--verbose']);
});
group('when platform was specified via arg results rest', () {
setUp(() {
when(() => argResults.rest).thenReturn(['ios', '--verbose']);
});
test('verifies artifacts exist and returns xcarchive path',
() async {
expect(
await runWithOverrides(iosReleaser.buildReleaseArtifacts),
equals(xcarchiveDirectory),
);
test('verifies artifacts exist and returns xcarchive path',
() async {
expect(
await runWithOverrides(iosReleaser.buildReleaseArtifacts),
equals(xcarchiveDirectory),
);
verify(() => artifactManager.getXcarchiveDirectory()).called(1);
verify(
() => artifactManager.getIosAppDirectory(
xcarchiveDirectory: xcarchiveDirectory,
),
).called(1);
verify(
() => artifactBuilder.buildIpa(
args: ['--verbose'],
),
).called(1);
});
verify(() => artifactManager.getXcarchiveDirectory()).called(1);
verify(
() => artifactManager.getIosAppDirectory(
xcarchiveDirectory: xcarchiveDirectory,
),
).called(1);
verify(
() => artifactBuilder.buildIpa(
args: ['--verbose'],
buildProgress: any(named: 'buildProgress'),
),
).called(1);
});
});
@@ -1,83 +0,0 @@
import 'package:shorebird_cli/src/extensions/shorebird_process_result.dart';
import 'package:shorebird_cli/src/shorebird_process.dart';
import 'package:test/test.dart';
void main() {
group('FindAppDill', () {
group('when gen_snapshot is invoked with app.dill', () {
test('returns the path to app.dill', () {
const result = ShorebirdProcessResult(
stdout: '''
[ ] Will strip AOT snapshot manually after build and dSYM generation.
[ ] executing: /Users/bryanoltman/shorebirdtech/_shorebird/shorebird/bin/cache/flutter/985ec84cb99d3c60341e2c78be9826e0a88cc697/bin/cache/artifacts/engine/ios-release/gen_snapshot_arm64 --deterministic --snapshot_kind=app-aot-assembly --assembly=/Users/bryanoltman/Documents/sandbox/ios_signing/.dart_tool/flutter_build/804399dd5f8e05d7b9ec7e0bb4ceb22c/arm64/snapshot_assembly.S /Users/bryanoltman/Documents/sandbox/ios_signing/.dart_tool/flutter_build/804399dd5f8e05d7b9ec7e0bb4ceb22c/app.dill
[+3688 ms] executing: sysctl hw.optional.arm64
''',
stderr: '',
exitCode: 0,
);
expect(
result.findAppDill(),
equals(
'/Users/bryanoltman/Documents/sandbox/ios_signing/.dart_tool/flutter_build/804399dd5f8e05d7b9ec7e0bb4ceb22c/app.dill',
),
);
});
test('returns the path to app.dill (local engine)', () {
const result = ShorebirdProcessResult(
stdout: '''
[ ] Will strip AOT snapshot manually after build and dSYM generation.
[ ] executing: /Users/felix/Development/github.com/shorebirdtech/engine/src/out/ios_release/clang_x64/gen_snapshot_arm64 --deterministic --snapshot_kind=app-aot-assembly --assembly=/Users/felix/Development/github.com/felangel/flutter_and_friends/.dart_tool/flutter_build/ae2d368b5940aefb0c55ff62186de056/arm64/snapshot_assembly.S /Users/felix/Development/github.com/felangel/flutter_and_friends/.dart_tool/flutter_build/ae2d368b5940aefb0c55ff62186de056/app.dill
[+5435 ms] executing: sysctl hw.optional.arm64
''',
stderr: '',
exitCode: 0,
);
expect(
result.findAppDill(),
equals(
'/Users/felix/Development/github.com/felangel/flutter_and_friends/.dart_tool/flutter_build/ae2d368b5940aefb0c55ff62186de056/app.dill',
),
);
});
group('when path to app.dill contains a space', () {
test('returns full path to app.dill, including the space(s)', () {
const result = ShorebirdProcessResult(
stdout: '''
[ +3 ms] targetingApplePlatform = true
[ ] extractAppleDebugSymbols = true
[ ] Will strip AOT snapshot manually after build and dSYM generation.
[ ] executing: /Users/bryanoltman/shorebirdtech/_shorebird/shorebird/bin/cache/flutter/9015e1b42a1ba41d97176e22b502b0e0e8ad28af/bin/cache/artifacts/engine/ios-release/gen_snapshot_arm64 --deterministic --snapshot_kind=app-aot-assembly --assembly=/Users/bryanoltman/Documents/sandbox/folder with space/ios_patcher/.dart_tool/flutter_build/cd4f4aa272817365910648606e3e4164/arm64/snapshot_assembly.S /Users/bryanoltman/Documents/sandbox/folder with space/ios_patcher/.dart_tool/flutter_build/cd4f4aa272817365910648606e3e4164/app.dill
[+3395 ms] executing: sysctl hw.optional.arm64
[ +3 ms] Exit code 0 from: sysctl hw.optional.arm64
''',
stderr: '',
exitCode: 0,
);
expect(
result.findAppDill(),
equals(
'/Users/bryanoltman/Documents/sandbox/folder with space/ios_patcher/.dart_tool/flutter_build/cd4f4aa272817365910648606e3e4164/app.dill',
),
);
});
});
});
group('when gen_snapshot is not invoked with app.dill', () {
test('returns null', () {
const result = ShorebirdProcessResult(
stdout: 'executing: .../gen_snapshot_arm64 .../snapshot_assembly.S',
stderr: '',
exitCode: 0,
);
expect(result.findAppDill(), isNull);
});
});
});
}