Последняя стабильная ветка Flutter с номером 3.44 имеет отдельные официальные примечания к выпуску, а публикация iOS-приложения по-прежнему проходит через macOS, Xcode и цепочку подписи Apple (документация Flutter по выпуску Flutter 3.44.0, инструкция Flutter для iOS). Поэтому ошибка подписи Flutter 3.44 не означает автоматически, что нужно перевыпустить все сертификаты.

Сегодня сначала определите этап сбоя: CLI-сборка, Xcode Build, Archive, экспорт IPA, проверка подписи или загрузка. В течение ближайшей проверки не удаляйте Keychain, не отзывайте сертификат и не очищайте все профили. Если Xcode в графической сессии создаёт Archive, а SSH-команда падает, первым подозреваемым становится не проект, а доступ удалённого процесса к Keychain.

Эта статья предназначена для вас, если вы пишете Flutter-приложение на Windows или Linux, а iOS-релиз выполняете через удалённый Mac. Она также пригодится владельцам старого проекта, где после обновления Flutter 3.44 ломается подпись Runner или отдельного плагина.

01

Сначала разделите ошибку подписи Flutter 3.44 по этапам

В логе нельзя ориентироваться только на последнюю строку codesign failed. Это итоговая команда, а первая полезная ошибка могла появиться раньше — при выборе Team, поиске профиля, создании Archive или доступе к закрытому ключу.

Снимите обезличенный лог. Названия проекта, Bundle ID, Team ID, имя сертификата, UUID профиля, путь, имя пользователя, адрес хоста, токены и пароли замените на такие значения:

[PROJECT_NAME]
[com.example.app]
[TEAM_ID]
[CERTIFICATE_NAME]
[PROFILE_UUID]
[KEYCHAIN_PATH]
[REMOTE_HOST]

Сравните четыре входа:

  • flutter build ios --release;
  • flutter build ipa --release;
  • Archive через Xcode с тем же Scheme;
  • тот же flutter build ipa через SSH или задачу CI.

Команда flutter build ipa относится к созданию релизного пакета, но сама по себе не доказывает корректность дальнейшего распространения. Согласно официальной документации Flutter по iOS-сборке, после подготовки iOS-проекта остаются этапы подписи, Archive и отправки приложения средствами Xcode.

Зафиксируйте для каждого запуска:

  • первый текст ошибки;
  • Target, на котором произошёл сбой;
  • Build Configuration — обычно Release;
  • Scheme и фактическую команду;
  • появился ли .xcarchive;
  • содержит ли Archive подписанное приложение;
  • удалось ли экспортировать IPA;
  • прошла ли проверка IPA;
  • прошла ли загрузка в App Store Connect.

Если графический Archive и SSH-сборка используют разные Scheme или разные переменные окружения, сравнивать их рано. Сначала добейтесь идентичного входа.

Важно. Не проверяйте только Runner. Уведомления, Widget, Share Extension и другие вложенные Target могут иметь собственные Bundle ID, профили и entitlements. Успешная сборка основного приложения не подтверждает готовность всего продукта.

02

Первый слой: Team, Bundle ID и режим подписи

Откройте iOS-проект Flutter в Xcode и проверьте Runner. Нужны не только выбранная команда, но и соответствие трёх объектов:

  • Team в настройках Target;
  • Bundle ID в проекте;
  • App ID в аккаунте разработчика.

Затем проверьте Scheme и конфигурацию Release. Частая ошибка возникает, когда Debug использует автоматическую подпись, а Release получает ручные параметры из старого файла конфигурации. Ещё один вариант — Xcode показывает один Target, а команда Flutter фактически строит другой Scheme.

У автоматической и ручной подписи разные точки контроля. В автоматическом режиме Xcode может подобрать или обновить профиль. В ручном режиме проект должен ссылаться на конкретные сертификаты и Provisioning Profile. Смешивать эти режимы можно только осознанно: например, локальная отладка автоматизирована, а релизный ExportOptions и профиль закреплены явно.

Проверьте такие места:

  1. Runner → Signing & Capabilities.
  2. Настройки проекта и Target для Release.
  3. ios/Flutter/Generated.xcconfig и пользовательские .xcconfig.
  4. Scheme → Archive → Build Configuration.
  5. Параметры команды, передаваемые в CI или SSH.
  6. Настройки расширений и плагинов.

