Настройка автоматической сборки iOS-приложения (Build Automation)
Build Automation для iOS — это не просто «добавить xcodebuild в CI». Это управление сертификатами, provisioning profiles, scheme-ами, конфигурациями, build number-ами и артефактами так, чтобы сборка воспроизводилась одинаково на любой машине без ручного вмешательства.
Анатомия iOS-сборки и где всё ломается
Типичная команда сборки:
xcodebuild archive \
-workspace MyApp.xcworkspace \
-scheme MyApp \
-configuration Release \
-destination generic/platform=iOS \
-archivePath build/MyApp.xcarchive \
CODE_SIGN_STYLE=Manual \
PROVISIONING_PROFILE_SPECIFIER="MyApp AdHoc" \
CODE_SIGN_IDENTITY="Apple Distribution: Acme Corp (XXXXXXXXXX)"
Четыре параметра code signing — и каждый может упасть по своей причине. CODE_SIGN_IDENTITY требует точного имени сертификата из Keychain. PROVISIONING_PROFILE_SPECIFIER — точное имя профиля, установленного на машине. На свежем CI-раннере ни того, ни другого нет.
Стратегия решения: fastlane match или ручная установка сертификата + профиля через скрипт (описано ниже).
Ручная установка сертификата без fastlane
Если fastlane match не вписывается в инфраструктуру:
#!/bin/bash
set -euo pipefail
# Создаём временный keychain
KEYCHAIN_NAME="ci-build.keychain"
KEYCHAIN_PASS=$(openssl rand -hex 16)
security create-keychain -p "$KEYCHAIN_PASS" "$KEYCHAIN_NAME"
security set-keychain-settings -lut 7200 "$KEYCHAIN_NAME"
security unlock-keychain -p "$KEYCHAIN_PASS" "$KEYCHAIN_NAME"
security list-keychains -d user -s "$KEYCHAIN_NAME" login.keychain
# Импортируем сертификат
echo "$DISTRIBUTION_CERT_BASE64" | base64 --decode > /tmp/cert.p12
security import /tmp/cert.p12 -k "$KEYCHAIN_NAME" \
-P "$CERT_PASSWORD" -T /usr/bin/codesign -T /usr/bin/xcodebuild
security set-key-partition-list -S apple-tool:,apple: -s -k "$KEYCHAIN_PASS" "$KEYCHAIN_NAME"
# Устанавливаем provisioning profile
PROFILE_PATH="$HOME/Library/MobileDevice/Provisioning Profiles"
mkdir -p "$PROFILE_PATH"
echo "$PROVISIONING_PROFILE_BASE64" | base64 --decode > "/tmp/profile.mobileprovision"
PROFILE_UUID=$(grep -a -A 1 'UUID' /tmp/profile.mobileprovision | grep string | sed 's/.*<string>//;s/<\/string>//')
cp /tmp/profile.mobileprovision "$PROFILE_PATH/$PROFILE_UUID.mobileprovision"
Скрипт параметризован через environment variables — все секреты приходят из CI, не хранятся в скрипте.
xcconfig и управление конфигурациями
Хардкодить Bundle ID, Team ID и Provisioning Profile в .pbxproj — путь к конфликтам при мерже. Лучше — xcconfig файлы:
# Configurations/Release.xcconfig
PRODUCT_BUNDLE_IDENTIFIER = com.acme.myapp
DEVELOPMENT_TEAM = XXXXXXXXXX
PROVISIONING_PROFILE_SPECIFIER = MyApp AppStore
CODE_SIGN_IDENTITY = Apple Distribution
В Xcode: Project → Info → Configurations → указываем xcconfig для каждой конфигурации. На CI передаём PROVISIONING_PROFILE_SPECIFIER через environment, не переписывая xcconfig.
Build Number: автоматизация без коллизий
# Из номера CI pipeline
BUILD_NUMBER=${CI_PIPELINE_IID:-$(git rev-list --count HEAD)}
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $BUILD_NUMBER" MyApp/Info.plist
# Или через agvtool
xcrun agvtool new-version -all $BUILD_NUMBER
agvtool обновляет CFBundleVersion во всех Info.plist проекта, включая Extensions — важно для Watch, Notification Service, Share Extensions.
Export и артефакты
После архивации — экспорт .ipa:
xcodebuild -exportArchive \
-archivePath build/MyApp.xcarchive \
-exportPath build/export \
-exportOptionsPlist ExportOptions.plist
ExportOptions.plist — ключевой файл:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "...">
<plist version="1.0">
<dict>
<key>method</key>
<string>ad-hoc</string>
<key>teamID</key>
<string>XXXXXXXXXX</string>
<key>provisioningProfiles</key>
<dict>
<key>com.acme.myapp</key>
<string>MyApp AdHoc</string>
</dict>
<key>uploadBitcode</key>
<false/>
<key>thinning</key>
<string><none></string>
</dict>
</plist>
Для разных таргетов (основной + Extensions) — добавляем все bundle ID в provisioningProfiles.
Сборка без Xcode: xcode-build-server + LSP
Для команд с CI без лицензионного ограничения на количество macOS-машин: xcode-build-server позволяет использовать xcodebuild из командной строки без открытия Xcode. Полезно на headless Mac mini с минимальной GUI.
Сроки
Базовый скрипт сборки с подписанием (без fastlane): 3–5 дней. Полная автоматизация с xcconfig, автоматическим build number, несколькими таргетами, интеграцией с CI-системой: 1–2 недели. Стоимость рассчитывается индивидуально.







