В статье будет кратко описано, как собрать приложение консольными командами и запустить на реальном девайсе и симуляторе без какой-либо надобности открывать xcode для этого.
В общем, стоит начать с того, что в моей компании имеется отдел ios разработки в составе > 10 человек, которые работают над пачкой приложений. Для автоматизации рутинной работы нам пришлось развернуть CI сервер (пока самописный, ввиду особых исторических причин (ну как у всех), планируется миграция на jenkins). К рутинным вещам я отношу — сборку проектов, запуск тестов (если, конечно, вы их пишете), создание ипашек для тестеров и для выкладки в app store. В общем, хотелось чтобы по нажатию кнопочки или по хуку в гите всё это начинало работать. Пока у нас всё работает по нажатию кнопки разработчиком, про гит пока только в планах. В данной статье я только затрону тему компиляции проекта и упаковку его в ipa файла. Функционал запуска проектов на девайсах на стороне CI сервера еще находится в стадии разработки, а вот сам процесс упаковки уже давно в «бою», может кому пригодиться.
Что планируем:
Имеем:
Для частоты эксперимента будем использовать стороннее приложение, чтобы уважаемые читатели могли повторить, что описано в статье. Используем тестовое приложение для опенсорсного apple-овского framework-а ResearchKit.
Перед компиляцией проекта необходимо установить apple doc generator github.com/tomaz/appledoc (там есть описание как установить).
Переходим к скаченному проекту. Корневой проект представляет собой сам ResearchKit framework, само тестовое приложение находится в другом месте, переходим туда:
Пробуем компилировать приложение. Собираем под симулятор, так как для сборки проекта под симулятор не нужны сертификаты/провижен файлы.
Получаем ошибку:
Билд падает, так как нет схемы ORKCatalog. Так как схема в оригинальном проекте не была помечена как shared, то после “слива” из гита, xcode еще ничего не знает об этой схеме, чтобы он ее снегирил, нужно просто открыть проект. Поэтому просто открываем проект:
И сразу же закрываем, после наша схема появится, проверить схемы можно через xcodebuild -list. Пробуем собрать снова:
После видим долгожданное ** BUILD SUCCEEDED **. Отлично, всё работает.
Компилируем под симулятор, архитектуры могут быть i386/x86_64. Через SYMROOT задаем путь результата сборки:
(Мысли вслух: когда писал статью, компиляция в режиме Release работала, перед публикацией статьи еще раз проверил все шаги и компиляция в этом режиме перестала работать, поэтому собираем в Debug, какие-то из последних коммитов это сломали.)
После успешной сборки мы получили ORKCatalog.app файл в build/Debug-iphonesimulator/. Осталось запустить это на симуляторе. Для этого будем использовать ios-sim утилиту github.com/phonegap/ios-sim. Пользоваться ей достаточно просто.
Получаем список доступных симуляторов:
Из предложенного списка я выбрал 'iPhone-6-Plus'. Запускаем приложение на нем:
Если всё правильно сделали, то должен запуститься симулятор с приложением (для входа в режим ввода в консоли используйте ctrl + C).
Тут немного сложнее, нам нужен mobileprovision файл для разработки (developer) и наличие сертификата на машине (p12 файл), при этом наличие аккаунта в xcode не нужно. При подписании/упаковки приложений из консоли нет необходимости добавлять аккаунт в xcode, это очень помогает, например, на CI сервере можно держать только p12 файлы.
Будем считать, что на вашей машине есть соответствующий сертификат. После генерируем developer mobileprovision через developer.apple.com в вашем аккаунте и скачиваем на машину (дадим ему название test.mobileprovision, а bundle id будет ru.habrahabr.test). После копируем его в директорию, где его сможет подхватить xcode:
Собираем архив под девайс (это архитектуры arm64/armv7):
Компиляция упадет, так как мы еще не прописали в приложении свой bundle id и не прилинковали mobileprovision, ошибка будет такого вида:
Будем передавать bundle id из консоли + нужно прописать его в Info.plist файле. Также через консоль будем передавать линк на наш mobileprovision. Plist файл находим по пути ORKCatalog/Supporting Files/Info.plist, в котором для ключа CFBundleIdentifier выставляем значение ru.habrahabr.test. Bundle id передаем через ключ со значением нашего ид PRODUCT_BUNDLE_IDENTIFIER=ru.habrahabr.test. Линк на mobileprovision передаем через ключ PROVISIONIG_PROFILE, со значние UUID, который прописан в mobileprovision.
Достаем UUID:
Значение будет подобно 87b0df89-793a-4a0f-92bf-c5f9c35f1405. Снова собираем:
В итоге получим архив build/archive.xcarchive, который осталось упаковать в ipa. В xcode 7 появился новый метод упаковки, им и воспользуемся. Перед этим создадим конфиг файл options.plist с таким содержанием:
Пробуем собирать ipa:
Сборка падает, по логам можно понять, что что-то с entitlements:
По ошибке ясно, что приложение подписываем entitlements, значения в котором не соответствуют значениям в нашем mobileprovision, а именно com.apple.developer.healthkit. Ищем причину. Смотрим каким entitlements подписано приложение:
Получаем:
Видим, что всё ок, за исключением этого:
У нас в mobileprovision нет этой опции, нужно выяснить, откуда это взялось:
Поиск нам выдал ORKCatalog/Supporitng Files/ORKCatalog.entitlements. Смотрим что внутри:
Там только одно значение:
По логике нужно перезаводить mobileprovision, в который нужно добавить это значение, но мы ленивые и для теста это не обязательно, нам нужно просто переподписать приложение с entitlements без этого значения.
У нас есть как минимум два варианта:
1. Просто редактируем существующий entitlements (ORKCatalog/Supporitng Files/ORKCatalog.entitlements) и снова пересобираем (через archive).
2. Без пересборки, сами переподпишем ORKCatalog.app с нужным entitlements.
Выберем первый вариант как более простой. Поэтому из файла ORKCatalog/Supporitng Files/ORKCatalog.entitlements просто удаляем строки:
и снова пересобираем архив:
После создаем ipa:
Видим долгожданное сообщение ** EXPORT SUCCEEDED **. Под build/dev-ipa/ появиться ipa файл, который будем устанавливать на девайс. Устанавливать на девайс будем с помощью ios-deploy github.com/phonegap/ios-deploy. Цепляем девайс к машине, получаем id девайса через:
Деплоим на девайс:
Всё тоже самое как для develop версии, только меняем линк на релизный mobileprovision, в options.plist вместо development проставляем app-store (какие еще options можно добавить смотрите в хелпе xcodebuild -help).
Без использования xcode смогли собрать ipa файлы для тестов и на выкладку в app store. Всё это можно легко автоматизировать на CI сервере чтобы облегчить жизнь разработчикам.
P.S.: Стоит отметить, что для процесса компиляции/подписания приложений со сложной структурой, когда несколько таргетов и каждый требует своего отдельного mobileprovision файла (приложения с extension, часами, embedded framework-ками), вышеописанный процесс без напильника работать не будет.
В общем, стоит начать с того, что в моей компании имеется отдел ios разработки в составе > 10 человек, которые работают над пачкой приложений. Для автоматизации рутинной работы нам пришлось развернуть CI сервер (пока самописный, ввиду особых исторических причин (ну как у всех), планируется миграция на jenkins). К рутинным вещам я отношу — сборку проектов, запуск тестов (если, конечно, вы их пишете), создание ипашек для тестеров и для выкладки в app store. В общем, хотелось чтобы по нажатию кнопочки или по хуку в гите всё это начинало работать. Пока у нас всё работает по нажатию кнопки разработчиком, про гит пока только в планах. В данной статье я только затрону тему компиляции проекта и упаковку его в ipa файла. Функционал запуска проектов на девайсах на стороне CI сервера еще находится в стадии разработки, а вот сам процесс упаковки уже давно в «бою», может кому пригодиться.
Что планируем:
- соберем приложение и запустим на симуляторе
- соберем ipa и запустим на реальном девайсе
- соберем ipa для выкладки на маркет
- немного подэбажим ошибки сборки
Имеем:
- OS X 10.10.5
- Xcode 7.1
Для частоты эксперимента будем использовать стороннее приложение, чтобы уважаемые читатели могли повторить, что описано в статье. Используем тестовое приложение для опенсорсного apple-овского framework-а ResearchKit.
git clone https://github.com/ResearchKit/ResearchKit.git ~/Downloads/researchkit
cd ~/Downloads/researchkit
Перед компиляцией проекта необходимо установить apple doc generator github.com/tomaz/appledoc (там есть описание как установить).
Переходим к скаченному проекту. Корневой проект представляет собой сам ResearchKit framework, само тестовое приложение находится в другом месте, переходим туда:
cd samples/ORKCatalog/
Пробуем компилировать приложение. Собираем под симулятор, так как для сборки проекта под симулятор не нужны сертификаты/провижен файлы.
xcodebuild -project ORKCatalog.xcodeproj -scheme ORKCatalog -arch x86_64 VALID_ARCHS=x86_64 -sdk iphonesimulator
Получаем ошибку:
Билд падает, так как нет схемы ORKCatalog. Так как схема в оригинальном проекте не была помечена как shared, то после “слива” из гита, xcode еще ничего не знает об этой схеме, чтобы он ее снегирил, нужно просто открыть проект. Поэтому просто открываем проект:
open ORKCatalog.xcodeproj
И сразу же закрываем, после наша схема появится, проверить схемы можно через xcodebuild -list. Пробуем собрать снова:
xcodebuild -project ORKCatalog.xcodeproj -scheme ORKCatalog -arch x86_64 VALID_ARCHS=x86_64 -sdk iphonesimulator
После видим долгожданное ** BUILD SUCCEEDED **. Отлично, всё работает.
Запускаем проект на симуляторе
Компилируем под симулятор, архитектуры могут быть i386/x86_64. Через SYMROOT задаем путь результата сборки:
xcodebuild -project ORKCatalog.xcodeproj -scheme ORKCatalog ARCHS='x86_64 i386' VALID_ARCHS='x86_64 i386' -sdk iphonesimulator -configuration Debug SYMROOT=$(pwd)/build build
(Мысли вслух: когда писал статью, компиляция в режиме Release работала, перед публикацией статьи еще раз проверил все шаги и компиляция в этом режиме перестала работать, поэтому собираем в Debug, какие-то из последних коммитов это сломали.)
После успешной сборки мы получили ORKCatalog.app файл в build/Debug-iphonesimulator/. Осталось запустить это на симуляторе. Для этого будем использовать ios-sim утилиту github.com/phonegap/ios-sim. Пользоваться ей достаточно просто.
Получаем список доступных симуляторов:
ios-sim showdevicetypes
Из предложенного списка я выбрал 'iPhone-6-Plus'. Запускаем приложение на нем:
ios-sim --devicetypeid iPhone-6-Plus launch build/Debug-iphonesimulator/ORKCatalog.app
Если всё правильно сделали, то должен запуститься симулятор с приложением (для входа в режим ввода в консоли используйте ctrl + C).
Создание ipa файла и запуск на реальном девайсе
Тут немного сложнее, нам нужен mobileprovision файл для разработки (developer) и наличие сертификата на машине (p12 файл), при этом наличие аккаунта в xcode не нужно. При подписании/упаковки приложений из консоли нет необходимости добавлять аккаунт в xcode, это очень помогает, например, на CI сервере можно держать только p12 файлы.
Будем считать, что на вашей машине есть соответствующий сертификат. После генерируем developer mobileprovision через developer.apple.com в вашем аккаунте и скачиваем на машину (дадим ему название test.mobileprovision, а bundle id будет ru.habrahabr.test). После копируем его в директорию, где его сможет подхватить xcode:
cp test.mobileprovision ~/Library/MobileDevice/Provisioning Profiles/
Собираем архив под девайс (это архитектуры arm64/armv7):
xcodebuild -project ORKCatalog.xcodeproj -scheme ORKCatalog ARCHS='arm64 armv7' VALID_ARCHS='arm64 armv7' -sdk iphoneos -configuration Debug archive -archivePath build/archive
Компиляция упадет, так как мы еще не прописали в приложении свой bundle id и не прилинковали mobileprovision, ошибка будет такого вида:
Будем передавать bundle id из консоли + нужно прописать его в Info.plist файле. Также через консоль будем передавать линк на наш mobileprovision. Plist файл находим по пути ORKCatalog/Supporting Files/Info.plist, в котором для ключа CFBundleIdentifier выставляем значение ru.habrahabr.test. Bundle id передаем через ключ со значением нашего ид PRODUCT_BUNDLE_IDENTIFIER=ru.habrahabr.test. Линк на mobileprovision передаем через ключ PROVISIONIG_PROFILE, со значние UUID, который прописан в mobileprovision.
Достаем UUID:
security cms -D -i test.mobileprovision | grep -o "\w\{8\}-\w\{4\}-\w\{4\}-\w\{4\}-\w\{12\}"
Значение будет подобно 87b0df89-793a-4a0f-92bf-c5f9c35f1405. Снова собираем:
xcodebuild -project ORKCatalog.xcodeproj -scheme ORKCatalog ARCHS='arm64 armv7' VALID_ARCHS='arm64 armv7' -sdk iphoneos -configuration Debug archive -archivePath build/archive PRODUCT_BUNDLE_IDENTIFIER=ru.habrahabr.test PROVISIONING_PROFILE=87b0df89-793a-4a0f-92bf-c5f9c35f1405
В итоге получим архив build/archive.xcarchive, который осталось упаковать в ipa. В xcode 7 появился новый метод упаковки, им и воспользуемся. Перед этим создадим конфиг файл options.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>development</string>
<key>uploadSymbols</key>
<false/>
</dict>
</plist>
Пробуем собирать ipa:
xcodebuild -exportArchive -exportOptionsPlist options.plist -archivePath build/archive.xcarchive -exportPath build/dev-ipa/
Сборка падает, по логам можно понять, что что-то с entitlements:
По ошибке ясно, что приложение подписываем entitlements, значения в котором не соответствуют значениям в нашем mobileprovision, а именно com.apple.developer.healthkit. Ищем причину. Смотрим каким entitlements подписано приложение:
codesign -d --entitlements - build/archive.xcarchive/Products/Applications/ORKCatalog.app
Получаем:
<dict>
<key>application-identifier</key>
<string>XXXXX.ru.habrahabr.test</string>
<key>beta-reports-active</key>
<true/>
<key>com.apple.developer.healthkit</key>
<true/>
<key>com.apple.developer.team-identifier</key>
<string>XXXXX</string>
<key>get-task-allow</key>
<false/>
</dict>
</plist>
Видим, что всё ок, за исключением этого:
<key>com.apple.developer.healthkit</key>
<true/>
У нас в mobileprovision нет этой опции, нужно выяснить, откуда это взялось:
find ORKCatalog/ -name "*.entitlements" -type f
Поиск нам выдал ORKCatalog/Supporitng Files/ORKCatalog.entitlements. Смотрим что внутри:
cat ORKCatalog/Supporting Files/ORKCatalog.entitlements
Там только одно значение:
<plist version="1.0">
<dict>
<key>com.apple.developer.healthkit</key>
<true/>
</dict>
</plist>
По логике нужно перезаводить mobileprovision, в который нужно добавить это значение, но мы ленивые и для теста это не обязательно, нам нужно просто переподписать приложение с entitlements без этого значения.
У нас есть как минимум два варианта:
1. Просто редактируем существующий entitlements (ORKCatalog/Supporitng Files/ORKCatalog.entitlements) и снова пересобираем (через archive).
2. Без пересборки, сами переподпишем ORKCatalog.app с нужным entitlements.
Выберем первый вариант как более простой. Поэтому из файла ORKCatalog/Supporitng Files/ORKCatalog.entitlements просто удаляем строки:
<key>com.apple.developer.healthkit</key>
<true/>
и снова пересобираем архив:
xcodebuild -project ORKCatalog.xcodeproj -scheme ORKCatalog ARCHS='arm64 armv7' VALID_ARCHS='arm64 armv7' -sdk iphoneos -configuration Debug archive -archivePath build/archive PRODUCT_BUNDLE_IDENTIFIER=ru.habrahabr.test PROVISIONING_PROFILE=87b0df89-793a-4a0f-92bf-c5f9c35f1405
После создаем ipa:
xcodebuild -exportArchive -exportOptionsPlist options.plist -archivePath build/archive.xcarchive -exportPath build/dev-ipa/
Видим долгожданное сообщение ** EXPORT SUCCEEDED **. Под build/dev-ipa/ появиться ipa файл, который будем устанавливать на девайс. Устанавливать на девайс будем с помощью ios-deploy github.com/phonegap/ios-deploy. Цепляем девайс к машине, получаем id девайса через:
ios-deploy -c
Деплоим на девайс:
ios-deploy -i <device id> -b build/dev-ipa/ORKCatalog.ipa
Собираем ipa файл для маркета
Всё тоже самое как для develop версии, только меняем линк на релизный mobileprovision, в options.plist вместо development проставляем app-store (какие еще options можно добавить смотрите в хелпе xcodebuild -help).
Итог
Без использования xcode смогли собрать ipa файлы для тестов и на выкладку в app store. Всё это можно легко автоматизировать на CI сервере чтобы облегчить жизнь разработчикам.
P.S.: Стоит отметить, что для процесса компиляции/подписания приложений со сложной структурой, когда несколько таргетов и каждый требует своего отдельного mobileprovision файла (приложения с extension, часами, embedded framework-ками), вышеописанный процесс без напильника работать не будет.
KeKc_Tosha
fastlane пробовали?