Минимальный критерий прохождения этого слоя — не зелёный Debug Build. Один и тот же коммит должен создавать Release Archive с ожидаемым Team и Bundle ID. Если flutter build ipa сообщает о необходимости выбрать Development Team, ищите Target, для которого команда не определена или определяется противоречивыми настройками.

03

Второй слой: сертификат, закрытый ключ и Provisioning Profile

Сертификат — это не вся идентичность подписи. Для успешного codesign нужны как минимум сертификат, соответствующий ему закрытый ключ в Keychain, и профиль, разрешающий подпись конкретного приложения.

Apple отдельно описывает назначение сертификатов в обзоре типов сертификатов. Перед изменениями определите:

  • какой тип распространения нужен для текущего сценария;
  • присутствует ли сертификат в нужном Keychain;
  • есть ли у него соответствующий закрытый ключ;
  • не истёк ли сертификат;
  • совпадает ли Team;
  • соответствует ли Profile нужному App ID;
  • разрешает ли Profile требуемые capabilities.

Профиль нельзя рассматривать как обычный файл настроек. В техническом описании Provisioning Profile Apple связывает его с идентификатором приложения, сертификатами и разрешениями. Поэтому импорт одного .cer часто не решает проблему: в нём нет закрытого ключа.

До отзыва, удаления или пересоздания активов сохраните исходные файлы и запишите, какие сборочные машины ими пользуются. Отзыв сертификата может остановить другой Mac и уже запущенные задачи публикации. Удаление профиля может сломать воспроизводимую сборку, которая ещё не перенесена на новый профиль.

Для диагностики на машине с разрешённым доступом используйте чтение, а не разрушительные операции:

security find-identity -v -p codesigning [KEYCHAIN_PATH]
security list-keychains
security default-keychain

Путь к Keychain оставляйте обезличенным в документации. Не добавляйте в команду пароль, токен или приватный ключ. Если identity отсутствует, переходите к восстановлению ключевой пары. Если identity есть, но выбранный Profile не совпадает по App ID или типу распространения, исправляйте пару «профиль — Target», а не весь проект.

04

Третий слой: Target, плагины и entitlements

Flutter-приложение может включать больше одного подписываемого объекта. Помимо Runner, проверьте уведомления, Widget, Share Extension и нативные части плагинов. У каждого Target могут быть собственные:

  • Bundle ID;
  • Team;
  • Signing Style;
  • Provisioning Profile;
  • capabilities;
  • entitlements.

Одинаковый Team не означает одинаковый Bundle ID или одинаковый профиль. Runner и расширение должны быть согласованы с точки зрения команды и разрешённых возможностей, но профиль расширения может быть отдельным.

После Archive откройте содержимое .xcarchive, а не только исходные настройки Xcode. Найдите фактически подписанные приложения и расширения. Для каждого объекта сопоставьте:

  • Bundle ID в продукте;
  • entitlements;
  • выбранный профиль;
  • capability, например Push Notifications или App Groups;
  • тип сборки и назначения распространения.

Apple описывает назначение entitlements в официальной документации по entitlements. Если IPA создан, но проверка сообщает о несовпадении разрешений, это не косметическая ошибка. Подписанное приложение заявляет набор возможностей, а профиль должен разрешать тот же набор.

Для чтения результата используйте копию Archive:

codesign -d --entitlements :- [ARCHIVE_APP_PATH]
security cms -D -i [PROFILE_PATH]

Эти команды не меняют проект. Сравнивайте вывод по фактическому приложению внутри Archive. Проверка только файла .entitlements в исходниках может дать ложное чувство безопасности: итоговые настройки формируются на этапе сборки и подписи.

05

Сравнение входов: проект или удалённая среда

В середине диагностики удобно разделить проблему по наблюдаемым результатам:

Проверка Графическая сессия SSH или CI Вероятный слой
Release Archive создаётся не создаётся Keychain, Scheme или переменные среды
Archive создаётся да да переход к IPA и проверке
IPA экспортируется да нет ExportOptions, профиль или доступ к ключу
IPA проверяется да нет entitlements, Target или профиль
Загрузка проходит да нет учётные данные, сеть или параметры загрузки

Эта таблица не заменяет журнал команд. Она помогает не перепутать ошибку проекта с ошибкой сеанса. Если одинаковый коммит и одинаковый Scheme проходят через Xcode, а SSH падает до создания Archive, сначала сравните окружение. Если оба запуска останавливаются на одном Target и одном Bundle ID, ремонтируйте проект или подписывающие активы.

