Всем привет! Меня зовут Иван Чечиков, я 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

Спасибо большое за уделенное время. Если у вас есть вопросы – буду рад ответить на них в комментариях.

Комментарии (0)