xcode 通过命令行将 iOS 应用存档并分发到 App Store

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21860632/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 04:40:32  来源:igfitidea点击:

Archive & Distribute iOS app to App Store via command line

iosxcodeapp-store

提问by Adam Johns

Normally when submitting an iOS app to the App Store I do Product -> Archive from Xcode and then choose distribute to the App Store. I can successfully archive a build with:

通常在向 App Store 提交 iOS 应用程序时,我会执行 Product -> Archive from Xcode,然后选择分发到 App Store。我可以通过以下方式成功归档构建:

xcodebuild -scheme "myScheme" archive -archivePath /my/path/myArchive

but how do I do the signing process with the correct provisioning profile and also distribute via command line?

但是如何使用正确的配置文件进行签名过程并通过命令行进行分发?

For ad hoc builds, I generate my ipa after archiving with:

对于临时构建,我在归档后生成我的 ipa:

xcodebuild -exportArchive -exportFormat IPA -archivePath myArchive.xcarchive -exportPath /my/path/myFile.ipa -exportProvisioningProfile 'my adhoc profile name'

But do I even need to generate an ipa when distributing to the app store? Either way, how do I do the signing with correct profile and distributing via command line?

但是我什至需要在分发到应用商店时生成 ipa 吗?无论哪种方式,我如何使用正确的配置文件进行签名并通过命令行进行分发?

回答by BitByteDog

See update for Xcode 8 at bottom of answer.

请参阅答案底部的 Xcode 8 更新。

To answer the last part of the question first - Yes an App Store Provisioning Profile is needed to submit your app through iTunes connect. It will not pass the preverification steps unless it has a correct provisioning profile. You will need to create an App Store distribution profile in the Member Centre

首先回答问题的最后一部分 - 是的,需要 App Store Provisioning Profile 才能通过 iTunes Connect 提交您的应用程序。除非它具有正确的配置文件,否则它不会通过预验证步骤。您需要在会员中心创建一个 App Store 分发配置文件

Add iOS Provisioning Profile Screenshot

添加 iOS 配置文件截图

Select "App Store" and click on continue

选择“App Store”并点击继续

The first part of the question is a little more difficult, as creating, signing and distributing archives and IPA files using command line tools is poorly documented. Implementing a scripted solution is full of pitfalls because tools don't behave as expected under some circumstances and a more detailed knowledge of the relationship between your developer account, your keychain, the signing certs and the provisioning profiles is required.

问题的第一部分有点困难,因为使用命令行工具创建、签名和分发档案和 IPA 文件的记录很少。实施脚本化解决方案充满了陷阱,因为工具在某些情况下不会按预期运行,并且需要更详细地了解开发人员帐户、钥匙串、签名证书和配置文件之间的关系。

Here is a sample of a script that can be used to create an archive with an embedded Ad Hoc provisioning profile, create an IPA for Ad Hoc distribution. As a bonus the DSYMs zip file is created for upload to TestFlight. Then two more scripts are presented. The first will create an App Store version of the IPA from the existing xcarchive, the second will show how to modify an xcarchive so it can be resigned by a third party for Enterprise In House distribution.

这是一个脚本示例,可用于创建带有嵌入式 Ad Hoc 配置文件的存档,为 Ad Hoc 分发创建 IPA。作为奖励,创建了 DSYMs zip 文件以上传到 TestFlight。然后呈现另外两个脚本。第一个将从现有的 xcarchive 创建 IPA 的 App Store 版本,第二个将展示如何修改 xcarchive,以便第三方可以辞职以进行 Enterprise In House 分发。

This automated build script assumes that the Provisioning Profiles are available in a directory called ProvisioningProfiles checked in with the source code. It is also assumes the password to unlock the keychain holding the signing cert is stored in a protected file in the build users home directory.

此自动构建脚本假定供应配置文件位于随源代码检入的名为 ProvisioningProfiles 的目录中。还假设解锁持有签名证书的钥匙串的密码存储在构建用户主目录中的受保护文件中。

#!/bin/sh

# SETME
# set to name of signing certification usually starts something like "iPhone Distribution: ...."
# (the associated private key must be available in the key store)
#
# use the command "security find-identity" to list all the possible values available
#
codeSignIdentity="iPhone Distribution"

# SETME
# set to location of Ad Hoc provisioning profile
# (this profile must have the codeSignIdentity specified above included in it)
#
provisioningProfile=ProvisioningProfiles/MyAppAdHocDistribution.mobileprovision

