Налаштування CI/CD для iOS-додатків через Fastlane
Fastlane — де-факто стандарт автоматизації iOS-сборок поза екосистемою Apple. Він працює на будь-якому CI: GitHub Actions, GitLab CI, Jenkins, Bitrise. Основна перевага перед Xcode Cloud—повний контроль над кожним кроком та можливість запустити ту саму команду локально, що запустить CI.
Чому кодування підписей—головна проблема
iOS вимагає валідний provisioning profile та сертифікат для будь-якої сборки не в симуляторі. На локальній машині вони є в Keychain. На CI-сервері—немає. Без правильної настройки fastlane кожна сборка закінчується помилкою Code Signing Error: No profiles for bundle ID found.
fastlane match вирішує це: всі сертифікати та профіли зберігаються зашифровані в окремому Git-репозиторії (або S3/Google Cloud). На CI—одна команда fastlane match adhoc завантажує та встановлює потрібний профіль. Passphrase до match—секретна змінна CI.
Робочий процес match:
Git-репозиторій (зашифрований) ←→ fastlane match ←→ Apple Developer Portal
↓
CI Keychain (тимчасовий)
Для команд з 5+ розробників, match у readonly-режимі на CI та в звичайному режимі на машинах розробників—стандартна конфігурація.
Структура Fastfile
default_platform(:ios)
platform :ios do
before_all do
setup_ci if ENV['CI'] # Створює тимчасовий Keychain на CI
end
lane :test do
run_tests(
scheme: "MyApp",
devices: ["iPhone 15", "iPhone SE (3rd generation)"],
code_coverage: true
)
end
lane :beta do
match(type: "adhoc", readonly: true)
increment_build_number(
build_number: ENV["CI_PIPELINE_ID"] || Time.now.to_i.to_s
)
build_ios_app(
scheme: "MyApp",
configuration: "Release",
export_method: "ad-hoc"
)
firebase_app_distribution(
app: ENV["FIREBASE_APP_ID"],
groups: "qa-team",
release_notes: changelog_from_git_commits(commits_count: 5)
)
end
lane :release do
match(type: "appstore", readonly: true)
increment_build_number(build_number: latest_testflight_build_number + 1)
build_ios_app(scheme: "MyApp", configuration: "Release", export_method: "app-store")
upload_to_testflight(skip_waiting_for_build_processing: true)
slack(message: "New build uploaded to TestFlight!", channel: "#releases")
end
end
setup_ci створює тимчасовий Keychain в рамках CI-джоба. Без цього fastlane намагатиметься відкрити користувацький Keychain, який на headless CI недоступний.
Управління номером сборки
increment_build_number без аргументу читає поточний номер з Info.plist та збільшує на 1. Але при паралельних CI-джобах можливі колізії—два PR збираються одночасно та обидва отримують однаковий номер. Рішення: використовуйте CI_PIPELINE_ID (GitLab) або github.run_number (GitHub Actions) як номер сборки. Це гарантує унікальність.
Інтеграція з GitHub Actions
- name: Run Fastlane Beta
env:
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
MATCH_GIT_BASIC_AUTHORIZATION: ${{ secrets.MATCH_GIT_AUTH }}
FIREBASE_APP_ID: ${{ secrets.FIREBASE_APP_ID }}
FIREBASE_TOKEN: ${{ secrets.FIREBASE_CLI_TOKEN }}
run: bundle exec fastlane beta
Раннер повинен бути macOS (runs-on: macos-14). Linux-раннери не підходять для iOS-сборок—Xcode працює тільки на macOS.
Кешування залежностей
Найдовший крок—pod install або swift package resolve. На GitHub Actions кешуємо:
- name: Cache CocoaPods
uses: actions/cache@v4
with:
path: Pods
key: ${{ runner.os }}-pods-${{ hashFiles('Podfile.lock') }}
Економить 3–8 хвилин на кожному прогоні при незмінному Podfile.lock.
Часова шкала
Базова настройка Fastlane з match, test та beta lanes на GitHub Actions: 3–5 днів. Повна конфігурація з release-lane, changelog, Slack-повідомленнями, кешуванням, підтримкою multi-scheme: 1–2 тижні. Стоимость рассчитывается индивидуально.







