Настройка CI/CD для мобильного приложения через Jenkins
Jenkins выбирают, когда нужен полный контроль над инфраструктурой, уже есть on-premise Jenkins-сервер в компании, или требования безопасности запрещают передачу кода в облачные CI-системы. Для мобильной разработки это означает: Linux-мастер для Android, macOS-агент для iOS.
Архитектура Jenkins для мобильной разработки
Jenkins Controller (Linux/macOS)
├── macOS Agent (для iOS)
│ ├── Xcode 16
│ ├── fastlane
│ └── CocoaPods / SPM
└── Linux Agent (для Android)
├── JDK 17
├── Android SDK
└── Gradle
Агенты подключаются по SSH (Launch agents via SSH) или через JNLP. Для macOS-агента — обязательно аккаунт с Keychain-доступом к code signing сертификатам.
Jenkinsfile: декларативный pipeline
pipeline {
agent none
environment {
FASTLANE_SKIP_UPDATE_CHECK = 'true'
MATCH_PASSWORD = credentials('match-passphrase')
FIREBASE_TOKEN = credentials('firebase-cli-token')
}
stages {
stage('Test iOS') {
agent { label 'macos-m2' }
steps {
checkout scm
sh 'bundle install --path vendor/bundle'
sh 'bundle exec fastlane test'
}
post {
always {
junit 'fastlane/test_output/report.junit'
}
}
}
stage('Build iOS Beta') {
agent { label 'macos-m2' }
when {
branch 'main'
}
steps {
sh 'bundle exec fastlane beta'
}
}
stage('Build Android') {
agent { label 'linux-android' }
steps {
checkout scm
sh './gradlew test assembleRelease'
archiveArtifacts artifacts: 'app/build/outputs/apk/release/*.apk'
}
}
}
post {
failure {
slackSend(
channel: '#mobile-ci',
color: 'danger',
message: "Build failed: ${env.JOB_NAME} #${env.BUILD_NUMBER}"
)
}
}
}
credentials('match-passphrase') — Jenkins Credentials Store. Секреты не хранятся в Jenkinsfile, только ссылки на ID.
Управление Keychain на macOS-агенте
Главная боль Jenkins + iOS: при запуске как фоновый daemon codesign не может открыть Keychain без unlock. Решение — unlock в начале пайплайна:
stage('Unlock Keychain') {
agent { label 'macos-m2' }
environment {
KEYCHAIN_PASSWORD = credentials('macos-keychain-password')
}
steps {
sh 'security unlock-keychain -p $KEYCHAIN_PASSWORD ~/Library/Keychains/login.keychain-db'
sh 'security set-keychain-settings -lut 3600 ~/Library/Keychains/login.keychain-db'
}
}
set-keychain-settings -lut 3600 — keychain не блокируется 1 час (достаточно для сборки). Без этого через 5 минут idle keychain блокируется автоматически и codesign получает errSecInteractionNotAllowed.
Кэширование артефактов
Jenkins не имеет встроенного умного кэша как GitHub Actions. Варианты:
Shared directory на агенте. Папка ~/.gradle или ~/Library/Caches/CocoaPods сохраняется между сборками автоматически — если всегда используется один и тот же агент (sticky agent).
Jenkins Workspace Caching Plugin. Кэширует по хэшу Podfile.lock/build.gradle, аналог GitHub Actions cache.
Artifactory/Nexus. Для enterprise — кэш Maven/Gradle зависимостей через внутренний артифакторий. GRADLE_USER_HOME=~/.gradle + зеркало Nexus.
Параллельные стадии
stage('Test Parallel') {
parallel {
stage('iOS Tests') {
agent { label 'macos-m2' }
steps { sh 'bundle exec fastlane test' }
}
stage('Android Tests') {
agent { label 'linux-android' }
steps { sh './gradlew test' }
}
}
}
Параллельный прогон iOS и Android тестов сокращает общее время с ~20 до ~12 минут.
Типичные проблемы
-
xcodebuildне находит симулятор — нужно явно указыватьxcrun simctl boot "iPhone 16"перед запуском тестов на новом агенте - Gradle wrapper не найден — нужен
chmod +x gradlewв начале шага - Build number конфликтует при параллельных сборках — используем
${env.BUILD_NUMBER}как суффикс
Сроки
Настройка Jenkins Pipeline с macOS + Linux агентами, test + deploy лейнами: 1–2 недели (включая агентов). Поддержка параллельных сборок, интеграция с Slack/Jira, настройка Artifactory: ещё 1 неделя. Стоимость рассчитывается индивидуально.