# The keychain needs to be unlocked for signing, which requires the keychain
# password. This is stored in a file in the build account only accessible to
# the build account user
if [ ! -f $HOME/.pass ] ; then
    echo "no keychain password file available"
    exit 1
fi

case `stat -L -f "%p" $HOME/.pass`
in
    *400) ;;
    *)
        echo "keychain password file permissions are not restrictive enough"
        echo "chmod 400 $HOME/.pass"
        exit 1
        ;;
esac

#
# turn off tracing if it is on for security command
# to prevent logging of password
#
case `set -o | grep xtrace`
in
    *on) xon=yes ;;
    *) xon=no ;;
esac

#
# unlock the keychain, automatically lock keychain on script exit
#
[ $xon == yes ] && set +x
security unlock-keychain -p `cat $HOME/.pass` $HOME/Library/Keychains/login.keychain
[ $xon == yes ] && set -x
trap "security lock-keychain $HOME/Library/Keychains/login.keychain" EXIT

#
# Extract the profile UUID from the checked in Provisioning Profile.
#
uuid=`/usr/libexec/plistbuddy -c Print:UUID /dev/stdin <<< \
        \`security cms -D -i $provisioningProfile\``

#
# Copy the profile to the location XCode expects to find it and start the build,
# specifying which profile and signing identity to use for the archived app
#
cp -f $provisioningProfile \
        "$HOME/Library/MobileDevice/Provisioning Profiles/$uuid.mobileprovision"

#
# Build the xcarchive - this will only be done once, will will then
# distribute it for Ad Hoc, App Store and Enterprise In House scenarios
# (profile must be specified by UUID for this step)
#
xcodebuild \
    -workspace MyApp.xcworkspace \
    -scheme MyApp \
    -archivePath build/MyApp.xcarchive \
    archive \
    PROVISIONING_PROFILE="$uuid" \
    CODE_SIGN_IDENTITY="$codeSignIdentity"

#
# Create a zip of the DSYMs for TestFlight
#
/usr/bin/zip -r MyApp.dSYM.zip build/MyApp.xcarchive/dSYMs/MyApp.app.dSYM

#
# now distribute the xcarchive using an Ad Hoc profile
# (for QA testing for example)
#
profileName=`/usr/libexec/plistbuddy -c Print:Name /dev/stdin <<< \
        \`security cms -D -i $provisioningProfile\``

#
# The profile must be specified by name for this step
#
xcodebuild \
        -exportArchive \
        -exportFormat IPA \
        -archivePath build/MyApp.xcarchive \
        -exportPath MyAppForAdHoc.ipa \
        -exportProvisioningProfile "$profileName"

To redistribute the xcarchive with the App Store Distribution profile, re-export the xcarchive with a new profile (the signing identity is the same for both the Ad Hoc and the App Store profiles).

要使用 App Store 分发配置文件重新分发 xcarchive,请使用新配置文件重新导出 xcarchive(Ad Hoc 和 App Store 配置文件的签名标识相同)。

# SETME
# set to location of App Store provisioning profile
#
appStoreProvisioningProfile=ProvisioningProfiles/MyAppAppStoreDistribution.mobileprovision

#
# Extract the App Store profile UUID from the checked in Provisioning Profile.
#
uuid=`/usr/libexec/plistbuddy -c Print:UUID /dev/stdin <<< \
        \`security cms -D -i $appStoreProvisioningProfile\``

#
# Copy the profile to the location XCode expects to find it and start the export,
# specifying which profile to use for the archived app
# (Profile must match with signing identity used to create xcarchive)
#
cp -f $appStoreProvisioningProfile \
        "$HOME/Library/MobileDevice/Provisioning Profiles/$uuid.mobileprovision"

#
# Extract the enterprise profile name from the checked in App Store Provisioning Profile.
# and redistribute the xcarchive as an App Store ready IPA
#
profileName=`/usr/libexec/plistbuddy -c Print:Name /dev/stdin <<< \
        \`security cms -D -i $appStoreProvisioningProfile\``

#
# Profile must be specified by name for this step
#
xcodebuild \
    -exportArchive \
    -exportFormat IPA \
    -archivePath build/MyApp.xcarchive \
    -exportPath MyAppForStore.ipa \
    -exportProvisioningProfile "$profileName"

Finally just to be complete, what if you want to resign the xcarchive with a new identity and provisioning profile? This might happen if you distribute xcarchives for in-house distribution to third party companies. The recipient needs to sign your xcarchive for distribution using their enterprise certificate. xcodebuild cannot be coerced into overwriting the existing code signature in the xcarchive, therefore codesign must be used directly.

