存档&通过命令行将iOS应用程序分发到App Store

时间:2014-02-18 17:09:06

标签: ios xcode app-store

通常在向App Store提交iOS应用时,我会做产品 - >从Xcode归档,然后选择分发到App Store。我可以使用:

成功存档构建
xcodebuild -scheme "myScheme" archive -archivePath /my/path/myArchive

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

对于临时版本,我使用以下代码生成我的ipa:

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

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

1 个答案:

答案 0 :(得分:16)

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

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

Add iOS Provisioning Profile Screenshot

选择" App Store"然后单击继续

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

以下是一个脚本示例,可用于创建具有嵌入式Ad Hoc配置文件的存档,为Ad Hoc分发创建IPA。作为奖励,创建DSYMs zip文件以上传到TestFlight。然后再呈现两个脚本。第一个将从现有的xcarchive创建一个AppA版本的IPA,第二个将展示如何修改xcarchive,以便第三方可以为Enterprise In House发行版重新签名。

此自动构建脚本假定供应配置文件在使用源代码签入的名为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"

要使用App Store Distribution配置文件重新分发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"

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

# 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"

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

OSX Mavericks和Yosemite的更新

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

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

点击此处查看原因xcodebuild - codesign -vvvv says"resource envelope is obsolete"

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

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

Xcode8的更新

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

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

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

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

  3. 我们运行分布式Jenkins CI环境,因此我们坚持手动签名,但导出IPA的方法发生了变化,现在必须使用 -exportOptionsPlist 选项。

    更改存档命令:

    #
    # 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 
    

    存档使用与构建帐户关联的iOS开发人员证书进行签名(因此请确保在密钥链中安装了一个)。现在,可以使用xcodebuild的 -exportOptionsPlist 选项将存档导出为Ad-hoc,Enterprise和App Store的IPA格式。

    使用以下内容创建名为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>
    

    请参阅 xcodebuild -help 输出,以获取 -exportOptionsPlist 选项可用的完整键列表。

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

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