Fixes flutter build ipa failure: Command line name "app-store" is deprecated. Use "app-store-connect" (#150407)

Fixes https://github.com/flutter/flutter/issues/149369
This commit is contained in:
LouiseHsu 2024-06-27 09:42:21 +02:00 committed by GitHub
parent 805d79607e
commit 9a673175ab
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 203 additions and 7 deletions

View File

@ -16,6 +16,7 @@ import '../base/logger.dart';
import '../base/process.dart';
import '../base/terminal.dart';
import '../base/utils.dart';
import '../base/version.dart';
import '../build_info.dart';
import '../convert.dart';
import '../doctor_validator.dart';
@ -467,14 +468,14 @@ class BuildIOSArchiveCommand extends _BuildIOSSubCommand {
String? exportOptions = exportOptionsPlist;
String? exportMethod = exportOptions != null ?
globals.plistParser.getValueFromFile<String?>(exportOptions, 'method') : null;
exportMethod ??= stringArg('export-method')!;
final bool isAppStoreUpload = exportMethod == 'app-store';
exportMethod ??= _getVersionAppropriateExportMethod(stringArg('export-method')!);
final bool isAppStoreUpload = exportMethod == 'app-store' || exportMethod == 'app-store-connect';
File? generatedExportPlist;
try {
final String exportMethodDisplayName = isAppStoreUpload ? 'App Store' : exportMethod;
status = globals.logger.startProgress('Building $exportMethodDisplayName IPA...');
if (exportOptions == null) {
generatedExportPlist = _createExportPlist();
generatedExportPlist = _createExportPlist(exportMethod);
exportOptions = generatedExportPlist.path;
}
@ -555,7 +556,7 @@ class BuildIOSArchiveCommand extends _BuildIOSSubCommand {
return FlutterCommandResult.success();
}
File _createExportPlist() {
File _createExportPlist(String exportMethod) {
// Create the plist to be passed into xcodebuild -exportOptionsPlist.
final StringBuffer plistContents = StringBuffer('''
<?xml version="1.0" encoding="UTF-8"?>
@ -563,7 +564,7 @@ class BuildIOSArchiveCommand extends _BuildIOSSubCommand {
<plist version="1.0">
<dict>
<key>method</key>
<string>${stringArg('export-method')}</string>
<string>$exportMethod</string>
<key>uploadBitcode</key>
<false/>
</dict>
@ -576,6 +577,29 @@ class BuildIOSArchiveCommand extends _BuildIOSSubCommand {
return tempPlist;
}
// As of Xcode 15.4, the old export methods 'app-store', 'ad-hoc', and 'development'
// are now deprecated. The new equivalents are 'app-store-connect', 'release-testing',
// and 'debugging'.
String _getVersionAppropriateExportMethod(String method) {
final Version? currVersion = globals.xcode!.currentVersion;
if (currVersion != null) {
if (currVersion >= Version(15, 4, 0)) {
switch (method) {
case 'app-store':
return 'app-store-connect';
case 'ad-hoc':
return 'release-testing';
case 'development':
return 'debugging';
default:
throwToolExit('Encountered invalid export-method input.');
}
}
return method;
}
throwToolExit('Xcode version could not be found.');
}
}
abstract class _BuildIOSSubCommand extends BuildSubCommand {

View File

@ -11,6 +11,7 @@ import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/base/process.dart';
import 'package:flutter_tools/src/base/version.dart';
import 'package:flutter_tools/src/build_system/build_system.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/build.dart';
@ -30,10 +31,12 @@ import '../../src/test_build_system.dart';
import '../../src/test_flutter_command_runner.dart';
class FakeXcodeProjectInterpreterWithBuildSettings extends FakeXcodeProjectInterpreter {
FakeXcodeProjectInterpreterWithBuildSettings({ this.overrides = const <String, String>{} });
FakeXcodeProjectInterpreterWithBuildSettings({
this.overrides = const <String, String>{},
Version? version,
}) : version = version ?? Version(14, 0, 0);
final Map<String, String> overrides;
@override
Future<Map<String, String>> getBuildSettings(
String projectPath, {
@ -46,6 +49,9 @@ class FakeXcodeProjectInterpreterWithBuildSettings extends FakeXcodeProjectInter
'DEVELOPMENT_TEAM': 'abc',
};
}
@override
final Version version;
}
final Platform macosPlatform = FakePlatform(
@ -413,6 +419,172 @@ void main() {
XcodeProjectInterpreter: () => FakeXcodeProjectInterpreterWithBuildSettings(),
});
testUsingContext('ipa build uses new "debugging" export method when on Xcode versions > 15.3', () async {
final File cachedExportOptionsPlist = fileSystem.file('/CachedExportOptions.plist');
final BuildCommand command = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
logger: logger,
fileSystem: fileSystem,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(),
);
fakeProcessManager.addCommands(<FakeCommand>[
xattrCommand,
setUpFakeXcodeBuildHandler(),
exportArchiveCommand(exportOptionsPlist: _exportOptionsPlist, cachePlist: cachedExportOptionsPlist),
]);
createMinimalMockProjectFiles();
await createTestCommandRunner(command).run(
const <String>['build', 'ipa','--export-method', 'development', '--no-pub']
);
const String expectedIpaPlistContents = '''
<?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">
<plist version="1.0">
<dict>
<key>method</key>
<string>debugging</string>
<key>uploadBitcode</key>
<false/>
</dict>
</plist>
''';
final String actualIpaPlistContents = fileSystem.file(cachedExportOptionsPlist).readAsStringSync();
expect(actualIpaPlistContents, expectedIpaPlistContents);
expect(logger.statusText, contains('Building debugging IPA'));
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
Logger: () => logger,
ProcessManager: () => fakeProcessManager,
Platform: () => macosPlatform,
XcodeProjectInterpreter: () => FakeXcodeProjectInterpreterWithBuildSettings(version: Version(15, 4, null)),
});
testUsingContext('ipa build uses new "release-testing" export method when on Xcode versions > 15.3', () async {
final File cachedExportOptionsPlist = fileSystem.file('/CachedExportOptions.plist');
final BuildCommand command = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
logger: logger,
fileSystem: fileSystem,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(),
);
fakeProcessManager.addCommands(<FakeCommand>[
xattrCommand,
setUpFakeXcodeBuildHandler(),
exportArchiveCommand(exportOptionsPlist: _exportOptionsPlist, cachePlist: cachedExportOptionsPlist),
]);
createMinimalMockProjectFiles();
await createTestCommandRunner(command).run(
const <String>['build', 'ipa','--export-method', 'ad-hoc', '--no-pub']
);
const String expectedIpaPlistContents = '''
<?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">
<plist version="1.0">
<dict>
<key>method</key>
<string>release-testing</string>
<key>uploadBitcode</key>
<false/>
</dict>
</plist>
''';
final String actualIpaPlistContents = fileSystem.file(cachedExportOptionsPlist).readAsStringSync();
expect(actualIpaPlistContents, expectedIpaPlistContents);
expect(logger.statusText, contains('Building release-testing IPA'));
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
Logger: () => logger,
ProcessManager: () => fakeProcessManager,
Platform: () => macosPlatform,
XcodeProjectInterpreter: () => FakeXcodeProjectInterpreterWithBuildSettings(version: Version(15, 4, null)),
});
testUsingContext('ipa build uses new "app-store-connect" export method when on Xcode versions > 15.3', () async {
final File cachedExportOptionsPlist = fileSystem.file('/CachedExportOptions.plist');
final BuildCommand command = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
logger: logger,
fileSystem: fileSystem,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(),
);
fakeProcessManager.addCommands(<FakeCommand>[
xattrCommand,
setUpFakeXcodeBuildHandler(),
exportArchiveCommand(exportOptionsPlist: _exportOptionsPlist, cachePlist: cachedExportOptionsPlist),
]);
createMinimalMockProjectFiles();
await createTestCommandRunner(command).run(
const <String>['build', 'ipa','--export-method', 'app-store', '--no-pub']
);
const String expectedIpaPlistContents = '''
<?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">
<plist version="1.0">
<dict>
<key>method</key>
<string>app-store-connect</string>
<key>uploadBitcode</key>
<false/>
</dict>
</plist>
''';
final String actualIpaPlistContents = fileSystem.file(cachedExportOptionsPlist).readAsStringSync();
expect(actualIpaPlistContents, expectedIpaPlistContents);
expect(logger.statusText, contains('Building App Store IPA'));
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
Logger: () => logger,
ProcessManager: () => fakeProcessManager,
Platform: () => macosPlatform,
XcodeProjectInterpreter: () => FakeXcodeProjectInterpreterWithBuildSettings(version: Version(15, 4, null)),
});
testUsingContext('ipa build accepts legacy methods when on Xcode versions <= 15.3', () async {
final BuildCommand command = BuildCommand(
artifacts: artifacts,
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
logger: logger,
fileSystem: fileSystem,
processUtils: processUtils,
osUtils: FakeOperatingSystemUtils(),
);
fakeProcessManager.addCommands(<FakeCommand>[
xattrCommand,
setUpFakeXcodeBuildHandler(),
exportArchiveCommand(exportOptionsPlist: _exportOptionsPlist),
]);
createMinimalMockProjectFiles();
await createTestCommandRunner(command).run(
const <String>['build', 'ipa','--export-method', 'app-store', '--no-pub']
);
expect(logger.statusText, contains('Building App Store IPA'));
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
Logger: () => logger,
ProcessManager: () => fakeProcessManager,
Platform: () => macosPlatform,
XcodeProjectInterpreter: () => FakeXcodeProjectInterpreterWithBuildSettings(version: Version(15, 3, null)),
});
testUsingContext('ipa build reports method from --export-options-plist when used', () async {
final File exportOptions = fileSystem.file('/ExportOptions.plist')
..createSync();