fix(shorebird_cli): look in build directory for ipa instead of finding name based on plist (#999)

This commit is contained in:
Bryan Oltman
2023-08-02 14:50:37 -04:00
committed by GitHub
parent ce5362127b
commit 32d98935f7
5 changed files with 195 additions and 32 deletions
@@ -3,7 +3,6 @@ import 'dart:io' hide Platform;
import 'package:crypto/crypto.dart';
import 'package:mason_logger/mason_logger.dart';
import 'package:path/path.dart' as p;
import 'package:platform/platform.dart';
import 'package:shorebird_cli/src/archive_analysis/archive_analysis.dart';
import 'package:shorebird_cli/src/code_push_client_wrapper.dart';
@@ -121,16 +120,15 @@ class PatchIosCommand extends ShorebirdCommand
final detectReleaseVersionProgress = logger.progress(
'Detecting release version',
);
final String ipaPath;
try {
final ipa = _ipaReader.read(
p.join(
Directory.current.path,
'build',
'ios',
'ipa',
'${getIpaName()}.ipa',
),
);
ipaPath = getIpaPath();
} catch (error) {
detectReleaseVersionProgress.fail('Could not find ipa file: $error');
return ExitCode.software.code;
}
try {
final ipa = _ipaReader.read(ipaPath);
releaseVersion = ipa.versionNumber;
detectReleaseVersionProgress.complete(
'Detected release version $releaseVersion',
@@ -95,11 +95,14 @@ make smaller updates to your app.
final releaseVersionProgress = logger.progress('Getting release version');
final iosBuildDir = p.join(Directory.current.path, 'build', 'ios');
final ipaPath = p.join(
iosBuildDir,
'ipa',
'${getIpaName()}.ipa',
);
final String ipaPath;
try {
ipaPath = getIpaPath();
} catch (error) {
releaseVersionProgress.fail('Could not find ipa file: $error');
return ExitCode.software.code;
}
final runnerPath = p.join(
iosBuildDir,
'archive',
@@ -1,7 +1,6 @@
import 'dart:io';
import 'package:path/path.dart' as p;
import 'package:propertylistserialization/propertylistserialization.dart';
import 'package:shorebird_cli/src/command.dart';
import 'package:shorebird_cli/src/logger.dart';
@@ -62,21 +61,39 @@ mixin ShorebirdArtifactMixin on ShorebirdCommand {
return extractedZipDir;
}
/// Finds CFBundleName in the app's Info.plist. The ipa generated by `flutter
/// build ipa` will have this name and an .ipa extension.
String getIpaName() {
final infoPlist = PropertyListSerialization.propertyListWithString(
File(
p.join(
Directory.current.path,
'ios',
'Runner',
'Info.plist',
),
).readAsStringSync(),
) as Map<String, Object>;
/// Returns the path to the .ipa file generated by `flutter build ipa`. Throws
/// an exception if there is not exactly one .ipa file in the build directory,
/// or if there is no build directory.
String getIpaPath() {
final ipaBuildDirectory = Directory(
p.join(
Directory.current.path,
'build',
'ios',
'ipa',
),
);
return infoPlist['CFBundleName']! as String;
if (!ipaBuildDirectory.existsSync()) {
throw Exception('No directory found at ${ipaBuildDirectory.path}');
}
final ipaFiles = ipaBuildDirectory
.listSync(recursive: true)
.whereType<File>()
.where((f) => p.extension(f.path) == '.ipa');
if (ipaFiles.isEmpty) {
throw Exception('No .ipa files found in ${ipaBuildDirectory.path}');
}
if (ipaFiles.length > 1) {
throw Exception(
'More than one .ipa file found in ${ipaBuildDirectory.path}',
);
}
return ipaFiles.single.path;
}
/// Finds the most recently-edited app.dill file in the .dart_tool directory.
@@ -67,6 +67,7 @@ void main() {
const appDisplayName = 'Test App';
const platformName = 'ios';
const elfAotSnapshotFileName = 'out.aot';
const ipaPath = 'build/ios/ipa/Runner.ipa';
const infoPlistContent = '''
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
@@ -143,6 +144,7 @@ flutter:
)
..createSync(recursive: true)
..writeAsStringSync(infoPlistContent);
File(p.join(tempDir.path, ipaPath)).createSync(recursive: true);
return tempDir;
}
@@ -316,6 +318,78 @@ flutter:
expect(exitCode, equals(ExitCode.software.code));
});
test('exits with code 70 if build directory does not exist', () async {
final tempDir = setUpTempDir();
setUpTempArtifacts(tempDir);
Directory(p.join(tempDir.path, 'build')).deleteSync(recursive: true);
final exitCode = await IOOverrides.runZoned(
() => runWithOverrides(command.run),
getCurrentDirectory: () => tempDir,
);
expect(exitCode, equals(ExitCode.software.code));
verify(
() => progress.fail(
any(
that: stringContainsInOrder([
'Could not find ipa file',
'No directory found at ${p.join(tempDir.path, 'build')}',
]),
),
),
).called(1);
});
test('exits with code 70 if ipa file does not exist', () async {
final tempDir = setUpTempDir();
setUpTempArtifacts(tempDir);
File(p.join(tempDir.path, ipaPath)).deleteSync(recursive: true);
final exitCode = await IOOverrides.runZoned(
() => runWithOverrides(command.run),
getCurrentDirectory: () => tempDir,
);
expect(exitCode, equals(ExitCode.software.code));
verify(
() => progress.fail(
any(
that: stringContainsInOrder([
'Could not find ipa file',
'No .ipa files found in',
'build/ios/ipa',
]),
),
),
).called(1);
});
test('exits with code 70 if more than one ipa file is found', () async {
final tempDir = setUpTempDir();
setUpTempArtifacts(tempDir);
File(p.join(tempDir.path, 'build/ios/ipa/Runner2.ipa'))
.createSync(recursive: true);
final exitCode = await IOOverrides.runZoned(
() => runWithOverrides(command.run),
getCurrentDirectory: () => tempDir,
);
expect(exitCode, equals(ExitCode.software.code));
verify(
() => progress.fail(
any(
that: stringContainsInOrder([
'Could not find ipa file',
'More than one .ipa file found in',
'build/ios/ipa',
]),
),
),
).called(1);
});
test(
'exits with usage code when '
'both --dry-run and --force are specified', () async {
@@ -62,6 +62,7 @@ void main() {
const appDisplayName = 'Test App';
const arch = 'armv7';
const releasePlatform = ReleasePlatform.ios;
const ipaPath = 'build/ios/ipa/Runner.ipa';
const appMetadata = AppMetadata(appId: appId, displayName: appDisplayName);
const release = Release(
id: 0,
@@ -135,6 +136,7 @@ flutter:
)
..createSync(recursive: true)
..writeAsStringSync(infoPlistContent);
File(p.join(tempDir.path, ipaPath)).createSync(recursive: true);
return tempDir;
}
@@ -383,6 +385,75 @@ error: exportArchive: No signing certificate "iOS Distribution" found
);
});
test('exits with code 70 if build directory does not exist', () async {
final tempDir = setUpTempDir();
Directory(p.join(tempDir.path, 'build')).deleteSync(recursive: true);
final exitCode = await IOOverrides.runZoned(
() => runWithOverrides(command.run),
getCurrentDirectory: () => tempDir,
);
expect(exitCode, equals(ExitCode.software.code));
verify(
() => progress.fail(
any(
that: stringContainsInOrder([
'Could not find ipa file',
'No directory found at ${p.join(tempDir.path, 'build')}',
]),
),
),
).called(1);
});
test('exits with code 70 if ipa file does not exist', () async {
final tempDir = setUpTempDir();
File(p.join(tempDir.path, ipaPath)).deleteSync(recursive: true);
final exitCode = await IOOverrides.runZoned(
() => runWithOverrides(command.run),
getCurrentDirectory: () => tempDir,
);
expect(exitCode, equals(ExitCode.software.code));
verify(
() => progress.fail(
any(
that: stringContainsInOrder([
'Could not find ipa file',
'No .ipa files found in',
'build/ios/ipa',
]),
),
),
).called(1);
});
test('exits with code 70 if more than one ipa file is found', () async {
final tempDir = setUpTempDir();
File(p.join(tempDir.path, 'build/ios/ipa/Runner2.ipa'))
.createSync(recursive: true);
final exitCode = await IOOverrides.runZoned(
() => runWithOverrides(command.run),
getCurrentDirectory: () => tempDir,
);
expect(exitCode, equals(ExitCode.software.code));
verify(
() => progress.fail(
any(
that: stringContainsInOrder([
'Could not find ipa file',
'More than one .ipa file found in',
'build/ios/ipa',
]),
),
),
).called(1);
});
test('throws error when unable to detect flutter revision', () async {
const error = 'oops';
when(() => flutterRevisionProcessResult.exitCode).thenReturn(1);
@@ -443,7 +514,7 @@ error: exportArchive: No signing certificate "iOS Distribution" found
that: stringContainsInOrder(
[
'Your next step is to upload the ipa to App Store Connect.',
'build/ios/ipa/app_bundle_name.ipa',
'build/ios/ipa/Runner.ipa',
],
),
),
@@ -495,7 +566,7 @@ flavors:
that: stringContainsInOrder(
[
'Your next step is to upload the ipa to App Store Connect.',
'build/ios/ipa/app_bundle_name.ipa',
'build/ios/ipa/Runner.ipa',
],
),
),