Подтвердить что ты не робот

Архивировать и распространять приложение iOS в App Store через командную строку

Обычно при отправке приложения iOS в App Store я делаю Product → Archive from 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 при распространении в хранилище приложений? В любом случае, как мне выполнить подписание с правильным профилем и распространять через командную строку?

4b9b3361

Ответ 1

См. обновление для Xcode 8 в нижней части ответа.

Сначала ответить на последнюю часть вопроса. Да, для представления вашего приложения через iTunes connect необходим профиль Provisioning App Store. Он не будет проходить шаги предварительной проверки, если у него нет правильного профиля подготовки. Вам нужно будет создать профиль распространения App Store в Центре-члене

Add iOS Provisioning Profile Screenshot

Выберите "Магазин приложений" и нажмите "Продолжить"

Первая часть вопроса немного сложнее, так как создание, подписание и распространение архивов и файлов IPA с использованием инструментов командной строки плохо документировано. Реализация скриптового решения полна подводных камней, поскольку в некоторых случаях инструменты не ведут себя так, как ожидалось, и требуется более подробное знание взаимосвязи между вашей учетной записью разработчика, вашей связью ключей, сертификатами подписи и профилями подготовки.

Ниже приведен пример script, который может быть использован для создания архива со встроенным профилем профилей Ad Hoc, создания IPA для распределения Ad Hoc. В качестве бонуса создается zip файл DSYMs для загрузки в TestFlight. Затем представлены еще два сценария. Первая создаст версию App Store для IPA из существующего xcarchive, вторая покажет, как изменить xcarchive, чтобы ее можно было отменить третьей стороной для распространения Enterprise In House.

В этой автоматизированной сборке script предполагается, что профили Provisioning Profiles доступны в каталоге 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"

Чтобы перераспределить xcarchive с профилем распространения в App Store, повторно экспортируйте 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"

Если script запускается с момента запуска daemon, см. этот ответ fooobar.com/info/56827/..., чтобы решить проблему с доступом к цепочке ключей входа в систему от демона launchd.

ОБНОВЛЕНИЕ для OSX Mavericks и Yosemite

В OSX Mavericks (v10.9.5) и OSX Yosemite вы можете увидеть ошибки подписи кода:

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

Проверьте эту публикацию здесь по причине xcodebuild - codesign -vvvv говорит, что "огибающий ресурс устарел"

Чтобы реализовать изменение, предложенное 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.

  • Возможно, если у вас есть одна машина CI для автоматической работы с кодом. Я обнаружил, что вам нужно создать и назначить учетную запись разработчика экземпляру Xcode на машине CI. Это был ручной шаг, и я не нашел способа импортировать профиль разработчика из командной строки.

  • Если вы используете распределенную среду CI с несколькими машинами сборки, это просто не работает. Сначала у вас есть проблема выше, вам нужно вручную добавить учетную запись разработчика ко всем экземплярам Xcode, а во-вторых, каждая из этих учетных записей должна отличаться от Apple ID, иначе вы получите проблемы с созданием сертификатов для общей учетной записи сборки (все машины разделяют учетную запись, которая вызывает столкновение в сертификате разработчика, поскольку она привязана к определенной машине).

Мы запускаем распределенную среду 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, связанным с учетной записью сборки (поэтому убедитесь, что он установлен в цепочке ключей). Теперь архив можно экспортировать в формат IPA для Ad-hoc, Enterprise и App Store, используя опцию -exportOptionsPlist для xcodebuild.

Создайте файл 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.

Теперь измените команду архивного архива, чтобы использовать файл plist новых файлов экспорта

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