Ключевые выводы
- Создайте отдельный файл связки ключей для CI, импортируйте туда сертификаты дистрибуции и разблокируйте паролем из хранилища секретов — не полагайтесь на связку входа по умолчанию после перезагрузок образа.
- Подписывайте с явным
--keychain/CODE_SIGN_KEYCHAINи проверяйтеsecurity find-identity -v -p codesigningв том же контексте shell, что и сборка. - После
xcodebuild(archive/export) выполняйте notarytool submit → ожидание → stapler staple на внешнем бандле или образе диска, который скачивают пользователи; подшивка только внутреннего бинарника — частая ошибка. - Размер runner’а и поведение очередей пересекаются с выбором hosted и выделенного Mac — для параллельных дорожек полезно сопоставить с матрицей решений Bitrise: облако iOS против собственного облачного Mac (на английском).

codesign -dv, фрагментам журнала нотаризации и наличию билета на отгружаемом артефакте.1. Границы связки ключей на безконсольных хостах
У облачного CI редко есть кто-то у консоли, поэтому всё, что предполагает одобрение в UI или дефолты связки сессии входа, начнёт «плавать» после обновления образа. Стандартизируйте файловую связку (например ~/Library/Keychains/ci-signing.keychain-db), импортируйте сертификат Apple Distribution и закрытый ключ, задайте известный пароль и в начале job выполняйте security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH". Пусть KEYCHAIN_PATH будет единственным источником истины и передайте его в xcodebuild через OTHER_CODE_SIGN_FLAGS=--keychain "$KEYCHAIN_PATH" или эквивалент в проекте. Не смешивайте Developer ID и Apple Distribution в одной цепочке без явной настройки команды и профиля в ExportOptions.plist. Для сетевого контура runner’а (регион, MTU, DNS) см. также WireGuard и сопряжение со шлюзом при трансграничном удалённом доступе: устранение неполадок MTU, асимметричной маршрутизации, разделения DNS и наблюдение за задержками (регион и конфигурация облачного Mac).
2. Слои codesign: entitlements, hardened runtime и вложенные бандлы
Воспроизводимая подпись означает одинаковые хеши списка entitlements и профиля подготовки на каждой сборке. После export запустите codesign --verify --deep --strict на .app и для хелперов в PlugIns/Frameworks убедитесь, что team ID и hardened runtime согласованы. Расхождения часто всплывают как errSecInternalComponent или errSecMissingEntitlement уже на этапе productbuild или notarytool, когда основное приложение казалось подписанным. Для .pkg подписывайте снизу вверх, затем пакет — сертификатом установщика из вашего runbook.
3. Нотаризация и stapler: строгий порядок
Используйте notarytool с API-ключом App Store Connect (issuer, key ID, путь к .p8) вне репозитория — предпочтительно пути из переменных окружения, без коммита ключей; поток только через Transporter оставьте для исключительных случаев. Отправляйте ровно тот бинарник, который оценит Gatekeeper — zip, .app или подготовленный .dmg, дождитесь Accepted, затем xcrun stapler staple <path> и stapler validate. Если подшить до успеха Apple или только внутренний .app, а наружу отдать перепакованный архив, у клиента другие хеши, чем у сервиса нотаризации. Ведите ротацию API-ключей и идентификаторы отправок в changelog; при отказе снимайте JSON через notarytool log <submission-id> и прикладывайте первый rule ID к тикету.
4. Минимальный чеклист конвейера
- Разблокировать связку CI; проверить личности через
security find-identity. - Чистить DerivedData при диагностике устаревших профилей; иначе — инкрементальные сборки с зафиксированными версиями зависимостей.
- Archive/export с зафиксированным
ExportOptions.plist; выгрузку dSYM отделить от нотаризации. - Отправка в Apple; опрос до
Accepted; staple; validate; опубликовать контрольные суммы рядом с артефактами.
Контейнеризованные помощники на том же хосте всё равно требуют корректных монтирований томов и запаса по диску — сочетайте чеклист с Производственный Docker на облачном Mac Apple Silicon: образы arm64/amd64, bind mount и кеш сборки — руководство по устранению неполадок (с границами ресурсов тарифов), если Docker и Xcode делят одну машину.
5. Симптом — причина — действие (подпись и нотаризация)
| Симптом / фрагмент лога | Вероятная причина | Предпочтительное действие |
|---|---|---|
errSecInternalComponent при подписи |
Закрытый ключ недоступен в связке CI; ACL или пароль | Переимпорт ключа и сертификата; unlock в job; явный --keychain |
No signing certificate found |
Неверный порядок поиска связок или нет профиля | Задать CODE_SIGN_KEYCHAIN; проверить UUID в ~/Library/MobileDevice/Provisioning Profiles |
Нотаризация Invalid с hardened runtime |
Entitlement не разрешён для дистрибуции; неподписанный вложенный хелпер | Сверить entitlements с документацией Apple; deep-verify вложенных бандлов |
| Gatekeeper карантинит после «успеха» в CI | Нет stapler на отгружаемом файле; перепакованный zip ломает билет | Staple на внешний артефакт; validate; не перепаковывать после staple |
Ошибки авторизации notarytool |
Неверный issuer / key ID; сдвиг времени; отозванный ключ | Ротация ключа; NTP; JSON-ключ вне репозитория |
6. Заключение
Считайте подпись, нотаризацию и stapler одним автоматом состояний: выход каждого шага — вход следующего. Зафиксируйте в одном runbook путь к связке, UUID профилей и submission ID нотаризации, чтобы дежурный не гадал, какая личность была активна. После стабилизации прогоните холодный старт на свежем образе runner’а — проверка переживания перезагрузок и смены учётных данных.
Почему облачный Mac mini уместен для CI с тяжёлой подписью
Apple Silicon даёт нативные arm64-цепочки без сюрпризов кросс-эмуляции, а unified memory держит Xcode, симуляторы и лёгкие агенты в одном предсказуемом пуле. macOS сочетает Gatekeeper, SIP и защиту уровня FileVault — ниже риск подмены, чем у разрозненных Windows-VM для сборки. У выделенного облачного Mac нет конкуренции за I/O и очередь подписи с соседями по SaaS, а компактный Mac mini на M-серии держит низкое энергопотребление в простое — разумный TCO для круглосуточных runner’ов.
Если нужна стабильная нативная платформа Apple для воспроизводимых конвейеров подписи, облачный Mac mini M4 от kvmboot — практичная отправная точка — тарифы и цены; подберите тариф под размер связки ключей и параллельные lane’ы.