Хотя идея выпуска новой версии нашего приложения с новыми функциями и устранением множества ошибок звучит приятно, реальность такова, что ручной процесс громоздкий и трудоемкий, что делает этот процесс не очень приятным.

Чтобы облегчить эту боль, я автоматизировал этот процесс с помощью CircleCI. Это был нелегкий путь из-за плохой документации и отсутствия рабочих примеров. Иногда у меня было ощущение, что некоторые части документации устарели и не поддерживаются.

В следующей статье вы найдете процесс, которому я следовал, чтобы автоматизировать процесс выпуска приложений для iOS, включая примечания к выпуску и ссылки на всю документацию и примеры, которые я использовал.

🔐 Определите настройки учетной записи App Store Connect и переменные среды CircleCI

Во-первых, нам нужно собрать все связанные идентификаторы, чтобы иметь возможность подключаться к App Store Connect API.

Создайте файл приложения

Нам нужно создать файл приложения, содержащий идентификатор пакета или идентификатор приложения и адрес электронной почты нашей учетной записи Apple:

your_project/ios/fastlane/Appfile

app_identifier("com.bla.bla") # The bundle identifier of your app
apple_id("[email protected]") # Your Apple email address

Создайте настройки выпуска

В начале файла Fastfile мы определим идентификатор приложения, профиль обеспечения и идентификатор команды, которые будут использоваться на полосе выпуска. Это очень полезно, потому что если у нас есть другие дорожки, такие как Adhoc, мы можем определить разные объекты конфигурации для использования в зависимости от дорожки:

your_project/ios/fastlane/Fastfile

APP_ID = "com.bla.bla"
PROVISIONING_PROFILE_APPSTORE = "match AppStore com.bla.bla"
TEAM_ID = "your_team_id"

settings_to_override_release = { 
  :BUNDLE_IDENTIFIER => APP_ID, 
  :PROVISIONING_PROFILE_SPECIFIER => PROVISIONING_PROFILE_APPSTORE, 
  :DEVELOPMENT_TEAM => TEAM_ID, 
}

Создайте ключ API и добавьте его в переменные среды CircleCI.

Во-первых, нам нужно получить key_id, issuer_id и key_content из нашей учетной записи App Store Connect. Мы можем получить их отсюда:

Во-вторых, мы должны создать переменные среды CircleCI для этих значений, перейдя в ваш проект › ⚙️ Настройки проекта › Переменные среды › Добавить переменную среды.

Наконец, используя команду app_store_connect_api_key в нашем Fastfile, мы прочитаем значения ключа API, ранее определенные как переменные среды, и, таким образом, мы сможем идентифицировать себя в App Store Connect API для доступа и отправки нашего проекта. данные.

your_project/ios/fastlane/Fastfile

desc "Export Release IPA & upload Release to App Store"
lane :release_ipa do
  api_key = app_store_connect_api_key( 
    key_id: $APP_STORE_CONNECT_API_KEY_KEY_ID, 
    issuer_id: $APP_STORE_CONNECT_API_KEY_ISSUER_ID, 
    key_content: $APP_STORE_CONNECT_API_KEY_KEY 
  )
end

Делая это таким образом, рекомендуется избегать фактора аутентификации 2, который запрашивается при входе в систему с использованием пользователя и пароля.

🚀 Определите полосу выпуска Fastfile

Нам нужно создать новую полосу, чтобы указать необходимые шаги для успешного выполнения нового выпуска в App Store Connect.

Создать полосу release_ipa

Давайте вернемся к нашему Fastfile, в этом файле мы определим все нужные полосы, связанные с процессами iOS, такие как экспорт adhoc IPA, экспорт IPA выпуска или создание нового выпуска и загрузка IPA выпуска в Магазин приложений.

В этой статье будет рассказано о создании полосы для запуска нового релиза в App Store, помимо генерации IPA и загрузки в новый релиз.

➕ Увеличение номера сборки

Мы продолжим разработку нашей линии release_ipa, добавив вызов increment_build_number:

your_project/ios/fastlane/Fastfile