最后只是为了完成,如果您想使用新的身份和配置文件退出 xcarchive 怎么办?如果您将用于内部分发的 xcarchive 分发给第三方公司,则可能会发生这种情况。收件人需要使用他们的企业证书签署您的 xcarchive 以进行分发。xcodebuild 不能强制覆盖 xcarchive 中现有的代码签名,因此必须直接使用 codesign。

# SETME
# set to name of enterprise signing certification usually starts something like
# "iPhone Distribution: ...."
#
# use the command "security find-identity" to list all the possible values available
#
enterpriseCodeSignIdentity="iPhone Distribution: Acme Ltd"

# SETME
# set to location of Enterprise In-House provisioning profile
# (this profile must be associated with the enterprise code signing identity)
#
enterpriseProvisioningProfile=ProvisioningProfiles/MyAppInHouseDistribution.mobileprovision

# SETME
# A resigning of the app with a different certificate requires a new bundle ID
# that is registered by the Enterprise and is included in the In-House distribution
# profile (This could be automatically extracted from the Enterprise In-House distribution
# profile, I leave that as an ETTR)
enterpriseBundleId="com.enterprise.myapp"

#
# Extract the enterprise profile UUID from the checked in Provisioning Profile.
#
euuid=`/usr/libexec/plistbuddy -c Print:UUID /dev/stdin <<< \
        \`security cms -D -i $enterpriseProvisioningProfile\``

#
# Copy the profile to the location XCode expects to find it and start the build,
# specifying which profile and signing identity to use for the archived app
#
cp -f $enterpriseProvisioningProfile \
        "$HOME/Library/MobileDevice/Provisioning Profiles/$euuid.mobileprovision"

#
# Copy, modify and resign the xcarchive ready for Enterprise deployment
# (has to be resigned as the production certificate is different for enterprise)
#
cp -Rp build/MyApp.xcarchive build/MyAppEnterprise.xcarchive

#
# Remove old code signature
#
rm -rf build/MyAppEnterprise.xcarchive/Products/Applications/MyApp.app/_CodeSignature

#
# copy in the enterprise provisioning profile
#
cp $enterpriseProvisioningProfile \
        build/MyAppEnterprise.xcarchive/Products/Applications/MyApp.app/embedded.mobileprovision

#
# Modify the bundle id to that of the enterprise bundle id
#   
/usr/libexec/plistbuddy -c "Set:CFBundleIdentifier $enterpriseBundleId" \
        build/MyAppEnterprise.xcarchive/Products/Applications/MyApp.app/Info.plist

#
# resign the xcarchive with the enterprise code signing identity
#
/usr/bin/codesign -f -v -s $enterpriseCodeSignIdentity \
        build/MyAppEnterprise.xcarchive/Products/Applications/MyApp.app 

#
# Update the DSYM bundle id and create a zip of the DSYMs for TestFlight (if applicable)
#
/usr/libexec/plistbuddy -c "Set:CFBundleIdentifier com.apple.xcode.dsym.${enterpriseBundleId}" \
        build/MyAppEnterprise.xcarchive/dSYMs/MyApp.app.dSYM/Contents/Info.plist
/usr/bin/zip -r MyAppEnterprise.dSYM.zip build/MyAppEnterprise.xcarchive/dSYMs/MyApp.app.dSYM

#
# Extract the enterprise profile Name from the checked in Provisioning Profile.
#
enterpriseProfileName=`/usr/libexec/plistbuddy -c Print:Name /dev/stdin <<< \
        l\`security cms -D -i $enterpriseProvisioningProfile\``

#
# Profile must be specified by name for this step
#
xcodebuild \
    -exportArchive \
    -exportFormat IPA \
    -archivePath build/MyAppEnterprise.xcarchive \
    -exportPath MyAppEnterprise.ipa \
    -exportProvisioningProfile "$enterpriseProfileName"

If the script is being run from as a launchd daemon, see this answer https://stackoverflow.com/a/9482707/2351246to solve the problem with accessing the login keychain from a launchd daemon.

如果脚本作为 launchd 守护程序运行,请参阅此答案https://stackoverflow.com/a/9482707/2351246以解决从 launchd 守护程序访问登录钥匙串的问题。

UPDATE for OSX Mavericks and Yosemite

OSX Mavericks 和 Yosemite 的更新

