Почему моя сборка устанавливается на симулятор, но не хочет устанавливаться на физическое устройство? Где найти сертификат разработчика? Какая ещё команда разработки? Эти вопросы рано или поздно возникают у разработчиков и начинающих лидов, сопровождающих релиз или настраивающих CI.
Я — Никита Коробейников, iOS Team Lead в Surf. Вдохновился возможностями Match — утилиты для управления сертификатами и профилями. В статье расскажу о типах сборок и распространения в iOS, какие палки в колеса нашего рабочего локомотива вставляет нам Apple и как нас может выручить утилита с парочкой команд.
Типы сборок
Каждое приложение имеет уникальный идентификатор — bundleID.
В зависимости от целей разработчика одно и то же приложение можно собрать по-разному. Существует два принципиально разных типа сборок:
Development — для отладки.
Distribution — для распространения.
Независимо от типа, сборки подписываются:
Сертификатом (.cert) — он идентифицирует нас с аккаунтом разработчика.
Профилем (.provisioningprofile) — включает информацию, кто и на каких устройствах может запускать приложение.
В зависимости от типа сборок меняются требования и ограничения к используемым сертификатам и профилям.
Development
Отладка на симуляторе не требует никаких сертификатов. Сертификат и профиль потребуются, когда дело дойдёт до сборки на реальное устройство.
По правилам Apple, development-профиль может включать несколько сертификатов и до 100 устройств. Регистрация устройств, создание сертификатов и профилей происходит через портал developer.apple.com. Этот портал служит местом синхронизации команд разработки и следит за уникальностью сертификатов, профилей и идентификаторов приложения.
Distribution
Существует несколько типов распространения.
Далее, говоря о распространении, мы будем подразумевать тип AppStore — как самый распространенный и отличный от development-сборки.
Проблемы при управлении сертификатами и профилями
При использовании портала разработчика Аpple мы сталкиваемся с недружелюбным интерфейсом, который порождает множество проблем.
Часто приходится заходить и выполнять ритуал добавления девайса в профиль: по сути, проставлять галочки на этом экране. Это рутинная операция. А разработчики— не для простановки галочек.
В нашей компании больше двадцати iOS разработчиков и ещё столько же пишут на Flutter. Все они входят в одну команду разработки, зарегистрированную на аккаунте apple.developer.com. Количество сертификатов ограничено: правда, информация об этом противоречивая, и точную цифру назвать сложно.
Некоторые разработчики используют опцию automatically manage codesigning: она автоматически создает и отзывает сертификаты личные сертификаты. Проблема — можно случайно отозвать чужой сертификат, который на самом деле использовался в каком-то профиле. Как следствие, профиль отваливается, сборки перестают собираться.
Сертификат состоит из пары файлов, один из которых — приватный ключ. С портала можно получить только публичный ключ (cer). Приватная часть остаётся у человека, создавшего сертификат. Он может поделиться ею, сделав экспорт ключа в файл с расширением p12.
Так уж получилось, что поиск подходящего ключа для выбранного типа распространения может занять время, если проектов много и их ведут разные разработчики. Если ввести общее хранилище ключей, путаницу можно решить, но хочется автоматизировать процесс поиска.
Итого. При управлении сертификатами и профилями мы:
Тратим время на рутину.
Упираемся в лимиты и периодически видим отозванные сертификаты.
Не всегда знаем, где найти ключ от сертификата.
Match — наш герой
Match позволяет собрать сертификаты и профили одного проекта в едином хранилище: можно решить проблемы при управлении сертификатами и профилями и практически забыть про портал разработчика Apple.
Эта утилита — часть Fastlane, мультиплатформенного инструмента для CI/CD c понятной разработчикам объектно-ориентированной конфигурацией.
Match использует Appstore Connect API через Spaceship-клиент и сохраняет созданные сертификаты в защищённом хранилище. Spaceship представляет оболочку над несколькими документированными и недокументированными API и является частью Fastlane, как и Match.
Конфигурация Match
У Match есть отдельный файлик конфигурации, в котором задаётся связь между хранилищем и аккаунтом команды разработки на портале разработчика Apple. Конфигурация проводится в меньшей степени в Fast-файле, но в большей степени в Match-файле. Создается он командой:
fastlane match init
Файл появится в расположении {your project}/fastlane/Matchfile.
git_url("git@github.com:your_company/ios-certificates.git")
git_branch("project/app/your_project")
clone_branch_directly(true)
storage_mode("git")
type("development") # The default type, can be: appstore, adhoc, enterprise or development
app_identifier(["com.your_project.debug"])
team_id("EFAAG9GXN4")
git_branch — ветка проекта.
clone_branch_directly — флажок для экономии времени клонирования.
storage_mode — тип хранилища. Возможно использовать git-репозиторий, Google Cloud Store или Amazon.
type — тип сборки или распространения.
app_identifier — набор идентификаторов (bundle_id), для которых будут создаваться профили. Их может быть несколько: при условии, что оба идентификатора относятся к одной команде разработки.
team_id — идентификатор команды разработки.
В конфигурации указаны только параметры по умолчанию. Значения можно переопределить при вызове команды.
Если у вас несколько команд разработки — например, заказчик добавил вас в свою команду, с которой будет происходить релиз — вам потребуется два Matchfile. Параметры git_branch, app_identifier, team_id из файла надо будет заменить: сертификаты и профили одной команды разработки не могут лежать рядом с сертификатами другой.
Независимо от типа хранилища, файлы сертификатов и профилей, записываемые в него, будут шифроваться секретным паролем MATCH_PASSWORD. Таким образом обеспечивается защита ваших файлов. Приватные ключи p12 при этом должны быть экспортированы без p12. Match спросит секретный пароль при инициализации, но важно понимать, что каждая ветка или корзинка может иметь свой секретный пароль.
Импорт сертификата и профиля
В случае миграции потребуется команда импорта для добавления существующих сертификатов.
fastlane match import
Эта команда позволит загрузить существующие файлы в репозиторий и зашифрует их. Интересно, что команда не имеет параметра type: при добавлении нового типа распространения необходимо менять тип, указанный в Matchfile.
Импорт профилей можно пропустить. Сертификаты обязательно указать вместе с приватными ключами.
Экспорт сертификата и профиля без изменений
Чтобы достать загруженный сертификат и профиль из хранилища, воспользуемся командой:
fastlane match $(type) --readonly
Параметр type соответствует типу распространения.
В нашем случае мы просто сходим в репозиторий и расшифруем нужные сертификаты и профили. После выполнения команды они уже будут установлены в keychain.
Если убрать флаг readonly, к процессу добавится валидация сертификатов через AppStore Connect API.
Экспорт сертификата с обновлением профиля
Команда обновления пригодится в случаях, когда:
необходимо добавить новое устройство в development-профиль,
срока действия профилей истек,
профили ещё не созданы.
Пример команды:
fastlane match $(type) -u $(user) --force --include_all_certificates
Параметр type соответствует типу распространения.
Параметр user соответствует AppleID аккаунта с доступом в команду разработки.
Команда спросит пароль и 2FA код AppleID, указанного в параметре user. Проверит актуальность сертификата и профиля, лежащих в хранилище, и создаст новый профиль при необходимости.
Флаги force и include_all_certificates инициируют принудительное обновление development-профиля. Они необходимы, потому что количество development-сертификатов в нашей команде больше одного.
К сожалению, нет другого способа гарантировать, что профиль, созданный средствами match, будет содержать тот сертификат, который добавлен в ветку проекта.
Если удалить лишние сертификаты с портала разработчика Apple, необходимость в force пропадет.
Опыт миграции на Match
На момент подготовки статьи мы провели миграцию двух проектов на Match.
Конечно же, не всё прошло гладко. Мы не сразу усвоили, что:
Сертификаты с различными team_id должны лежать в отдельной ветке, иначе будет выбираться неверный сертификат.
Импортировать приватный ключ сертификата надо без пароля, иначе он не будет расшифрован правильно.
Однозначно можно сказать, что проблемы, которые мы испытывали, решены:
Рутинное проставление галочек исчезло.
Проблемы с протухшими или отозванными сертификатами решаются одной командой обновления.
Все разработчики знают, как установить себе нужные для сборки сертификаты на устройство.
Можно было бы сказать, что мы преуспели, но рано радоваться: предстоит большая работа. В наших планах перевести проекты студии на использование Match и почистить development-сертификаты. Мы верим, что с Match это возможно.