Всем привет! Меня зовут Иван Чечиков, я QA-инженер в МТС Digital, работаю в проекте WASD.TV. В этой статье я расскажу о своем способе автоматизации iOS-сборки в TestFlight через Jenkins. С помощью такого метода можно настроить автоматизацию как локально, так и на удаленной машине. Поможет в этом Jenkins – это простой в использовании CI/CD-инструмент. Я рассмотрю локальное применение Jenkins.
Подробности – под катом.
Apple Developer Program
Нам необходимо быть зарегистрированным в apple developer program и иметь доступ к профилям.
Добавляем новый профиль для дистрибуции:
Выбираем App ID:
Далее генерируем и скачиваем профиль.
Xcode
Нам потребуется Xcode последней версии, устанавливаем его из AppStore. После установки авторизуемся в Xcode:
Выбираем учетку, у которой есть доступ к созданному ранее Provisioning Profile и кликаем на Manage Certificates. Сертификат Apple Distribution должен сгенерироваться и попасть в связку ключей на нашей машине..
Jenkins
Jenkins устанавливаем отсюда. После установки создаем аккаунт и авторизуемся:
Далее отправляемся в Управление плагинами и добавляем плагин SCM:
В Конфигурации системы в Jenkins Location указываем url http://127.0.0.1/
Теперь мы можем создать новый Item - Pipeline
Переходим к настройкам нашего Pipeline
У нас будет параметризованная сборка. Добавляем строковые параметры:
BRANCH – это наша рабочая ветка в Git, на которой мы будем билдить проект. По дефолту указываем свою ветку, либо оставляем поле пустым.
Создаем следущий строковой параметр – BUILD_NUMBER. Это наш номер сборки в TestFlight.
VERSION – версия сборки в TestFlight.
Потом добавляем адрес репозитория в Git и данные для авторизации – логин и пароль. В Branch Specifier указываем наш строковой параметр BRANCH.
Основная логика билда будет лежать у нас в Jenkinsfile. Сохраняем настройки.
Jenkinsfile
Jenkinsfile нужно создать в нашем Git репозитории в ветке, которую мы будем билдить. Он будет выглядеть так:
#!/usr/bin/env groovy
pipeline {
agent any
parameters {
string(name: 'BUILD_NUMBER', defaultValue: '', description: 'Номер сборки в TestFlight')
string(name: 'VERSION', defaultValue: '', description: 'Версия сборки в TestFlight')
}
stages {
stage('Install dependencies') {
steps {
sh '''
xcodegen generate
pod install
brew install fastlane
'''
}
}
stage('Pre-build') {
steps {
sh """
agvtool new-version -all ${params.BUILD_NUMBER}
agvtool new-marketing-version ${params.VERSION}
echo \"PROVISIONING_PROFILE_SPECIFIER=Ваш Provisioning Profile\" >> ConfigFile.xcconfig
fastlane spaceauth -u Аккаунт_в_Apple_Developer_Program
echo n
"""
}
}
stage('Build') {
steps {
sh '''
xcodebuild -workspace file.xcworkspace -scheme Схема_проекта -configuration Конфигурация_проекта DEVELOPMENT_TEAM=Идентификатор_команды_разработки CODE_SIGN_STYLE=Manual CODE_SIGN_IDENTITY='Apple Distribution' CONFIGURATION_BUILD_DIR=Путь_где_будет_сохранен_билд -archivePath Путь_где_будет_сохранен_архив archive
xcodebuild -exportArchive -archivePath Путь_где_был_сохранен_архив -exportPath Путь_где_будем_сохранять_файл_ipa -exportOptionsPlist exportOptions.plist
'''
}
}
stage('Send build to TestFlight') {
steps {
sh """
cd Переходим_в_место_где_сохранен_ipa_файл
fastlane pilot upload --changelog "Something that is new here" -u Аккаунт_в_Apple_Developer_Program
"""
}
}
}
}
По порядку:
parameters {
string(name: 'BUILD_NUMBER', defaultValue: '', description: 'Номер сборки в TestFlight')
string(name: 'VERSION', defaultValue: '', description: 'Версия сборки в TestFlight')
}
В параметрах указываем номер билда и версию проекта. Наши строковые параметры в Jenkins, которые мы введем в начале сборки, будут подставлены в конфигурационные файлы проекта командами:
agvtool new-version -all ${params.BUILD_NUMBER}
agvtool new-marketing-version ${params.VERSION}
Так мы заполняем данные поля для сборки в TestFlight.
На этапе:
stage('Install dependencies') {
steps {
sh '''
xcodegen generate
pod install
brew install fastlane
'''
}
}
Генерируем файл проекта .xcodeproj, устанавливаем pod и fastlane библиотеки после клонирования проекта с Git.
stage('Pre-build') {
steps {
sh """
agvtool new-version -all ${params.BUILD_NUMBER}
agvtool new-marketing-version ${params.VERSION}
echo \"PROVISIONING_PROFILE_SPECIFIER=Ваш Provisioning Profile\" >> ConfigFile.xcconfig
fastlane spaceauth -u Аккаунт_в_Apple_Developer_Program
echo n
"""
}
}
Здесь мы проставляем номер билда и версию проекта. Также хардкодим значение PROVISIONING_PROFILE_SPECIFIER в конфигурационном файле (к примеру, Dev.xcconfig), так как после генерации xcodeproj файла проекта в настройках Xcode по дефолту проставляется Automatic Signing-режим для сборки проекта. Нам это не нужно, у нас режим Manual. Далее авторизуемся в Fastlane и отказываемся от сохранения сессии в cookie.
stage('Build') {
steps {
sh '''
xcodebuild -workspace file.xcworkspace -scheme Схема_проекта -configuration Конфигурация_проекта DEVELOPMENT_TEAM=Идентификатор_команды_разработки CODE_SIGN_STYLE=Manual CODE_SIGN_IDENTITY='Apple Distribution' CONFIGURATION_BUILD_DIR=Путь_где_будет_сохранен_билд -archivePath Путь_где_будет_сохранен_архив archive
xcodebuild -exportArchive -archivePath Путь_где_был_сохранен_архив -exportPath Путь_где_будет_сохранен_файл_ipa -exportOptionsPlist exportOptions.plist
'''
}
}
Билдим проект, сохраняем его в архиве и экспортируем в .ipa файл. Файл exportOptions.plist имеет следущий вид:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>method</key>
<string>app-store</string>
<key>provisioningProfiles</key>
<dict>
<key>Ваш Bundle Identifier</key>
<string>Ваш Provisioning Profile</string>
</dict>
</dict>
</plist>
stage('Send build to TestFlight') {
steps {
sh """
cd Переходим_в_место_где_сохранен_ipa_файл
fastlane pilot upload --changelog "Something that is new here" -u Аккаунт_в_Apple_Developer_Program
"""
}
}
В итоге отправляем сборку в TestFlight. Это длительный процесс, который может занять примерно 30-40 минут.
Пушим Jenkinsfile в репозиторий, переходим в Jenkins и запускаем билд.
Должен получиться вот такой сценарий:
Сборка в TestFlight
Спасибо большое за уделенное время. Если у вас есть вопросы – буду рад ответить на них в комментариях.