06

Четвёртый слой: Keychain и права SSH на удалённом Mac

Удалённая машина может быть полностью рабочей в графическом сеансе и непригодной для бездействующей задачи. Причина — разные пользовательские контексты. GUI видит разблокированный Keychain и ранее выданное разрешение на закрытый ключ. SSH-процесс может использовать другой default Keychain, закрытый набор ключей или ограниченный доступ.

Проверьте в обоих режимах:

  1. имя пользователя и домашний каталог;
  2. default Keychain;
  3. список подключённых Keychain;
  4. состояние блокировки;
  5. наличие signing identity;
  6. доступ процесса к закрытому ключу;
  7. переменные PATH, HOME и параметры Scheme;
  8. рабочий каталог Flutter-проекта.

Сначала выполните безопасную диагностическую команду через графический терминал. Затем повторите её через SSH. Не копируйте секреты в командную строку и не выводите содержимое приватного ключа.

Если проблема подтверждена, выбирайте минимальное изменение:

  • подключить правильный Keychain к сеансу задачи;
  • разблокировать только нужный Keychain на время операции;
  • разрешить доступ только конкретному процессу подписи;
  • изолировать секреты от обычных задач;
  • проверить результат после закрытия SSH-сеанса.

Apple публикует отдельные материалы по проблемам кода подписи, включая сценарии с удалённым выполнением, в разделе поддержки тем code signing. Не используйте универсальный сброс Keychain как первый шаг. Он может удалить рабочие ключи, нарушить другие приложения и усложнить восстановление.

Если вы арендуете удалённый Mac для Flutter-сборки, заранее уточните права пользователя, постоянство Keychain после перезапуска и возможность безопасно выполнять SSH-задачи. Описание вариантов аренды Mac mini полезно использовать только после проверки самой цепочки подписи, а не вместо неё.

07

Пошаговое восстановление публикации

Первый шаг: сохраните исходное состояние

Скопируйте проект, .xcarchive, экспортированные настройки и обезличенный лог. Зафиксируйте коммит, версию Flutter, версию Xcode, Scheme и конфигурацию. Не очищайте DerivedData, профили и Keychain одновременно: иначе вы потеряете точку сравнения.

Второй шаг: подтвердите GUI-сборку

В Xcode выберите тот же Scheme и Release, который использует CLI. Создайте Archive. Если он не создаётся, удалённый доступ пока не является главным подозреваемым.

Третий шаг: проверьте все Target

Составьте список Runner, расширений и нативных целей. Для каждого запишите Bundle ID, Team, профиль и entitlements. Исправляйте только тот Target, где найдено несоответствие.

Четвёртый шаг: проверьте identity

Убедитесь, что сертификат и закрытый ключ находятся в одном доступном Keychain. Сопоставьте профиль с App ID, Team, типом распространения и capabilities. Не отзывайте активный сертификат, пока не проверили влияние на другие машины.

Пятый шаг: повторите CLI локально

Запустите flutter build ipa --release в графическом терминале удалённого Mac. Проверьте появление Archive и содержимое продукта. Если CLI проходит, а SSH нет, переходите к сеансу и Keychain.

Шестой шаг: исправьте неинтерактивный доступ

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

Седьмой шаг: экспортируйте и проверьте IPA

Создайте IPA из Archive. Затем проверьте подпись, entitlements и соответствие профиля фактическому приложению и вложенным Target. Сам факт появления файла IPA не равен готовности к публикации.

Восьмой шаг: выполните реальную загрузку

Используйте штатный процесс App Store Connect. Зафиксируйте результат проверки и загрузки. Не заменяйте эту проверку локальным успешным codesign: публикационный этап может выявить другой набор разрешений или неверный тип профиля. Apple описывает порядок Archive и распространения в руководстве по бета-тестированию и релизам, а требования подготовки — в документе о подготовке приложения к распространению.

08

Решение по результатам диагностики

