ΠžΡΠΎΠ±Π΅Π½Π½ΠΎΡΡ‚ΠΈ сборки ΠΈ доставки iOS-ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠΉ

Π’ этой ΡΡ‚Π°Ρ‚ΡŒΠ΅ ΠΌΡ‹ дСлимся ΠΎΠΏΡ‹Ρ‚ΠΎΠΌ сборки ΠΈ доставки ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡΠΌ ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠΉ для iOS, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ накопился Ρƒ студии Plarium Krasnodar Π² процСссС ΠΎΡ‚Π»Π°Π΄ΠΊΠΈ CI/CD.

ΠžΡΠΎΠ±Π΅Π½Π½ΠΎΡΡ‚ΠΈ сборки ΠΈ доставки iOS-ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠΉ

ΠŸΠΎΠ΄Π³ΠΎΡ‚ΠΎΠ²ΠΊΠ°

ΠšΠ°ΠΆΠ΄Ρ‹ΠΉ Ρ‡Π΅Π»ΠΎΠ²Π΅ΠΊ, Ρ‚Π°ΠΊ ΠΈΠ»ΠΈ ΠΈΠ½Π°Ρ‡Π΅ связанный с Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΠΎΠΉ ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠΉ для устройств Apple, ΡƒΠΆΠ΅ успСл ΠΎΡ†Π΅Π½ΠΈΡ‚ΡŒ спорноС удобство инфраструктуры. БлоТности Π²ΡΡ‚Ρ€Π΅Ρ‡Π°ΡŽΡ‚ΡΡ ΠΏΠΎΠ²ΡΡŽΠ΄Ρƒ: начиная с мСню профиля Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠ° ΠΈ заканчивая инструмСнтами ΠΎΡ‚Π»Π°Π΄ΠΊΠΈ ΠΈ сборки.

Π‘Ρ‚Π°Ρ‚Π΅ΠΉ ΠΎΠ± Β«Π°Π·Π°Ρ…Β» прСдостаточно Π² сСти, поэтому постараСмся Π²Ρ‹Π΄Π΅Π»ΠΈΡ‚ΡŒ Π³Π»Π°Π²Π½ΠΎΠ΅. Π’ΠΎΡ‚ Ρ‡Ρ‚ΠΎ Π½ΡƒΠΆΠ½ΠΎ для ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎΠΉ сборки прилоТСния:

  • Π°ΠΊΠΊΠ°ΡƒΠ½Ρ‚ Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠ°;
  • устройство Π½Π° Π±Π°Π·Π΅ macOS, Π²Ρ‹ΡΡ‚ΡƒΠΏΠ°ΡŽΡ‰Π΅Π΅ Π² Ρ€ΠΎΠ»ΠΈ Π±ΠΈΠ»Π΄-сСрвСра;
  • сгСнСрированный сСртификат Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠ°, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Π±ΡƒΠ΄Π΅Ρ‚ Π΄Π°Π»Π΅Π΅ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒΡΡ для подписи прилоТСния;
  • созданноС ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ с ΡƒΠ½ΠΈΠΊΠ°Π»ΡŒΠ½Ρ‹ΠΌ ID (слСдуСт ΠΎΡ‚ΠΌΠ΅Ρ‚ΠΈΡ‚ΡŒ Π²Π°ΠΆΠ½ΠΎΡΡ‚ΡŒ Bundle Identifier, ΠΏΠΎΡ‚ΠΎΠΌΡƒ Ρ‡Ρ‚ΠΎ ΠΏΡ€ΠΈΠΌΠ΅Π½Π΅Π½ΠΈΠ΅ wildcard ID Π΄Π΅Π»Π°Π΅Ρ‚ Π½Π΅Π²ΠΎΠ·ΠΌΠΎΠΆΠ½Ρ‹ΠΌ использованиС ΠΌΠ½ΠΎΠ³ΠΈΡ… Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΉ прилоТСния, Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€: Associated Domains, Push Notifications, Apple Sign In ΠΈ ΠΏΡ€ΠΎΡ‡ΠΈΡ…);
  • ΠΏΡ€ΠΎΡ„ΠΈΠ»ΡŒ подписи прилоТСния.

Π‘Π΅Ρ€Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠ° слСдуСт ΡΠ³Π΅Π½Π΅Ρ€ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Ρ‡Π΅Ρ€Π΅Π· Keychain Π½Π° любом устройствС Π½Π° Π±Π°Π·Π΅ macOS. ΠžΡ‡Π΅Π½ΡŒ Π²Π°ΠΆΠ½Ρ‹ΠΌ являСтся Ρ‚ΠΈΠΏ сСртификата. Π’ зависимости ΠΎΡ‚ срСды прилоТСния (Dev, QA, Staging, Production) ΠΎΠ½ Π±ΡƒΠ΄Π΅Ρ‚ Ρ€Π°Π·Π»ΠΈΡ‡Π°Ρ‚ΡŒΡΡ (Development ΠΈΠ»ΠΈ Distribution), Ρ‚Π°ΠΊ ΠΆΠ΅ ΠΊΠ°ΠΊ ΠΈ Ρ‚ΠΈΠΏ профиля подписи прилоТСния.

