[Разработка под iOS, Системы сборки, Облачные сервисы] Интеграция CI/CD для нескольких сред с Jenkins и Fastlane. Часть 2 (перевод)

Автор Сообщение
news_bot ®

Стаж: 6 лет 9 месяцев
Сообщений: 27286

Создавать темы news_bot ® написал(а)
11-Ноя-2020 21:31
В преддверии старта курса "iOS Developer. Basic" продолжаем публиковать серию полезных переводов, а также приглашаем записаться на бесплатный демо-урок по теме: "Result Type".

Читать первую часть5. Сборка билда
stage('Build') {
        withEnv(["FASTLANE_USER=fastlane_user_email_address"]) {
            withCredentials([
                    string([
                      credentialsId:'match_password_id',
                      variable: 'MATCH_PASSWORD'
                    ]),
                    string([
                      credentialsId: 'fastlane_password_id',
                      variable: 'FASTLANE_PASSWORD']),
                    ]) {
                       sh 'bundle exec fastlane build'
                    }
        }
  }
 На этом этапе мы устанавливаем необходимые переменные среды с помощью функции Jenkins Pipeline withEnv как указано здесь, в соответствии с документацией Fastlane. Итак, мы устанавливаем переменную окружения FASTLANE_USER. После этого мы устанавливаем еще две переменные среды, MATCH_PASSWORD и FASTLANE_PASSWORD, которые нельзя получить без учетных данных. Они по очевидным причинам хранятся в зашифрованном виде внутри Jenkins в пункте меню «Credential» дашборда Jenkins в формате secret_text, и их можно получить, предоставив credentialsId.Опять же, на этапе сборки мы реализовали настраиваемый лейн внутри Fastfile, который будет самым сложным лейном, который нам нужно будет создать следующим образом:
lane :build do
     match(
        git_branch: "the_branch_of_the_repo_with_the_prov_profile",
        username: "github_username",
        git_url: "github_repo_with_prov_profiles",
        type: "appstore",
        app_identifier: "production_app_identifier",
        force: true)
     version = get_version_number(
                       xcodeproj: "our_project.xcodeproj",
                       target: "production_target"
               )
     build_number = latest_testflight_build_number(
                       version: version,
                       app_identifier: "production_app_identifier",
                       initial_build_number: 0
                     )
     increment_build_number({ build_number: build_number + 1 })
     settings_to_override = {
      :BUNDLE_IDENTIFIER => "production_bundle_id",
      :PROVISIONING_PROFILE_SPECIFIER => "production_prov_profile",
      :DEVELOPMENT_TEAM => "team_id"
     }
     export_options = {
       iCloudContainerEnvironment: "Production",
       provisioningProfiles: { "production_bundle_id": "production_prov_profile" }
     }
     gym(
       clean: true,
       scheme: "production_scheme",
       configuration: "production_configuration",
       xcargs: settings_to_override,
       export_method: "app-store",
       include_bitcode: true,
       include_symbols: true,
       export_options: export_options
     )
  end
Теперь давайте разберем его шаг за шагом.Мы начинаем с использования экшена Fastlane match. Match по сути создает все необходимые сертификаты и профили обеспечения, которые хранятся в отдельном git-репозитории, т.е. он по сути автоматизирует процесс подписания кода. Это означает, что перед запуском match мы должны были создать другой Github-репозиторий, где мы хранили бы наши профили обеспечения. В качестве альтернативы, если мы не хотим использовать match для подписи кода, мы можем использовать экшены sigh и cert.А теперь самое интересное. Что бы мы хотели автоматизировать, так это увеличение номер версии билда для одного и того же релиза, чтобы не делать это каждый раз вручную через настройки билда Xcode. Все мы знаем о том, что для того, чтобы загрузить билд в Testflight несколько раз для одного и того же релиза и автоматически не словить ошибку, мы должны повышать версию сборки каждый раз, т.е. мы должны переходить к настройкам проекта или. plist, делать это вручную, а затем пробовать повторно загрузить его. В коде, приведенном выше, нам удалось автоматизировать эту процедуру, выполнив 3 следующих шага:
  • get_version_number: Получить версию проекта, загруженного в настоящее время 
  • latest_testflight_build_number: Получить текущий номер билда для версии, котрую мы получили в предыдущем шаге
  • increment_build_number: Инкремент номера билда с указанным шагом (здесь на единицу).