desc "Export Release IPA & upload Release to App Store"
lane :release_ipa do
  api_key = app_store_connect_api_key( 
    key_id: $APP_STORE_CONNECT_API_KEY_KEY_ID, 
    issuer_id: $APP_STORE_CONNECT_API_KEY_ISSUER_ID, 
    key_content: $APP_STORE_CONNECT_API_KEY_KEY 
  )
  increment_build_number(
    build_number: app_store_build_number(
      api_key: api_key,
      initial_build_number: 0,
      version: get_version_number(xcodeproj: "your_project.xcodeproj"),
      live: false
    ) + 1,
  )
end

Этот фрагмент кода будет очень полезен при загрузке нескольких сборок для пользователей Testflight. Автоматически назначает следующую версию сборки с учетом того, существует ли IPA для нового выпуска.

Учтите, что этот вызов должен включать учетные данные api_key, это недостаточно документировано в официальных документах. Подробнее о app_store_build_number.

🔑 команда соответствия

Вызов match — это то, где мы определяем тип разрешений / профилей обеспечения, применяемых к App Store, в этом случае ключевое слово type, назначенное режиму выпуска, — appstore.

your_project/ios/fastlane/Fastfile

desc "Export Release IPA & upload Release to App Store"
lane :release_ipa do
  api_key = app_store_connect_api_key( 
    key_id: $APP_STORE_CONNECT_API_KEY_KEY_ID, 
    issuer_id: $APP_STORE_CONNECT_API_KEY_ISSUER_ID, 
    key_content: $APP_STORE_CONNECT_API_KEY_KEY 
  )
  increment_build_number(
    build_number: app_store_build_number(
      api_key: api_key,
      initial_build_number: 0,
      version: get_version_number(xcodeproj: "your_project.xcodeproj"),
      live: false
    ) + 1,
  )
  match(
    app_identifier: APP_ID,
    readonly: is_ci,
    type:"appstore"
  )
end

🏗 команда сборки приложения

Вызов build_app создает IPA с желаемой конфигурацией. В следующем фрагменте кода вы можете увидеть, как установить конфигурацию Release:

your_project/ios/fastlane/Fastfile

desc "Export Release IPA & upload Release to App Store"
lane :release_ipa do
  api_key = app_store_connect_api_key( 
    key_id: $APP_STORE_CONNECT_API_KEY_KEY_ID, 
    issuer_id: $APP_STORE_CONNECT_API_KEY_ISSUER_ID, 
    key_content: $APP_STORE_CONNECT_API_KEY_KEY 
  )
  increment_build_number(
    build_number: app_store_build_number(
      api_key: api_key,
      initial_build_number: 0,
      version: get_version_number(xcodeproj: "your_project.xcodeproj"),
      live: false
    ) + 1,
  )
  match(
    app_identifier: APP_ID,
    readonly: is_ci,
    type:"appstore"
  )
 build_app(
    scheme:"your_project_name", 
    export_method:"app-store",
    skip_profile_detection:true,
    configuration: "Release",
    workspace: "your_project_name.xcworkspace",
    xcargs: settings_to_override_release,
    export_options: {
      provisioningProfiles: { 
        APP_ID => PROVISIONING_PROFILE_APPSTORE
      },
      installerSigningCertificate: "your_installer_signing_certificate_name"
    }
  )
end

🛵 доставить команду

Вызов доставить создает новую версию в App Store, а затем загружает двоичный файл IPA, сгенерированный на этапе build_app:

your_project/ios/fastlane/Fastfile

APP_ID = "com.bla.bla"
PROVISIONING_PROFILE_APPSTORE = "match AppStore com.bla.bla"
TEAM_ID = "your_team_id"

settings_to_override_release = { 
  :BUNDLE_IDENTIFIER => APP_ID, 
  :PROVISIONING_PROFILE_SPECIFIER => PROVISIONING_PROFILE_APPSTORE, 
  :DEVELOPMENT_TEAM => TEAM_ID, 
}

default_platform(:ios)