On OSX Mavericks (v10.9.5) and OSX Yosemite you may see code signing errors:

在 OSX Mavericks (v10.9.5) 和 OSX Yosemite 上,您可能会看到代码签名错误:

Codesign check fails : ...../MyApp.app: resource envelope is obsolete

Check this posting here for the cause xcodebuild - codesign -vvvv says"resource envelope is obsolete"

在此处查看此帖子以了解原因xcodebuild - codesign -vvvv 说“资源信封已过时”

To implement the change suggested by Apple Support in the referenced post, run the following command:

要实施 Apple 支持在引用的帖子中建议的更改,请运行以下命令:

 sudo perl -pi.bak -e 's/--verify"./--verify", "--no-strict",/ if /codesign.*origApp/;' `xcrun -sdk iphoneos -f PackageApplication`

UPDATE for Xcode8

Xcode8 更新

In Xcode8, the procedure described in my previous answer no longer works with the new Automatically manage signingfeature, so you will need to select manual signing to use this method.

在 Xcode8 中,我之前的回答中描述的过程不再适用于新的自动管理签名功能,因此您需要选择手动签名才能使用此方法。

If you wish to use automatic signing, here are some observations based on our attempts to get it working with both a IBM Jazz and a Jenkins a CI environment.

如果您希望使用自动签名,以下是一些基于我们尝试使其与 IBM Jazz 和 Jenkins 和 CI 环境一起工作的观察结果。

  1. It is possible if you have one CI machine to get auto code signing working. I found you had to create and assign a developer account to the instance of Xcode on the CI machine. This was a manual step and I found no way to import a developer profile from the commandline.

  2. If you use a distributed CI environment with multiple build machines, it just doesn't work well. First you have the issue above, you have to manually add a developer account to all instances of Xcode, and second, each of those accounts has to be a different Apple ID, otherwise you get certificate generation issues for the common build account (All machines are sharing an account which causes a collision in the developer certificate because it is tied to a specific machine).

  1. 如果您有一台 CI 机器可以使自动代码签名工作,则是可能的。我发现您必须为 CI 机器上的 Xcode 实例创建并分配一个开发者帐户。这是一个手动步骤,我发现无法从命令行导入开发人员配置文件。

  2. 如果您使用具有多个构建机器的分布式 CI 环境,它就不能很好地工作。首先,您遇到上述问题,您必须手动将开发者帐户添加到 Xcode 的所有实例,其次,这些帐户中的每一个都必须是不同的 Apple ID,否则您会遇到公共构建帐户的证书生成问题(所有机器正在共享一个帐户,这会导致开发人员证书发生冲突,因为它与特定机器相关联)。

We run a distributed Jenkins CI environment, so we stuck with manual signing, but the method of exporting IPA changed, the -exportOptionsPlistoption must be used now.

我们运行分布式Jenkins CI环境,所以我们坚持手动签名,但是导出IPA的方法改变了,现在必须使用-exportOptionsPlist选项。

Change the archiving command:

更改归档命令:

#
# Build the xcarchive - this will only be done once, will will then
# distribute it for Ad Hoc, App Store and Enterprise In House scenarios
#
xcodebuild \
    -workspace MyApp.xcworkspace \
    -scheme MyApp \
    -archivePath build/MyApp.xcarchive \
    archive 

The archive is signed with the iOS Developer certificate associated with the build account (so make sure it has one installed in the keychain). Now the archive can be exported to IPA format for Ad-hoc, Enterprise and App Store using the -exportOptionsPlistoption to xcodebuild.

存档使用与构建帐户关联的 iOS 开发人员证书签名(因此请确保它已安装在钥匙串中)。现在,可以使用xcodebuild的-exportOptionsPlist选项将存档导出为 Ad-hoc、企业和 App Store 的 IPA 格式。

Create a file called exportAppStore.plist with the following contents and save it in your top level project directory.

使用以下内容创建一个名为 exportAppStore.plist 的文件,并将其保存在您的顶级项目目录中。

<?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</string>
</dict>
</plist>

See the output xcodebuild -helpfor a complete list of keys available for the -exportOptionsPlistoption.

有关-exportOptionsPlist选项可用的键的完整列表,请参阅输出xcodebuild -help

Now modify the export archive command to use the new export options plist file

现在修改 export archive 命令以使用新的导出选项 plist 文件

xcodebuild \
    -exportArchive \
    -archivePath build/MyApp.xcarchive \
    -exportOptionsPlist exportAppStore.plist \
    -exportPath MyAppForStore.ipa