Ошибка экспорта IPA в xcodebuild: как исправить на удалённом Mac в 2026 году?
📋 Содержание
Если экспорт IPA в xcodebuild завершился ошибкой, не пересобирайте проект сразу: зафиксируйте тот же xcarchive, сравните экспорт через Xcode и командную строку, а затем проверьте ExportOptions.plist, подпись, профили и права удалённой сессии. Этот порядок подходит, если Archive показывает успешный результат, но каталог вывода пуст или содержит только промежуточные файлы.
Эта статья предназначена для вас, если вы запускаете сборку через SSH или CI, используете удалённый Mac как постоянный упаковочный узел и хотите различать Build, Archive, Export и Upload. Она также полезна небольшой команде, которой нужно сократить время выпуска, не маскируя ошибку повторной компиляцией.
Сначала разделите четыре этапа, а не ищите причину в последнем коде
Обезличенный симптом обычно выглядит так: Archive отображается как успешный, команда завершается, но IPA в каталоге нет. Это не противоречие. archive создаёт пакет xcarchive, а exportArchive использует его как входной файл и выполняет отдельные проверки распространения, подписи и профилей. Загрузка в App Store Connect — ещё один этап, который начинается только после получения корректного IPA.
Apple описывает процесс распространения приложения как последовательность операций, где архивирование и подготовка сборки к распространению имеют разные задачи. Это подтверждается в официальном описании распространения приложений в Xcode.
Зафиксируйте исходные условия до исправлений:
- полный вывод
xcodebuild, включая предупреждения; - журнал Distribution из графического интерфейса, если он создаётся;
- точный путь к
xcarchive; - активный каталог разработчика;
- используемый Scheme и Configuration;
- цель распространения;
- путь к
ExportOptions.plist; - пользователя, от имени которого выполняется SSH или CI-задача.
Пример команды для повторяемого запуска:
xcodebuild \
-exportArchive \
-archivePath "/путь/к/Release.xcarchive" \
-exportOptionsPlist "/путь/к/ExportOptions.plist" \
-exportPath "/путь/к/export"
Не заменяйте путь к архиву новым результатом сборки, пока не закончите диагностику. Если после изменения проекта ошибка исчезнет, вы уже не сможете уверенно сказать, исправили ли вы экспорт или случайно изменили входные данные.
Первый полезный вывод — это не exit code, а ближайшее содержательное сообщение перед ним. Ищите упоминания отсутствующего профиля, неподходящего способа распространения, закрытого ключа, Entitlements, Bundle ID, прав каталога и невозможности прочитать файл.
Сопоставьте архив и конфигурацию: успешное Archive ещё не даёт права на экспорт
Проверка xcarchive должна отвечать на вопрос: содержит ли архив всё, что требуется для выбранного способа распространения. Просмотрите пакет и убедитесь, что внутри есть ожидаемое приложение, расширения, встроенные Framework и Info.plist. Для многоцелевого проекта отдельно проверьте App Clip и каждое расширение, если они используются.
Проверьте следующие признаки:
- архив создан нужным Scheme;
- использовалась конфигурация, предназначенная для распространения;
- целью был универсальный iOS-агрегат, а не симулятор;
- основной Bundle ID совпадает с ожидаемым;
- Bundle ID расширений не потерялись и не были заменены;
- вложенный код находится в корректной структуре;
- в архиве присутствуют сведения о подписи и Entitlements.
Через Organizer можно выполнить Validate или перейти к подготовке распространения. Это не заменяет командную проверку, но даёт полезную точку сравнения. Если графический интерфейс не может обработать тот же архив, проблема, вероятно, находится во входном архиве, его подписи или связанных профилях. Если интерфейс экспортирует IPA, а SSH-команда нет, переходите к проверке окружения и параметров.
Не удаляйте архив на этом этапе. До успешной проверки сохраните его в неизменном виде, а логи и промежуточные результаты вынесите в отдельный каталог. Пути, имя проекта, Bundle ID, Team ID, UUID профиля и имя пользователя в отчёте замените на обезличенные значения.
Сравните цель распространения и ExportOptions.plist
ExportOptions.plist описывает не «любую сборку», а конкретный сценарий распространения. Тестовая установка на зарегистрированные устройства, публикация через App Store Connect и другие варианты требуют разных условий. Нельзя считать один старый plist универсальным только потому, что он раньше сработал.
Начните с успешного графического экспорта. Сохраните полученный файл настроек, если среда позволяет его получить, и сравните его с тем, что использует скрипт. Затем выполните на целевом Mac:
xcodebuild -help
Именно этот вывод, а не случайный шаблон из старого репозитория, должен быть исходной точкой для поддерживаемых параметров. Состав ключей и допустимые значения необходимо сверять с установленной версией Xcode и актуальной документацией Apple.
Проверяйте по отдельности:
method— соответствует ли он реальной цели;- настройки автоматического или ручного управления подписью;
teamIDили другое поле команды, если оно поддерживается текущим окружением;- сопоставление профиля с каждым Bundle ID;
- каталог экспорта и права на его запись;
- отсутствие устаревших или случайно скопированных ключей.
Не меняйте сразу все параметры. Сначала создайте копию plist, измените один спорный блок, повторите экспорт того же xcarchive и сравните лог. Так вы сохраните причинно-следственную связь.
Важно: если параметр найден в старом примере, но отсутствует в выводе
xcodebuild -helpна целевом Mac, не делайте вывод, что его значение просто нужно подобрать. Сначала подтвердите поддержку ключа для этой версии Xcode и выбранного сценария распространения.
Официальное описание типов сертификатов помогает связать способ распространения с нужным видом подписи; сверяйтесь с обзором сертификатов в Apple Developer Account, а не с названием файла сертификата в локальной папке.
Проверьте цепочку подписи: сертификат без закрытого ключа не решает проблему
На этапе экспорта проверяется не только наличие сертификата. Для подписи требуется связка, в которой доступны идентичность подписи и соответствующий закрытый ключ. Если на удалённый Mac импортировали только файл сертификата, экспорт может завершиться отказом, хотя в Keychain видна знакомая строка.
Проверяйте отдельно:
- сертификат, срок его действия и назначение;
- наличие соответствующего закрытого ключа;
- доступ текущего пользователя к Keychain;
- выбранную signing identity;
- Provisioning Profile для основного приложения;
- профили для расширений и других вложенных целей;
- Entitlements внутри архива и разрешения, заявленные профилем.
Профиль должен соответствовать Bundle ID, команде и возможностям приложения. Для ручной подписи сопоставьте каждый Bundle ID с конкретным профилем в ExportOptions.plist. Для автоматической подписи проверьте, может ли текущая сессия получить или использовать нужный ресурс без интерактивного подтверждения.
Apple отдельно описывает операции редактирования, загрузки и удаления профилей в справке по Provisioning Profiles. Используйте удаление только после сохранения действующего профиля и понимания того, какие цели проекта от него зависят.
Изменения с высоким риском нельзя выполнять первым шагом:
- удалять все профили;
- отзывать сертификат;
- менять права Keychain;
- очищать архив;
- пересоздавать всю цепочку подписи.
Перед такой операцией экспортируйте резервные материалы, запишите исходное состояние и определите обратный путь. Отзыв сертификата может затронуть другие проекты и рабочие машины, а удаление профиля может сломать параллельный выпуск.
Сравните Entitlements основного приложения с Entitlements расширений и встроенного кода. Несовпадение может быть незаметно при архивировании, но проявиться при подготовке финального пакета. Особенно внимательно проверяйте capabilities, связанные с группами приложений, push-уведомлениями, связкой ключей и другими разрешениями.
Сопоставьте графическую сессию и SSH: удалённый Mac должен экспортировать без ручной помощи
Когда локальный интерфейс экспортирует IPA, а команда через SSH возвращает ошибку, рассматривайте это как различие окружений, а не как доказательство того, что xcodebuild работает нестабильно. Графический вход и автоматическая задача могут использовать разные:
- macOS-пользователи;
- Keychain и состояние его разблокировки;
DEVELOPER_DIR;- рабочие каталоги;
- переменные окружения;
- права на временные и выходные файлы;
- сетевые учётные данные;
- разрешения на использование закрытого ключа.
Сначала в интерактивной и SSH-сессии выведите диагностические значения:
whoami
pwd
xcode-select -p
echo "$DEVELOPER_DIR"
env | sort
Не публикуйте этот вывод без маскировки путей, имён пользователей и идентификаторов команды. Затем выполните один и тот же экспорт с неизменным xcarchive, plist и каталогом результата. Если графическая сессия использует другую версию инструментов или другой DEVELOPER_DIR, результаты нельзя сравнивать напрямую.
Проверьте права каталога:
ls -ld "/путь/к/export"
ls -ld "/путь/к/архиву"
Убедитесь, что пользователь CI может читать архив, обращаться к нужному Keychain и создавать файлы в каталоге экспорта. Не исправляйте проблему бездумным расширением прав на всю систему. Меняйте разрешения только для конкретного пользователя и конкретных каталогов, фиксируя исходное состояние.
Если задача обрывается после перезапуска или разрыва SSH, проверьте, сохраняются ли:
- полный терминальный вывод;
- журнал Distribution;
- временный каталог экспорта;
- копия исходного
xcarchive; - код завершения;
- сведения о версии Xcode и активном разработческом каталоге.
Удалённая машина готова к автоматическому экспорту только тогда, когда процесс не требует открытия интерфейса, ручного разблокирования Keychain или выбора профиля в диалоговом окне.
Оцените варианты исправления по риску и повторяемости
| Вариант проверки | Что подтверждает | Сильная сторона | Ограничение | Следующее действие |
|---|---|---|---|---|
Повторный экспорт того же xcarchive в Xcode |
Архив, подпись и цель могут быть обработаны графически | Быстро отделяет проблему входных данных от SSH-среды | Не доказывает готовность CI | Сравнить настройки и журналы |
Повторный экспорт через xcodebuild в интерактивном терминале |
Командные параметры и plist согласованы | Сохраняется тот же инструментальный путь | Сессия может иметь ручной доступ к Keychain | Запустить под тем же пользователем |
| Экспорт через SSH с тем же пользователем | Среда пригодна для удалённой задачи | Ближе всего к реальному CI-сценарию | Ошибки прав и путей могут скрываться в оболочке | Зафиксировать окружение и логи |
| Экспорт с ручным сопоставлением профилей | Bundle ID и профили соответствуют друг другу | Даёт прозрачную диагностику | Требует аккуратного обслуживания ресурсов | Сохранить рабочий plist |
| Экспорт после пересоздания всей подписи | Ресурсы созданы заново | Иногда устраняет повреждённое состояние | Может сломать другие приложения и не объяснить первопричину | Использовать только после резервирования |
По этой матрице выбирайте минимальное изменение. Если тот же архив не экспортируется в графическом интерфейсе, не тратьте время на настройку SSH. Если графический экспорт успешен, а интерактивный xcodebuild нет, сравнивайте plist и выбранный каталог. Если интерактивная команда работает, а SSH нет, проверяйте пользователя, Keychain, DEVELOPER_DIR и права.
Для команд, которым нужен постоянный узел, полезно заранее изучить разницу между выделенным Mac и виртуализацией macOS. Это не заменяет проверку проекта, но помогает оценить, связана ли нестабильность с особенностями среды, а не только с кодом приложения.
FAQ: что проверить перед повторным запуском
Почему Archive отображается успешным, но IPA отсутствует?
Archive подтверждает создание xcarchive, но не успешность последующего Export. Проверьте, что архив содержит приложение и все расширения, затем повторите экспорт без новой сборки. После этого сравните ExportOptions.plist, способ распространения, профили, Entitlements, доступ к закрытому ключу и права каталога. Отсутствие IPA — это сигнал проверить следующий этап, а не автоматически запускать Build заново.
Как интерпретировать exit code 70?
Не используйте exit code 70 как название конкретной неисправности. Он лишь сообщает, что операция завершилась неуспешно; полезная причина обычно находится выше в полном логе. Сохраните вывод xcodebuild, журнал Distribution и параметры запуска. Затем повторите тот же экспорт в графической и SSH-сессии. Так вы определите, относится ли сбой к архиву, конфигурации подписи или удалённому окружению.
Как сопоставить настройки ExportOptions.plist?
Сначала определите фактическую цель: тестирование, распространение зарегистрированным устройствам или публикация через App Store Connect. Затем сравните plist с успешным графическим экспортом и выводом xcodebuild -help на целевом Mac. Проверьте способ подписи, команду, профили всех Bundle ID и путь вывода. Не добавляйте ключи из старых шаблонов без подтверждения их поддержки в текущей версии Xcode.
Почему локальный экспорт работает, а SSH-задача нет?
Локальная и SSH-сессия могут запускаться от разных пользователей и обращаться к разным Keychain, каталогам и инструментам разработки. Выполните диагностику whoami, pwd, xcode-select -p и echo "$DEVELOPER_DIR" в обеих сессиях. Затем запустите один и тот же экспорт с одним архивом. Если проблема возникает только в SSH, не пересобирайте проект — исправляйте доступ к ключу, пути, окружение и сохранение журналов.
Как доказать готовность удалённой машины?
Нужно выполнить полный повторяемый тест без ручных действий: взять зафиксированный xcarchive, экспортировать его целевым способом, проверить IPA, сохранить логи и повторить задачу после перезапуска. Отдельно убедитесь, что процесс использует нужного пользователя, видит закрытый ключ и записывает результат в разрешённый каталог. Если требуется открыть Xcode или подтвердить доступ в диалоговом окне, автоматический режим ещё не принят.
Приёмка удалённого Mac по измеримым признакам
После исправления не ограничивайтесь сообщением «команда завершилась без ошибки». Составьте короткий акт приёмки:
- один и тот же
xcarchiveэкспортируется без повторной сборки; - графическая и командная операции используют одну цель распространения;
ExportOptions.plistсоответствует текущему выводуxcodebuild -help;- основной Bundle ID и все вложенные цели сопоставлены с профилями;
- сертификат и закрытый ключ доступны нужному пользователю;
- Entitlements архива согласуются с разрешениями профилей;
- SSH-задача не зависит от открытого окна Xcode;
- каталог вывода доступен для записи;
- полный лог сохраняется после завершения;
- после перезапуска машины диагностические материалы не исчезают.
Проверяйте состав IPA, а не только его наличие. Убедитесь, что пакет содержит ожидаемое приложение и вложенные компоненты, а его имя и расположение соответствуют следующему этапу конвейера. Загрузку в App Store Connect диагностируйте отдельно: Apple публикует самостоятельные статусы обработки загруженных сборок в справке о статусах загрузки. Успешный экспорт ещё не означает, что загрузка и обработка завершились.
Для последующей автоматизации разделите секреты, журналы и артефакты. IPA и xcarchive нельзя хранить рядом с открытыми ключами или незамаскированными переменными CI. Проверьте также, что роль учётной записи имеет необходимые разрешения для операций App Store Connect; соответствующие границы описаны в таблице ролей и разрешений.
Если проект только создаётся в App Store Connect, запись приложения и Bundle ID должны быть согласованы до попытки публикации. В официальной инструкции по созданию записи приложения проверьте требования к идентификатору и данным приложения.
Для дальнейшего обслуживания удалённого узла используйте отдельную процедуру: настройка и приёмка среды для iOS-сборки на удалённом Mac. Она полезна, когда проблема уже исправлена, но вы хотите подтвердить стабильность машины до подключения постоянного CI.
Когда менять машину, а не продолжать править проект
Если один и тот же архив успешно экспортируется в графической сессии, но регулярно не проходит через SSH после проверки пользователя, Keychain, plist и путей, источник проблемы может находиться в текущем окружении. Это особенно вероятно, когда машина очищает рабочие каталоги, теряет доступ к закрытому ключу после перезапуска или требует ручного подтверждения при каждом выпуске.
В таком случае не переносите проблему на новый проект. Возьмите зафиксированный xcarchive и проверьте его на отдельном удалённом Mac по тем же критериям: графический экспорт, командный экспорт, SSH-запуск, проверка IPA, сохранение логов и восстановление после перезапуска. Сравнивайте не обещанную производительность, а факт прохождения одинакового сценария.
Покупка собственного Mac оправдана, если вам нужны постоянная физическая доступность, локальные USB-устройства, долгий стабильный срок эксплуатации и полный контроль над оборудованием. Но отдельная машина требует первоначальных затрат, обслуживания, обновлений, резервного доступа и самостоятельного восстановления Keychain. Облачная виртуализация может добавить ограничения по вложенной системе, графическому доступу или совместимости инструментов.
Если задача — временно проверить выпуск, подготовить CI или получить постоянный Mac без покупки оборудования, аренда MacDate позволяет сначала принять среду по вашему архиву и сценарию. Это практичнее, чем переносить неподтверждённый plist и сертификаты вслепую: сначала добейтесь повторяемого экспорта через SSH, затем решайте, подходит ли узел для длительной работы.
Начните с одного обезличенного xcarchive, сохраните исходные логи и примените критерии из этой статьи. Если текущая машина проходит только ручной графический экспорт, не называйте её готовым iOS-сервером сборки; сначала проверьте независимый удалённый Mac в том же режиме и только после успешной приёмки подключайте его к постоянному конвейеру.