platform :ios do

  before_all do
    setup_circle_ci
  end

  desc "Export Release IPA & upload Release to App Store"
  lane :release_ipa do
    api_key = app_store_connect_api_key( 
      key_id: $APP_STORE_CONNECT_API_KEY_KEY_ID, 
      issuer_id: $APP_STORE_CONNECT_API_KEY_ISSUER_ID, 
      key_content: $APP_STORE_CONNECT_API_KEY_KEY 
    )
    increment_build_number(
      build_number: app_store_build_number(
        api_key: api_key,
        initial_build_number: 0,
        version: get_version_number(xcodeproj: "your_project.xcodeproj"),
        live: false
      ) + 1,
    )
    match(
      app_identifier: APP_ID,
      readonly: is_ci,
      type:"appstore"
    )
   build_app(
      scheme:"your_project_name", 
      export_method:"app-store",
      skip_profile_detection:true,
      configuration: "Release",
      workspace: "your_project_name.xcworkspace",
      xcargs: settings_to_override_release,
      export_options: {
        provisioningProfiles: { 
          APP_ID => PROVISIONING_PROFILE_APPSTORE
        },
        installerSigningCertificate: "your_installer_signing_certificate_name"
      }
    )
    deliver(
      api_key: api_key,
      submit_for_review: false,
      force: true,
      precheck_include_in_app_purchases: false
    )
  end
end

Этот шаг также должен включать учетные данные api_key, это четко не задокументировано в официальных документах.

☝️ Если мы установим submit_for_review: true, новый выпуск будет отправлен на проверку автоматически, поэтому нам не нужно нажимать на него вручную с веб-сайта Apple Store Connect.

Дополнительный совет. Если у вас нет покупок в приложении, установите precheck_include_in_app_purchases: false, иначе рабочий процесс завершится сбоем в CircleCI, даже если IPA был успешно отправлен в App Store.
Precheck не может проверять покупки в приложении с помощью ключа API App Store Connect · Проблема № 18250 · fastlane/fastlane

Теперь наш Fastfile готов 🎉

🐾 Определите задание Release в config.yml

Как только наша полоса будет завершена, нам нужно создать задание, которое вызывает эту полосу среди других процессов, чтобы наш поток был готов.

Создайте файл .env

В файле config.yml мы создадим задание ios-release.

В этом задании нам понадобится шаг для создания файла .env, содержащего ранее определенные ключи, связанные с App Store Connect, в переменных среды CircleCI:

- run:
    name: "Create .env file"
    command: echo -e "APP_STORE_CONNECT_API_KEY_ISSUER_ID=${APP_STORE_CONNECT_API_KEY_ISSUER_ID}\nAPP_STORE_CONNECT_API_KEY_KEY=${APP_STORE_CONNECT_API_KEY_KEY}\nAPP_STORE_CONNECT_API_KEY_KEY_ID=${APP_STORE_CONNECT_API_KEY_KEY_ID}" > .env

Если вы уже создали этот шаг, просто добавьте новые переменные среды.

Звонок на переулок release_ipa

В config.yml мы добавим новый шаг для вызова нашей ранее созданной дорожки release_ipa:

- run:
    command: rm -rf Pods && pod install && bundle exec fastlane release_ipa --verbose
    no_output_timeout: 30m
    working_directory: ios

Скелет задания релиза config.yml будет примерно таким:

ios-release:
    macos:
      xcode: '13.3.0'
    working_directory: ~/your-working-directory

    # use a --login shell so our "set Ruby version" command gets picked up for later steps
    shell: /bin/bash --login -o pipefail

    steps:
      - checkout

      - run:
          name: "Create .env file"
          command: echo -e "APP_STORE_CONNECT_API_KEY_ISSUER_ID=${APP_STORE_CONNECT_API_KEY_ISSUER_ID}\nAPP_STORE_CONNECT_API_KEY_KEY=${APP_STORE_CONNECT_API_KEY_KEY}\nAPP_STORE_CONNECT_API_KEY_KEY_ID=${APP_STORE_CONNECT_API_KEY_KEY_ID}" > .env
      
      (...)

      - run:
          command: rm -rf Pods && pod install && bundle exec fastlane release_ipa --verbose
          no_output_timeout: 30m
          working_directory: ios

      - store_artifacts:
          path: ios/your_project.ipa