Наконец, мы продолжаем вызовом экшена gym, который производит фактическую сборку и упаковку приложения. Он настраивается с несколькими аргументами, такими как configuration, scheme и xcargs, где мы можем указать bundle_identifier, export_options и т. д.6. Загрузка в Testflight
stage('Upload to TestFlight') {
  withEnv(["FASTLANE_USER=fastlane_user_email_address"]) {
    withCredentials([
      string([
           credentialsId: 'fastlane_password_id',
           variable: 'FASTLANE_PASSWORD']),
       ]) {
         sh "bundle exec fastlane upload_to_testflight"
       }
  }
}
Здесь мы снова указываем необходимые переменные среды, как мы делали в предыдущем шаге, и мы реализуем другой кастомный лейн внутри Fastfile следующим образом:
lane :upload_to_testflight do
    pilot(
      ipa: "./build/our_project.ipa",
      skip_submission: true,
      skip_waiting_for_build_processing: true,
      app_identifier: "production_app_identifier"
    )
end
Мы используем команду Fastlane pilot, которая загружает сгенерированный на предыдущем шаге файл .ipa в Testflight. Этим действием мы также можем указать лог изменений. Мы также можем пропустить отправку двоичного файла, что означает, что файл .ipa будет только загружен, но не распространен среди тестировщиков.7. Очистка И последнее, но не менее важное: на этом этапе мы выполняем очистку рабочей области с помощью плагина Jenkins cleanup.
stage('Cleanup') {
    cleanWs notFailBuild: true
}
Это означает, что мы удаляем воркспейс по завершении сборки, поскольку он больше не нужен.Подводя итог, вот как функция deploy() выглядит внутри созданного скрипта Deploy.script.
def deploy() {
    stage('Checkout') {
        checkout scm
    }
    stage('Install dependencies') {
       sh 'gem install bundler'
       sh 'bundle update'
       sh 'bundle exec pod repo update'
       sh 'bundle exec pod install'
    }
    stage('Reset Simulators') {
       sh 'bundle exec fastlane snapshot reset_simulators --force'
    }
    stage('Run Tests') {
       sh 'bundle exec fastlane test'
    }
    stage('Build') {
        withEnv(["FASTLANE_USER=fastlane_user_email_address"]) {
            withCredentials([
                 string([
                      credentialsId:'match_password_id',
                      variable: 'MATCH_PASSWORD'
                 ]),
                 string([
                      credentialsId: 'fastlane_password_id',
                      variable: 'FASTLANE_PASSWORD']),
                 ]) {
                      sh 'bundle exec fastlane build'
                 }
        }
    }
    stage('Upload to TestFlight') {
        withEnv(["FASTLANE_USER=fastlane_user_email_address"]) {
            withCredentials([
                 string([
                      credentialsId: 'fastlane_password_id',
                      variable: 'FASTLANE_PASSWORD']),
                  ]) {
                      sh "bundle exec fastlane upload_to_testflight"
                 }
       }
    }
    stage('Cleanup') {
        cleanWs notFailBuild: true
    }
}
Теперь наш Fastfile выглядит вот так:
fastlane_version "2.75.0"
default_platform :ios
lane :test do
    scan(
        clean: true,
        devices: ["iPhone X"],
        workspace: "our_project.xcworkspace",
        scheme: "production_scheme",
        code_coverage: true,
        output_directory: "./test_output",
        output_types: "html,junit"
    )
    slather(
        cobertura_xml: true,
        proj: "our_project.xcodeproj",
        workspace: "our_project.xcworkspace",
        output_directory: "./test_output",
        scheme: "production_scheme",
        jenkins: true,
        ignore: [array_of_docs_to_ignore]
    )