ΠžΡΠ½ΠΎΠ²Π½Ρ‹Π΅ Ρ‚ΠΈΠΏΡ‹ ΠΏΡ€ΠΎΡ„ΠΈΠ»Π΅ΠΉ:

  • Development β€” ΠΏΡ€Π΅Π΄Π½Π°Π·Π½Π°Ρ‡Π΅Π½ для подписи прилоТСния ΠΊΠΎΠΌΠ°Π½Π΄Ρ‹ Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΎΠ², ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ Development-сСртификат (имя Π²ΠΈΠ΄Π° iPhone Developer: XXXXX);
  • Ad Hoc β€” ΠΏΡ€Π΅Π΄Π½Π°Π·Π½Π°Ρ‡Π΅Π½ для подписи тСстового прилоТСния ΠΈ Π²Π½ΡƒΡ‚Ρ€Π΅Π½Π½Π΅ΠΉ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠΈ QA-ΠΎΡ‚Π΄Π΅Π»ΠΎΠΌ, ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ Distribution-сСртификат Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠ° (имя Π²ΠΈΠ΄Π° iPhone Distribution: XXXXX);
  • App Store β€” Ρ€Π΅Π»ΠΈΠ·Π½Ρ‹ΠΉ Π±ΠΈΠ»Π΄ для внСшнСго тСстирования Ρ‡Π΅Ρ€Π΅Π· TestFlight ΠΈ Π²Ρ‹Π³Ρ€ΡƒΠ·ΠΊΠΈ Π² App Store, ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ Distribution-сСртификат Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠ°.

ΠŸΡ€ΠΈ Π³Π΅Π½Π΅Ρ€Π°Ρ†ΠΈΠΈ ΠΏΡ€ΠΎΡ„ΠΈΠ»Π΅ΠΉ Development ΠΈ Ad Hoc Ρ‚Π°ΠΊΠΆΠ΅ указываСтся список устройств, Π½Π° ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΌΠΎΠΆΠ½ΠΎ ΡƒΡΡ‚Π°Π½ΠΎΠ²ΠΈΡ‚ΡŒ Π±ΠΈΠ»Π΄, Ρ‡Ρ‚ΠΎ позволяСт Π΄ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ Ρ€Π°Π·Π³Ρ€Π°Π½ΠΈΡ‡ΠΈΡ‚ΡŒ доступ для ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Π΅ΠΉ. Π’ ΠΏΡ€ΠΎΡ„ΠΈΠ»Π΅ App Store Π½Π΅Ρ‚ списка устройств, Ρ‚Π°ΠΊ ΠΊΠ°ΠΊ Ρ€Π°Π·Π³Ρ€Π°Π½ΠΈΡ‡Π΅Π½ΠΈΠ΅ΠΌ доступа ΠΏΡ€ΠΈ Π·Π°ΠΊΡ€Ρ‹Ρ‚ΠΎΠΌ Π±Π΅Ρ‚Π°-тСстировании занимаСтся TestFlight, ΠΎ ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΌ Π±ΡƒΠ΄Π΅Ρ‚ рассказано ΠΏΠΎΠ·ΠΆΠ΅.

Для наглядности ΠΌΠΎΠΆΠ½ΠΎ ΠΏΡ€Π΅Π΄ΡΡ‚Π°Π²ΠΈΡ‚ΡŒ ΠΏΡ€ΠΎΡ„ΠΈΠ»ΡŒ Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠ° Π² Π²ΠΈΠ΄Π΅ Ρ‚Π°Π±Π»ΠΈΡ‡ΠΊΠΈ Π½ΠΈΠΆΠ΅. Π’Π°ΠΊ ΠΏΡ€ΠΎΡ‰Π΅ ΠΏΠΎΠ½ΡΡ‚ΡŒ, ΠΊΠ°ΠΊΠΈΠ΅ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Ρ‹ для сборки Π½Π°ΠΌ Π½ΡƒΠΆΠ½Ρ‹ ΠΈ ΠΎΡ‚ΠΊΡƒΠ΄Π° ΠΈΡ… Π±Ρ€Π°Ρ‚ΡŒ.

ΠžΡΠΎΠ±Π΅Π½Π½ΠΎΡΡ‚ΠΈ сборки ΠΈ доставки iOS-ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠΉ

Π‘Π±ΠΎΡ€ΠΊΠ°

Π§Ρ‚ΠΎΠ±Ρ‹ Π±Ρ‹Π»ΠΎ ΠΏΡ€ΠΎΡ‰Π΅ Ρ€Π°Π·Π΄Π΅Π»ΡΡ‚ΡŒ сборки ΠΏΠΎ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Ρƒ ΠΈ срСдС, ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌ ΠΈΠΌΠ΅Π½Π° ΠΏΡ€ΠΎΡ„ΠΈΠ»Π΅ΠΉ Π²ΠΈΠ΄Π° ${ProjectName}_${Instance}, Ρ‚ΠΎ Π΅ΡΡ‚ΡŒ имя ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π° + инстанс (зависит ΠΎΡ‚ срСды прилоТСния: Dev, QA, GD, Staging, Live ΠΈ Ρ‚Π°ΠΊ Π΄Π°Π»Π΅Π΅).

