fix(shorebird_cli): look in build directory for ipa instead of finding name based on plist (#999)
This commit is contained in:
@@ -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',
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user