Подпись работает локально, но падает на CI с ошибкой об отсутствии профиля
CI-задача архивирует приложение и падает на шаге подписи. Та же схема подписывается и запускается на Mac у каждого разработчика, и ни bundle id, ни исходники не менялись.
Ограничения:
- Раннер — чистая headless-машина, в Xcode на ней никто не залогинен.
- Нельзя ослаблять безопасность (не отключать подпись, не коммитить приватные ключи).
Лог сборки показывает:
error: No profiles for 'com.acme.app' were found: Xcode couldn't find any
iOS Distribution provisioning profiles matching 'com.acme.app'.
Automatic signing is disabled and unable to generate a profile.
Определите причину.
CI — чистая машина без login-keychain, поэтому нет ни сертификата, ни профиля, что накопил Mac, а автоподписи нужна сессия Apple ID, которой у CI нет. Чинится ручной подписью: установите их в keychain на раннере через fastlane match.
- ✗Считают, что у CI тот же keychain и профили, что и на Mac разработчика
- ✗Полагаются на автоматическую подпись на headless-раннере без сессии Apple ID
- ✗Отключают подпись кода на CI вместо безопасной установки сертификатов
- →Почему автоматической подписи нужна сессия Apple ID, которой нет у headless-раннера?
- →Как инструмент вроде fastlane match не оставляет сертификат в постоянном keychain раннера?
The failure is environmental, not in the code. A developer's Mac has a login keychain that has quietly accumulated the signing certificate and the downloaded provisioning profile; automatic signing also leans on a live Apple ID session in Xcode. A clean CI runner has neither, so it cannot find (or auto-generate) a profile for com.acme.app.
The fix is explicit, manual signing driven by secrets the runner fetches at build time:
# Create a throwaway keychain and import the distribution certificate (.p12)
security create-keychain -p "$KC_PASS" build.keychain
security import cert.p12 -k build.keychain -P "$P12_PASS" -T /usr/bin/codesign
security list-keychains -s build.keychain
# Install the matching provisioning profile where Xcode looks for it
cp acme_distribution.mobileprovision \
~/Library/MobileDevice/Provisioning\ Profiles/
# Build with manual signing (no Apple ID session required)
xcodebuild -scheme App -archivePath App.xcarchive archive \
CODE_SIGN_STYLE=Manual \
PROVISIONING_PROFILE_SPECIFIER="Acme Distribution"
In practice fastlane match automates exactly this — it stores the certificate and profile encrypted in a private repo and installs them into a temporary keychain per run, so nothing sensitive lives permanently on the runner. Never "fix" it by setting CODE_SIGNING_REQUIRED=NO.