ΠŸΡ€ΠΈ ΠΈΠΌΠΏΠΎΡ€Ρ‚Π΅ Π½Π° Π±ΠΈΠ»Π΄-сСрвСр ΠΏΡ€ΠΎΡ„ΠΈΠ»ΡŒ мСняСт Π½Π°Π·Π²Π°Π½ΠΈΠ΅ Π½Π° ΡƒΠ½ΠΈΠΊΠ°Π»ΡŒΠ½Ρ‹ΠΉ ID ΠΈ пСрСмСщаСтся Π² ΠΏΠ°ΠΏΠΊΡƒ /Users/$Username/Library/MobileDevice/Provisioning Profiles (Π³Π΄Π΅ $Username соотвСтствуСт ΠΈΠΌΠ΅Π½ΠΈ ΡƒΡ‡Π΅Ρ‚Π½ΠΎΠΉ записи ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ Π±ΠΈΠ»Π΄-сСрвСра).

БущСствуСт Π΄Π²Π° способа сборки Ρ„Π°ΠΉΠ»Π° *.ipa β€” ΡƒΡΡ‚Π°Ρ€Π΅Π²ΡˆΠΈΠΉ (PackageApplication) ΠΈ соврСмСнный (Ρ‡Π΅Ρ€Π΅Π· созданиС XcAchive ΠΈ экспорт). ΠŸΠ΅Ρ€Π²Ρ‹ΠΉ способ считаСтся ΡƒΡΡ‚Π°Ρ€Π΅Π²ΡˆΠΈΠΌ, Ρ‚Π°ΠΊ ΠΊΠ°ΠΊ с вСрсии 8.3 ΠΌΠΎΠ΄ΡƒΠ»ΡŒ ΡƒΠΏΠ°ΠΊΠΎΠ²ΠΊΠΈ app-Ρ„Π°ΠΉΠ»Π° ΡƒΠ±Ρ€Π°Π½ ΠΈΠ· дистрибутива Xcode. Для Π΅Π³ΠΎ использования Π½Π°Π΄ΠΎ ΡΠΊΠΎΠΏΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ ΠΌΠΎΠ΄ΡƒΠ»ΡŒ ΠΈΠ· старого Xcode (вСрсии 8.2 ΠΈ Π±ΠΎΠ»Π΅Π΅ Ρ€Π°Π½Π½ΠΈΡ…) Π² ΠΏΠ°ΠΏΠΊΡƒ:
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/

И Π·Π°Ρ‚Π΅ΠΌ Π²Ρ‹ΠΏΠΎΠ»Π½ΠΈΡ‚ΡŒ ΠΊΠΎΠΌΠ°Π½Π΄Ρƒ:

chmod +x /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/*

Π”Π°Π»Π΅Π΅ Π½ΡƒΠΆΠ½ΠΎ ΡΠΎΠ±Ρ€Π°Ρ‚ΡŒ *.app-Ρ„Π°ΠΉΠ» прилоТСния:

xcodebuild 
-workspace $ProjectDir/$ProjectName.xcworkspace 
-scheme $SchemeName 
-sdk iphoneos 
build 
-configuration Release 
-derivedDataPath build 
CODE_SIGN_IDENTITY=”$DevAccName”
PROVISIONING_PROFILE=”$ProfileId”
DEPLOYMENT_POSTPROCESSING=YES 
SKIP_INSTALL=YES 
ENABLE_BITCODE=NO

Π“Π΄Π΅:

-workspace β€” ΠΏΡƒΡ‚ΡŒ ΠΊ Ρ„Π°ΠΉΠ»Ρƒ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°.

-scheme β€” ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌΠ°Ρ схСма, указанная Π² ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π΅.

-derivedDataPath β€” ΠΏΡƒΡ‚ΡŒ Π²Ρ‹Π³Ρ€ΡƒΠ·ΠΊΠΈ собранного прилоТСния (*.app).

CODE_SIGN_IDENTITY β€” имя Π°ΠΊΠΊΠ°ΡƒΠ½Ρ‚Π° Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠ°, ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ ΠΌΠΎΠΆΠ½ΠΎ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΈΡ‚ΡŒ Π² Keychain (iPhone Developer: XXXX XXXXXXX, Π±Π΅Π· TeamID Π² скобках).

ΠžΡΠΎΠ±Π΅Π½Π½ΠΎΡΡ‚ΠΈ сборки ΠΈ доставки iOS-ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠΉ

PROVISIONING_PROFILE β€” ID профиля для подписи прилоТСния, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ ΠΌΠΎΠΆΠ½ΠΎ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ ΠΊΠΎΠΌΠ°Π½Π΄ΠΎΠΉ:

cd "/Users/$Username/Library/MobileDevice/Provisioning Profiles/" && find *.mobileprovision -type f | xargs grep -li ">${ProjectName}_${Instance}<" | sed -e 's/.mobileprovision//'

Если Π² ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠΈ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ Π΄ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ ΠΏΡ€ΠΎΡ„ΠΈΠ»ΡŒ (Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, для Push Notifications), Ρ‚ΠΎ вмСсто PROVISIONING_PROFILE ΡƒΠΊΠ°Π·Ρ‹Π²Π°Π΅ΠΌ:

APP_PROFILE=”$AppProfile” 
EXTENSION_PROFILE=”$ExtProfile” 

Π”Π°Π»Π΅Π΅ ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½Π½Ρ‹ΠΉ Ρ„Π°ΠΉΠ» *.app слСдуСт ΡƒΠΏΠ°ΠΊΠΎΠ²Π°Ρ‚ΡŒ Π² *.ipa. Для этого ΠΌΠΎΠΆΠ½ΠΎ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ ΠΊΠΎΠΌΠ°Π½Π΄Ρƒ Π²ΠΈΠ΄Π°:

/usr/bin/xcrun --sdk iphoneos PackageApplication 
-v $(find "$ProjectDir/build/Build/Products/Release-iphoneos" -name "*.app") 
-o "$ProjectDir/$ProjectName_$Instance.ipa"

Однако Π΄Π°Π½Π½Ρ‹ΠΉ способ считаСтся ΡƒΡΡ‚Π°Ρ€Π΅Π²ΡˆΠΈΠΌ с Ρ‚ΠΎΡ‡ΠΊΠΈ зрСния Apple. ΠΠΊΡ‚ΡƒΠ°Π»ΡŒΠ½Ρ‹ΠΌ являСтся ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½ΠΈΠ΅ *.ipa ΠΏΡƒΡ‚Π΅ΠΌ экспорта ΠΈΠ· Π°Ρ€Ρ…ΠΈΠ²Π° прилоТСния.

Для Π½Π°Ρ‡Π°Π»Π° Π½ΡƒΠΆΠ½ΠΎ ΡΠΎΠ±Ρ€Π°Ρ‚ΡŒ Π°Ρ€Ρ…ΠΈΠ² ΠΊΠΎΠΌΠ°Π½Π΄ΠΎΠΉ:

xcodebuild 
-workspace $ProjectDir/$ProjectName.xcworkspace 
-scheme $SchemeName 
-sdk iphoneos 
-configuration Release 
archive 
-archivePath $ProjectDir/build/$ProjectName.xcarchive 
CODE_SIGN_IDENTITY=”$DevAccName” 
PROVISIONING_PROFILE=”$ProfileId”
ENABLE_BITCODE=NO 
SYNCHRONOUS_SYMBOL_PROCESSING=FALSE

ΠžΡ‚Π»ΠΈΡ‡ΠΈΡ Π·Π°ΠΊΠ»ΡŽΡ‡Π°ΡŽΡ‚ΡΡ Π² ΠΌΠ΅Ρ‚ΠΎΠ΄Π΅ сборки ΠΈ ΠΎΠΏΡ†ΠΈΠΈ SYNCHRONOUS_SYMBOL_PROCESSING, которая ΠΎΡ‚ΠΊΠ»ΡŽΡ‡Π°Π΅Ρ‚ Π²Ρ‹Π³Ρ€ΡƒΠ·ΠΊΡƒ символов Π²ΠΎ врСмя сборки.

Π”Π°Π»Π΅Π΅ Π½Π°ΠΌ Π½Π°Π΄ΠΎ ΡΠ³Π΅Π½Π΅Ρ€ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Ρ„Π°ΠΉΠ» с настройками экспорта:

ExportSettings="$ProjectDir/exportOptions.plist"

cat << EOF > $ExportSettings
<?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>compileBitcode</key>
<false/>
<key>uploadBitcode</key>
<false/>
<key>uploadSymbols</key>
<false/>
<key>method</key>
<string>$Method</string>
<key>provisioningProfiles</key>
<dict>
<key>$BundleID</key>
<string>$ProfileId</string>
</dict>
<key>signingCertificate</key>
<string>$DevAccName</string>
<key>signingStyle</key>
<string>manual</string>
<key>stripSwiftSymbols</key>
<true/>
<key>teamID</key>
<string>$TeamID</string>
<key>thinning</key>
<string><none></string>
</dict>
</plist>
EOF

Π“Π΄Π΅:

$Method β€” ΠΌΠ΅Ρ‚ΠΎΠ΄ доставки, соотвСтствуСт Ρ‚ΠΈΠΏΡƒ профиля подписи прилоТСния, Ρ‚ΠΎ Π΅ΡΡ‚ΡŒ для Development Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ Π±ΡƒΠ΄Π΅Ρ‚ development, для Ad Hoc β€” ad-hoc, Π° для App Store β€” app-store.

$BundleID β€” ID прилоТСния, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ ΡƒΠΊΠ°Π·Π°Π½ Π² настройках прилоТСния. ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΈΡ‚ΡŒ ΠΌΠΎΠΆΠ½ΠΎ ΠΊΠΎΠΌΠ°Π½Π΄ΠΎΠΉ:

defaults read $ProjectDir/Info CFBundleIdentifier

$DevAccName ΠΈ $ProfileId β€” настройки ΠΈΠΌΠ΅Π½ΠΈ Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠ° ΠΈ ID профиля подписи, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ использовались Ρ€Π°Π½Π΅Π΅ ΠΈ Π΄ΠΎΠ»ΠΆΠ½Ρ‹ ΡΠΎΠ²ΠΏΠ°Π΄Π°Ρ‚ΡŒ со значСниями Π² настройках экспорта.

$TeamID β€” дСсятизначный ID Π² скобках послС ΠΈΠΌΠ΅Π½ΠΈ Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠ°, ΠΏΡ€ΠΈΠΌΠ΅Ρ€: iPhone Developer: …… (XXXXXXXXXX); ΠΌΠΎΠΆΠ½ΠΎ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΈΡ‚ΡŒ Π² Keychain.

Π”Π°Π»Π΅Π΅ с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ ΠΊΠΎΠΌΠ°Π½Π΄Ρ‹ экспорта ΠΏΠΎΠ»ΡƒΡ‡Π°Π΅ΠΌ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΡ‹ΠΉ Ρ„Π°ΠΉΠ» *.ipa:

xcodebuild 
-exportArchive 
-archivePath $ProjectDir/build/$ProjectName.xcarchive 
-exportPath $ProjectDir 
-exportOptionsPlist $ExportSettings

Доставка

Π’Π΅ΠΏΠ΅Ρ€ΡŒ собранный Ρ„Π°ΠΉΠ» Π½ΡƒΠΆΠ½ΠΎ Π΄ΠΎΡΡ‚Π°Π²ΠΈΡ‚ΡŒ ΠΊΠΎΠ½Π΅Ρ‡Π½ΠΎΠΌΡƒ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŽ, Ρ‚ΠΎ Π΅ΡΡ‚ΡŒ ΡƒΡΡ‚Π°Π½ΠΎΠ²ΠΈΡ‚ΡŒ Π½Π° устройство.

Для распространСния Π±ΠΈΠ»Π΄ΠΎΠ² Development ΠΈ Ad Hoc сущСствуСт мноТСство сСрвисов Π²Ρ€ΠΎΠ΄Π΅ HockeyApp, AppBlade ΠΈ ΠΏΡ€ΠΎΡ‡ΠΈΡ…, ΠΎΠ΄Π½Π°ΠΊΠΎ Π² Ρ€Π°ΠΌΠΊΠ°Ρ… Π΄Π°Π½Π½ΠΎΠΉ ΡΡ‚Π°Ρ‚ΡŒΠΈ Ρ€Π΅Ρ‡ΡŒ ΠΏΠΎΠΉΠ΄Π΅Ρ‚ ΠΎΠ± Π°Π²Ρ‚ΠΎΠ½ΠΎΠΌΠ½ΠΎΠΌ сСрвСрС для Ρ€Π°Π·Π΄Π°Ρ‡ΠΈ ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠΉ.

Установка прилоТСния для iOS ΠΏΡ€ΠΎΡ…ΠΎΠ΄ΠΈΡ‚ Π² 2 этапа:

  1. ΠŸΠΎΠ»ΡƒΡ‡Π΅Π½ΠΈΠ΅ манифСста установки прилоТСния Ρ‡Π΅Ρ€Π΅Π· Items Service.
  2. Установка Ρ„Π°ΠΉΠ»Π° *.ipa согласно ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΠΈ, ΡƒΠΊΠ°Π·Π°Π½Π½ΠΎΠΉ Π² манифСстС, Ρ‡Π΅Ρ€Π΅Π· HTTPS.

Π’Π°ΠΊΠΈΠΌ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ, Π½Π°ΠΌ для Π½Π°Ρ‡Π°Π»Π° Π½Π°Π΄ΠΎ ΡΠ³Π΅Π½Π΅Ρ€ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ манифСст установки (Ρ‚ΠΈΠΏ Ρ„Π°ΠΉΠ»Π° *.plist) ΠΊΠΎΠΌΠ°Π½Π΄ΠΎΠΉ:

cat << EOF > $manifest
<?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>items</key>
<array>
<dict>
<key>assets</key>
<array>
<dict>
<key>kind</key>
<string>software-package</string>
<key>url</key>
<string>$ipaUrl</string>
</dict>
</array>
<key>metadata</key>
<dict>
<key>bundle-identifier</key>
<string>$BundleID</string>
<key>bundle-version</key>
<string>$AppVersion</string>
<key>kind</key>
<string>software</string>
<key>title</key>
<string>$ProjectName_$Instance</string>
<key>subtitle</key>
<string>$Instance</string>
</dict>
</dict>
</array>
</dict>
</plist>
EOF

Как Π²ΠΈΠ΄ΠΈΠΌ, манифСст содСрТит практичСски всС ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Ρ‹, ΡƒΡ‡Π°ΡΡ‚Π²ΡƒΡŽΡ‰ΠΈΠ΅ Π² сборкС прилоТСния.

Π’Π΅Ρ€ΡΠΈΡŽ прилоТСния ($AppVersion) ΠΌΠΎΠΆΠ½ΠΎ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΈΡ‚ΡŒ ΠΊΠΎΠΌΠ°Π½Π΄ΠΎΠΉ:

defaults read $ProjectDir/Info CFBundleVersion

ΠŸΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ $ipaUrl содСрТит ΠΏΡ€ΡΠΌΡƒΡŽ ссылку Π½Π° скачиваниС Ρ„Π°ΠΉΠ»Π° *.ipa. Π‘ сСдьмой вСрсии iOS ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ Π΄ΠΎΠ»ΠΆΠ½ΠΎ Π±Ρ‹Ρ‚ΡŒ установлСно Ρ‡Π΅Ρ€Π΅Π· HTTPS. Π’ восьмой вСрсии Π½Π΅ΠΌΠ½ΠΎΠ³ΠΎ измСнился Ρ„ΠΎΡ€ΠΌΠ°Ρ‚ манифСста: Π±Ρ‹Π»ΠΈ ΡƒΠ΄Π°Π»Π΅Π½Ρ‹ Π±Π»ΠΎΠΊΠΈ с настройками ΠΈΠΊΠΎΠ½ΠΎΠΊ прилоТСния Π²ΠΈΠ΄Π°

<images>
   <image>...</image>
</images>

Π’Π°ΠΊΠΈΠΌ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ, для установки прилоТСния достаточно простой html-страницы со ссылкой Π²ΠΈΠ΄Π°:

itms-services://?action=download-manifest&url=https://$ServerUrl/$ProjectName/$Instance/iOS/$AppVersion/manifest.plist

Для Π½ΡƒΠΆΠ΄ ΠΎΡ‚Π΄Π΅Π»ΠΎΠ² Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ ΠΈ тСстирования компания Plarium создала своС ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ установки Π±ΠΈΠ»Π΄ΠΎΠ², ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ Π΄Π°Π΅Ρ‚ Π½Π°ΠΌ:

  • Π°Π²Ρ‚ΠΎΠ½ΠΎΠΌΠ½ΠΎΡΡ‚ΡŒ ΠΈ Π½Π΅Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡ‚ΡŒ,
  • Ρ†Π΅Π½Ρ‚Ρ€Π°Π»ΠΈΠ·Π°Ρ†ΠΈΡŽ управлСния доступом ΠΈ Π±Π΅Π·ΠΎΠΏΠ°ΡΠ½ΡƒΡŽ установку ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠΉ Ρ‡Π΅Ρ€Π΅Π· Β«Π²Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹Π΅Β», динамичСски создаваСмыС ссылки,
  • Ρ€Π°ΡΡˆΠΈΡ€ΡΠ΅ΠΌΡ‹ΠΉ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΎΠ½Π°Π» (Ρ‚ΠΎ Π΅ΡΡ‚ΡŒ ΠΊΠΎΠΌΠ°Π½Π΄Π° Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ ΠΏΡ€ΠΈ нСобходимости ΠΌΠΎΠΆΠ΅Ρ‚ ΠΈΠ½Ρ‚Π΅Π³Ρ€ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Π½Π΅Π΄ΠΎΡΡ‚Π°ΡŽΡ‰ΠΈΠ΅ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ Π² ΡƒΠΆΠ΅ ΡΡƒΡ‰Π΅ΡΡ‚Π²ΡƒΡŽΡ‰Π΅Π΅ ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅).

ВСстированиС

Π’Π΅ΠΏΠ΅Ρ€ΡŒ Ρ€Π΅Ρ‡ΡŒ ΠΏΠΎΠΉΠ΄Π΅Ρ‚ ΠΎ ΠΏΡ€Π΅Π΄Ρ€Π΅Π»ΠΈΠ·Π½ΠΎΠΌ тСстировании прилоТСния с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ TestFlight.

ΠžΠ±ΡΠ·Π°Ρ‚Π΅Π»ΡŒΠ½Ρ‹ΠΌΠΈ условиями для Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠΈ ΡΠ²Π»ΡΡŽΡ‚ΡΡ Ρ‚ΠΈΠΏ профиля подписи App Store ΠΈ Π½Π°Π»ΠΈΡ‡ΠΈΠ΅ сгСнСрированных API-ΠΊΠ»ΡŽΡ‡Π΅ΠΉ.

Π•ΡΡ‚ΡŒ нСсколько способов Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠΈ прилоТСния:

  • Ρ‡Π΅Ρ€Π΅Π· Xcode (Organizer),
  • Ρ‡Π΅Ρ€Π΅Π· altool,
  • Ρ‡Π΅Ρ€Π΅Π· Application Loader для старых вСрсий Xcode (Ρ‚Π΅ΠΏΠ΅Ρ€ΡŒ Transporter).

Для автоматичСской Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠΈ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ altool, Π² ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΌ Ρ‚ΠΎΠΆΠ΅ Π΅ΡΡ‚ΡŒ Π΄Π²Π° способа Π°Π²Ρ‚ΠΎΡ€ΠΈΠ·Π°Ρ†ΠΈΠΈ:

  • App-Specific Password,
  • API Key.

Π‘ΠΎΠ»Π΅Π΅ ΠΏΡ€Π΅Π΄ΠΏΠΎΡ‡Ρ‚ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΠΉ являСтся Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠ° прилоТСния с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ API Key.

Для получСния API Key ΠΏΠ΅Ρ€Π΅Ρ…ΠΎΠ΄ΠΈΠΌ ΠΏΠΎ ссылкС ΠΈ Π³Π΅Π½Π΅Ρ€ΠΈΡ€ΡƒΠ΅ΠΌ ΠΊΠ»ΡŽΡ‡. ΠšΡ€ΠΎΠΌΠ΅ самого ΠΊΠ»ΡŽΡ‡Π° Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅ *.p8, Π½Π°ΠΌ понадобятся Π΄Π²Π° ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Π°: IssuerID ΠΈ KeyID.

ΠžΡΠΎΠ±Π΅Π½Π½ΠΎΡΡ‚ΠΈ сборки ΠΈ доставки iOS-ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠΉ

Π”Π°Π»Π΅Π΅ скачанный ΠΊΠ»ΡŽΡ‡ ΠΈΠΌΠΏΠΎΡ€Ρ‚ΠΈΡ€ΡƒΠ΅ΠΌ Π½Π° Π±ΠΈΠ»Π΄-сСрвСр:

mkdir -p ~/.appstoreconnect/private_keys
mv ~/Downloads/AuthKey_${KeyID}.p8 ~/.appstoreconnect/private_keys/

ΠŸΠ΅Ρ€Π΅Π΄ Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠΎΠΉ прилоТСния Π² TestFlight Π½ΡƒΠΆΠ½ΠΎ Π²Ρ‹ΠΏΠΎΠ»Π½ΠΈΡ‚ΡŒ Π²Π°Π»ΠΈΠ΄Π°Ρ†ΠΈΡŽ прилоТСния, Π΄Π΅Π»Π°Π΅ΠΌ это ΠΊΠΎΠΌΠ°Π½Π΄ΠΎΠΉ:

xcrun altool 
--validate-app 
-t ios 
-f $(find "$ProjectDir" -name "*.ipa") 
--apiKey β€œ$KeyID” 
--apiIssuer β€œ$IssuerID” 

Π“Π΄Π΅ apiKey ΠΈ apiIssuer ΠΈΠΌΠ΅ΡŽΡ‚ значСния ΠΏΠΎΠ»Π΅ΠΉ со страницы Π³Π΅Π½Π΅Ρ€Π°Ρ†ΠΈΠΈ API-ΠΊΠ»ΡŽΡ‡Π°.

Π”Π°Π»Π΅Π΅ ΠΏΡ€ΠΈ ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎΠΉ Π²Π°Π»ΠΈΠ΄Π°Ρ†ΠΈΠΈ выполняСм Π·Π°Π³Ρ€ΡƒΠ·ΠΊΡƒ прилоТСния ΠΊΠΎΠΌΠ°Π½Π΄ΠΎΠΉ --upload-app c Ρ‚Π΅ΠΌΠΈ ΠΆΠ΅ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Π°ΠΌΠΈ.

ΠŸΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ Π±ΡƒΠ΄Π΅Ρ‚ ΠΏΡ€ΠΎΠ²Π΅Ρ€Π΅Π½ΠΎ Apple Π² Ρ‚Π΅Ρ‡Π΅Π½ΠΈΠ΅ ΠΎΠ΄Π½ΠΎΠ³ΠΎ-Π΄Π²ΡƒΡ… Π΄Π½Π΅ΠΉ ΠΈ послС станСт доступным внСшним тСстировщикам: ΠΈΠΌ ΠΏΡ€ΠΈΡˆΠ»ΡŽΡ‚ Π½Π° ΠΏΠΎΡ‡Ρ‚Ρƒ ссылки для установки.

Π”Ρ€ΡƒΠ³ΠΈΠΌ способом Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠΈ прилоТСния Ρ‡Π΅Ρ€Π΅Π· altool являСтся использованиС App-Specific Password.

Для получСния App-Specific Password Π½ΡƒΠΆΠ½ΠΎ ΠΏΠ΅Ρ€Π΅ΠΉΡ‚ΠΈ ΠΏΠΎ ссылкС ΠΈ ΡΠ³Π΅Π½Π΅Ρ€ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Π΅Π³ΠΎ Π² Ρ€Π°Π·Π΄Π΅Π»Π΅ Security.

ΠžΡΠΎΠ±Π΅Π½Π½ΠΎΡΡ‚ΠΈ сборки ΠΈ доставки iOS-ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠΉ

Π”Π°Π»Π΅Π΅ слСдуСт ΡΠΎΠ·Π΄Π°Ρ‚ΡŒ Π² Keychain запись Π±ΠΈΠ»Π΄-сСрвСра с этим ΠΏΠ°Ρ€ΠΎΠ»Π΅ΠΌ. Π‘ 11 вСрсии Xcode это ΠΌΠΎΠΆΠ½ΠΎ ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ ΠΊΠΎΠΌΠ°Π½Π΄ΠΎΠΉ:

xcrun altool --store-password-in-keychain-item "Altool" -u "$DeveloperName" -p $AppPswd

Π“Π΄Π΅:

$DeveloperName β€” имя Π°ΠΊΠΊΠ°ΡƒΠ½Ρ‚Π° iOS-Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠ°, ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌΠΎΠ΅ для Π»ΠΎΠ³ΠΈΠ½Π° Π² сСрвисы Apple.

$AppPswd β€” сгСнСрированный App-Specific Password.

Π”Π°Π»Π΅Π΅ ΠΏΠΎΠ»ΡƒΡ‡Π°Π΅ΠΌ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Π° asc-provider ΠΈ провСряСм ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎΡΡ‚ΡŒ ΠΈΠΌΠΏΠΎΡ€Ρ‚Π° пароля ΠΊΠΎΠΌΠ°Π½Π΄ΠΎΠΉ:

xcrun altool --list-providers -u "$DeveloperName" -p "@keychain:Altool"

ΠŸΠΎΠ»ΡƒΡ‡Π°Π΅ΠΌ Π²Ρ‹Π²ΠΎΠ΄:

Provider listing:
- Long Name - - Short Name -
XXXXXXX        XXXXXXXXX

Как Π²ΠΈΠ΄ΠΈΠΌ, искомоС Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ Short Name (asc-provider) совпадаСт с ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΎΠΌ $TeamID, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ ΠΌΡ‹ использовали ΠΏΡ€ΠΈ сборкС прилоТСния.

Для Π²Π°Π»ΠΈΠ΄Π°Ρ†ΠΈΠΈ ΠΈ Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠΈ прилоТСния Π² TestFlight примСняСм ΠΊΠΎΠΌΠ°Π½Π΄Ρƒ:

xcrun altool 
--(validate|upload)-app   
-f $(find "$ProjectDir" -name "*.ipa") 
-u "$DeveloperName" 
-p "@keychain:Altool" 

Π’ качСствС Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Π° -p ΠΌΠΎΠΆΠ½ΠΎ Π²Π·ΡΡ‚ΡŒ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ $AppPswd Π² Π½Π΅Π·Π°ΡˆΠΈΡ„Ρ€ΠΎΠ²Π°Π½Π½ΠΎΠΌ (явном) Π²ΠΈΠ΄Π΅.

Однако, ΠΊΠ°ΠΊ ΡƒΠΆΠ΅ Π±Ρ‹Π»ΠΎ сказано, с Ρ‚ΠΎΡ‡ΠΊΠΈ зрСния работоспособности для Π°Π²Ρ‚ΠΎΡ€ΠΈΠ·Π°Ρ†ΠΈΠΈ altool Π»ΡƒΡ‡ΡˆΠ΅ Π²Ρ‹Π±Ρ€Π°Ρ‚ΡŒ API Key, Ρ‚Π°ΠΊ ΠΊΠ°ΠΊ Π² Ρ€Π°Π·Π½Ρ‹Ρ… вСрсиях Xcode Π²ΡΡ‚Ρ€Π΅Ρ‡Π°ΡŽΡ‚ΡΡ Ρ‚Π΅ ΠΈΠ»ΠΈ ΠΈΠ½Ρ‹Π΅ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΡ‹ (Β«Π½Π΅ Π²ΠΈΠ΄ΠΈΡ‚Β» Keychain, ошибки Π°Π²Ρ‚ΠΎΡ€ΠΈΠ·Π°Ρ†ΠΈΠΈ ΠΏΡ€ΠΈ Π²Ρ‹Π³Ρ€ΡƒΠ·ΠΊΠ΅ ΠΈ ΠΏΡ€ΠΎΡ‡Π΅Π΅).

На этом, собствСнно, всС. Π–Π΅Π»Π°ΡŽ всСм причастным ΡƒΡΠΏΠ΅ΡˆΠ½Ρ‹Ρ… сборок ΠΈ бСспроблСмных Ρ€Π΅Π»ΠΈΠ·ΠΎΠ² Π² App Store.

Π˜ΡΡ‚ΠΎΡ‡Π½ΠΈΠΊ: habr.com

Π”ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