end
lane :build do
     match(
        git_branch: "the_branch_of_the_repo_with_the_prov_profile",
        username: "github_username",
        git_url: "github_repo_with_prov_profiles",
        type: "appstore",
        app_identifier: "production_app_identifier",
        force: true)
     version = get_version_number(
                       xcodeproj: "our_project.xcodeproj",
                       target: "production_target"
               )
     build_number = latest_testflight_build_number(
                       version: version,
                       app_identifier: "production_app_identifier",
                       initial_build_number: 0
                     )
     increment_build_number({ build_number: build_number + 1 })
     settings_to_override = {
      :BUNDLE_IDENTIFIER => "production_bundle_id",
      :PROVISIONING_PROFILE_SPECIFIER => "production_prov_profile",
      :DEVELOPMENT_TEAM => "team_id"
     }
     export_options = {
       iCloudContainerEnvironment: "Production",
       provisioningProfiles: { "production_bundle_id": "production_prov_profile" }
     }
     gym(
       clean: true,
       scheme: "production_scheme",
       configuration: "production_configuration",
       xcargs: settings_to_override,
       export_method: "app-store",
       include_bitcode: true,
       include_symbols: true,
       export_options: export_options
     )
end
lane :upload_to_testflight do
    pilot(
      ipa: "./build/our_project.ipa",
      skip_submission: true,
      skip_waiting_for_build_processing: true,
      app_identifier: "production_app_identifier"
    )
end
Функция deploy() вызывается из скрипта, который мы определили в задаче - MyScript.groovy, и выглядит следующим образом:
node(label: 'ios') {
  def deploy;
  def utils;
  String RVM = "ruby-2.5.0"
  ansiColor('xterm') {
    withEnv(["LANG=en_US.UTF-8", "LANGUAGE=en_US.UTF-8", "LC_ALL=en_US.UTF-8"]) {
        deploy = load("jenkins/Deploy.groovy")
        utils = load("jenkins/utils.groovy")
        utils.withRvm(RVM) {
          deploy.deploy()
        }
    }
  }
}
Мы загружаем скрипт Deploy.groovy и вызываем функцию deploy(), которая выполняет всю работу. Здесь мы можем заметить, что мы также загружаем скрипт utils.groovy, который помогает нам установить некоторые переменные среды перед запуском Jenkins.job. AnsiColor еще один плагин Jenkins который используется для того, чтобы раскрасить вывод этапов в пайплайне сборки. Наконец, мы можем заметить, что мы запускаем скрипт внутри
node(label: 'ios')
В Scripted Pipeline указанный выше node является важным первым шагом, поскольку он выделяет исполнителя и рабочее пространство для пайплайна.И вот нам удалось создать задачу Jenkins, которая распространяет различные ветки нашего приложения, определяя ветку в качестве параметра в Jenkins.Часть 3В следующей части мы рассмотрим как можно настроить Jenkins , чтобы распространять наше приложение на различные среды для различных конфигураций Xcode.Записаться на бесплатный демо-урок.
===========
Источник:
habr.com
===========

===========
Автор оригинала: Eleni Papanikolopoulou
===========
Похожие новости: Теги для поиска: #_razrabotka_pod_ios (Разработка под iOS), #_sistemy_sborki (Системы сборки), #_oblachnye_servisy (Облачные сервисы), #_configuration_management, #_jenkins, #_fastlane, #_ios, #_cicd, #_blog_kompanii_otus._onlajnobrazovanie (
Блог компании OTUS. Онлайн-образование
)
, #_razrabotka_pod_ios (
Разработка под iOS
)
, #_sistemy_sborki (
Системы сборки
)
, #_oblachnye_servisy (
Облачные сервисы
)
Профиль  ЛС 
Показать сообщения:     

Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы

Текущее время: 22-Ноя 19:54
Часовой пояс: UTC + 5