🕹 Определите рабочий процесс выпуска в config.yml

Мы собираемся определить два задания в файле config.yml:

  • request-ipa-and-prepare-release: чтобы иметь возможность решить, когда мы хотим создать выпуск, это задание даст нам возможность решить, на какой ветке мы хотим инициировать новый выпуск. iOS-релиз.
  • ios-release: при нажатии на палец вверх в задании request-ipa-and-prepare-release будет запущен процесс выпуска iOS, поэтому будет выпущен новый выпуск. созданный в App Store Connect, и будет загружен новый сгенерированный IPA.

Скелет рабочих процессов будет примерно таким:

workflows:
  version: 2
  ios:
    jobs:
      - request-ipa-and-prepare-release:
          type: approval
          filters:
            <<: *filters-node      
      - ios-release:
          requires:
            - request-ipa-and-prepare-release
          filters:
            <<: *filters-node    

А вот как это будет выглядеть на CircleCI:

🆕 Автоматически загружать примечания к выпуску

Автоматизировать загрузку примечаний к выпуску для каждого нового выпуска iOS очень просто, поэтому нам не нужно заходить в App Store Connect и заполнять его для каждого языка. Ниже приведен уникальный шаг:

  • Создайте папку для каждого из языков, поддерживаемых вашим приложением, в следующем месте:
ios/fastlane/metadata/en-US/release_notes.txt
ios/fastlane/metadata/es-ES/release_notes.txt
ios/fastlane/metadata/fr-FR/release_notes.txt

В файлах release_notes.txt добавьте текст так, как вы бы это сделали: Никаких дополнительных шагов или дополнительных флагов не требуется.

Это автоматически определяется командой доставить, поэтому нет необходимости добавлять дополнительную конфигурацию.

Поздравляем, вы сделали это! Теперь ваш процесс выпуска iOS автоматизирован! 🥳

🥹 При попытке нового процесса выпуска вы заметите, что IPA появляется МГНОВЕННО на портале App Store Connect, когда вы делаете это через CircleCI, в отличие от того, когда мы делали это вручную из Xcode, для появления которого потребовалось около 20 минут. .

🐞 Ошибки, обнаруженные в процессе

CircleCI показывает ошибку, связанную с iTMSTransporter: ошибка An exception has occurred: issuerId is required

[пилот] не может загрузить сборку в TestFlight с помощью ключа API после автоматического обновления iTMSTransporter до версии 3.0.0 с ошибкой «Произошло исключение: требуется issuerId · Проблема № 20741 · fastlane/fastlane»

Это было решено путем добавления следующей переменной env в CircleCI: ITMSTRANSPORTER_FORCE_ITMS_PACKAGE_UPLOAD = true

И обновление fastlane версии до 2.210.1 на Gemfile.lock:

// on the Terminal, go to your project

cd ios
bundle update

// after these steps, commit and push the Gemfile.lock

В следующей статье мы узнаем, как автоматизировать процесс выпуска приложений для Android в Google Play Store и CircleCI 🤖

📚 Документы

Документация и дополнительные руководства
- Официальное руководство Fastlane по развертыванию в Apple Store
- Как создать идеальный конвейер fastline для iOS
- Как использовать fastlane для быстрого развертывания приложения iOS
Создайте и разверните приложение iOS для Testflight с помощью Github Actions
Установите переменные среды в CI
Дорожки
App Store Connect API
Аутентификация с помощью сервисов Apple

Примеры
-
circleci-demo-ios/config.yml на мастере · CircleCI-Public/circleci-demo-ios
- circleci-demo-ios/Fastfile на master · CircleCI-Public/circleci-demo-ios
https://webcache.googleusercontent.com/search?q=cache:DFydATxAjUEJ:https://circleci.com/docs/ios-tutorial&cd= 2&hl=en&ct=clnk&gl=es