Используйте следующие условия, а не общее «пересоздать сертификаты»:

  • Если GUI и SSH ломаются на одном Target, исправляйте Team, Bundle ID, Profile или entitlements проекта.
  • Если GUI проходит, а SSH не видит identity, восстанавливайте Keychain и права неинтерактивного процесса.
  • Если Runner проходит, а расширение нет, создайте отдельную проверку Bundle ID, профиля и capabilities расширения.
  • Если Archive создаётся, но IPA не экспортируется, проверяйте ExportOptions, тип распространения и фактическую пару сертификат — профиль.
  • Если IPA экспортируется, но загрузка не проходит, повторно проверьте подпись и параметры распространения, а затем учётные данные загрузчика.
  • Если после перезапуска всё ломается, среда не готова для постоянного iOS-буилда: сначала исправьте сохранение Keychain и восстановимость, затем переносите туда CI.

Три итоговых решения должны быть разными. Исправляйте проект, если ошибка воспроизводится независимо от сеанса. Пересобирайте подписывающую среду, если активы неполны или повреждены. Меняйте macOS-среду, если права, постоянство Keychain и доступ к удалённой задаче нельзя обеспечить безопасно.

Последняя проверка материала выполнена 7 сентября 2026 года по официальным документам Flutter о выпуске 3.44 и iOS-сборке, а также по материалам Apple о сертификатах, Provisioning Profile, entitlements и распространении. При изменении версии Flutter, Xcode или интерфейса публикации этот порядок нужно сверить заново.

09

Частые вопросы

Почему flutter build ipa просит выбрать Development Team?

Обычно фактический Runner или другой Target не получает Team в Release-конфигурации. Проверьте Scheme, настройки Target, .xcconfig и Bundle ID. Если в Debug работает автоматическая подпись, а Release использует ручную, параметры могут конфликтовать. Сначала сохраните проект, затем устраните одну причину и повторите сборку, не пересоздавая все профили сразу.

Почему Flutter собирается в Xcode, но падает через SSH?

Графическая сессия и SSH могут видеть разные Keychain, пользователя и переменные окружения. Сравните default Keychain, signing identity, домашний каталог и состояние блокировки. Если Archive в Xcode создаётся, а SSH останавливается до него, начинайте с доступа процесса к закрытому ключу. Не передавайте секреты в аргументах команды или обычном логе.

Нужны ли Runner и Target плагинам одинаковые настройки?

Им нужна согласованность по Team, режиму Release и разрешённым возможностям, но Bundle ID и Provisioning Profile у расширений могут отличаться. Проверяйте каждый Target отдельно. Скопированные настройки Runner иногда придают расширению неправильный App ID или entitlements. Окончательным доказательством служит содержимое подписанного Archive, а не только экран Xcode.

Что делать при несовпадении entitlements в готовом IPA?

Извлеките entitlements из подписанного приложения и сравните их с профилем, capabilities и настройками соответствующего Target. Исправьте источник расхождения, заново создайте Archive и экспортируйте IPA. Затем повторите проверку и загрузку. Если оставить уже созданный IPA, последующая публикация не станет корректной только потому, что файл существует.

10

Что выбрать для постоянной сборки

Если текущий компьютер справляется с Xcode, но не может круглосуточно выполнять iOS-релизы, удалённый Mac разумно сначала использовать как проверяемую среду. Перенесите тот же коммит, повторите Archive, экспорт IPA и загрузку, затем перезапустите задачу после разрыва SSH. Только воспроизводимый результат оправдывает переход к постоянному Flutter-буилду.

Собственный Mac удобнее, если вам нужны физические устройства, локальные кабели, длительная тяжёлая работа и полный контроль над хранением ключей. Однако отдельный компьютер для нерегулярных релизов создаёт расходы на оборудование, обслуживание и постоянное состояние окружения. Облачная сборка может быть удобной, но её ограничения по интерактивному Xcode-доступу, Keychain и отладке нужно оценивать отдельно.

Аренда Mac подходит лучше, когда вам нужен реальный macOS-хост на период миграции, релиза или настройки CI. В этом сценарии вы проверяете не рекламное обещание, а конкретные свойства: root-доступ, SSH, графический сеанс, сохранение Keychain, перезапуск и повторяемость подписи. Для разовой проверки можно начать с заказа Mac mini в VpsMesh, а затем решить, нужен ли вам постоянный или временный срок.

Если после этого проекта вам требуется только периодическая публикация, аренда VpsMesh может быть практичнее покупки отдельного Mac: вы получаете удалённую среду именно на время проверки или релизного цикла. Но для постоянной нагрузки, физического тестирования устройств и долгого хранения чувствительных ключей сначала сравните аренду с собственным оборудованием. Решение принимайте после успешного прохождения полной цепочки — Release Archive, IPA, проверка подписи и реальная загрузка.