CI/CD заказывали? Или простое, но подробное руководство по настройке CI/CD под несколько iOS проектов

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!

Привет, меня зовут Дмитрий, и я iOS разработчик в компании Triada. В этой статье я расскажу, как настроить CI/CD для вашего iOS приложения, и приведу пошаговую инструкцию, как сделать это правильно с первого раза – чтобы не пришлось переделывать.

Мы настроим CI/CD для iOS проекта с репозиторием на GitLab с использованием Fastlane. Сборки будем отправлять в TestFlight и в Firebase, если он у вас настроен. Полный код решения находится здесь.

Что нам потребуется: 

  1. 3 Gitlab репозитория:

    1. репозиторий с проектом, для которого мы настраиваем CI/CD (PROJECT repo).
      Предполагается, что на проекте настроен линтер, однако, его отсутствие не критично. Также для тестирования проекта будут использоваться Unit тесты.

    2. нужно создать 

      1. репозиторий для хранения сертификатов (CERTS repo)

      2. репозиторий с файлами CI/CD (Если вы работаете исключительно с одним проектом, то скрипты можно расположить и в репозитории с проектом. В рамках этой статьи будем считать, что вы работаете с несколькими проектами)

  2. MacOS машина, на которой будет работать CI/CD (CI/CD SERVER)

  3. Apple ID с доступом к проектам, от имени которого будет публиковаться приложение

  4. (Опционально) Firebase Service Account - для доступа к проектам. Авторизация CI/CD будет происходить от имени данного пользователя. Firebase здесь будет использоваться исключительно для предоставления сборок тестировщикам. 

  5. (Опционально) Gitlab (Premium or Ultimate) для использования Gitlab API запросов на отправку сообщений

  6. (Опционально) Discord сервер - стоит учесть, что на канале необходимы привилегии для создания вебхуков только в рамках настройки.

  7. (Опционально) Jira - так как в данном решении управление задачами осуществляется c помощью Jira, то потребуется аккаунт с доступом на чтение задач.

Нам понадобится два вспомогательных репозитория - один для безопасного хранения сертификатов, а второй для хранения скриптов CI/CD. В первой части статьи расскажу про то, как будет выглядеть процесс настройки CI/CD в целом, а во второй части подробно опишу каждый шаг: 

  1. Создание и настройка репозитория для хранения сертификатов

  2. Создание репозитория для скриптов CI/CD

  3. Настройка iOS проекта для работы с GitLab

  4. Настройка машины (хоста) для раннеров CI/CD

Введение

Рассмотрим следующий пайплайн:
При открытии мердж реквеста (MR) автоматически запускается сборка текущей ветки и прогон тестов. После их успешного завершения пайплайн ожидает ручного запуска следующего шага, чтобы разработчик мог при необходимости внести корректировки в код. На первом этапе пайплайна можно также прогнать линтер/форматтер. Если в MR вносятся правки, пайплайн запускается заново.

При запуске следующего этапа create_archive повышает версию приложения, генерирует .ipa-архив, а также release notes для Firebase. Затем этот архив будет отправлен в Firebase и TestFlight для тестирования.

В общем и целом, наш процесс CI/CD выглядит примерно так:

Выполнение начальных шагов пайплайна: выполнение сборки и запуск тестоРис 2. Выполнение заключительных шагов пайплайна: генерация архива и теплой в TestFlight и Firebase
Выполнение начальных шагов пайплайна: выполнение сборки и запуск тестов
Выполнение заключительных шагов пайплайна: генерация архива и теплой в TestFlight и Firebase
Выполнение заключительных шагов пайплайна: генерация архива и теплой в TestFlight и Firebase

Как я упомянул ранее, в нашей реализации на этапе сборки дополнительно выполняется проверка линтером файлов .swift, участвующих в MR, и если будут обнаружены какие-либо конфликты, соответствующие сообщения отправляются в MR. Хочу отметить, что даже если независимо от того, нашел ли линтер какие-либо проблемы или нет - пайплайн не блокируется. Если мы хотим, чтобы в MR на GitLab отображался статус проверки кода линтером, нам нужна подписка, иначе у нас не будет токена для API Gitlab.

Сообщения от линтера выглядят следующим образом:

Рис 3. Сообщения от линтера в MR
Рис 3. Сообщения от линтера в MR
Там, где возможно, линтер открывает тред в MR.

В текущей реализации нет гарантий, что для каждого конфликта литера будет заведен diff комментарий с указанием кода. Это связано с тем, что в Gitlab API, на мой взгляд, несколько неудобно организована отправка diff комментариев: необходимо указывать начальную и конечную позиции блоков кода как в старом файле, так и в новом, дополнительно предоставляя sha1 для файла. Но если у вас есть время поиграться с Gitlab API, можно написать еще несколько десятков строк кода и решить эту интересную задачку. Подробнее об Gitlab API можно почитать тут.

Шаги deploy_to_fb и deploy_to_tf отвечают за отправку архива приложения в Firebase и TestFlight соответственно. Для Firebase дополнительно установлено оповещение группы тестировщиков и прикрепляются release notes.

Несколько слов о Release notes

В случае нахождения задачи в Jira - будет предоставлен номер задачи и ссылка на нее. Если для задачи присутствует еще и эпик - он также предоставляется в подобном формате. На последней строке всегда присутствует номер версии и сборки.

Пример текущего исполнения release notes
Пример текущего исполнения release notes
Возможный пример исполнения release notes
Возможный пример исполнения release notes

Если же задача не будет найдена, в release notes будет представлена только информация о версии и номере сборки

Возможный пример исполнения release notes
Возможный пример исполнения release notes

Для архива, отправляющегося в TestFlight в рамках данного примера оповещения отключены.

Настройка CI/CD

Настало время приступить к настройке нашего процесса CI/CD

Настройка [CERTS repo] 

Прежде всего, нам нужен репозиторий для хранения сертификатов. Он будет использован для хранения сертификатов и поэтому не должен находиться в общем доступе.
Доступ к репозиторию необходимо оформить только определенному кругу лиц и серверу CI/CD, поэтому создаем репо в Gitlab и делаем его приватным.
Вуаля - вы замечательны. На этом настройка репозитория с сертификатами завершена. 

Настройка [CI/CD repo]

Мы создаем репозиторий и делаем его приватным. Файлы, перечисленные здесь, представлены только для справки. По окончанию настройки, репозиторий должен содержать следующие файлы, которые вы можете взять из репозитория.

Корень проекта репозитория со скриптами CI/CD
Корень проекта репозитория со скриптами CI/CD

где по пути fastlane/ располагается:

Содержимое каталога Fastlane
Содержимое каталога Fastlane

Обратите внимание на .gitlab-ci-template.yml - этот файл содержит необходимую информацию о нашем пайплайне и будет использоваться любым проектом с CI/CD. Как вы могли заметить, он довольно небольшой, и в нем не так много переменных - они должны быть объявлены позже в настройках CI/CD вашего проекта.

Вы можете скорректировать файл удобным для вас образом, например, добавить везде дополнительное условие на именование ветки, как это было сделано в шаге create_archive, либо убрать его вовсе:

Условие запускаcreate_archiveдля определенного эвента и имени ветки
Условие запускаcreate_archiveдля определенного эвента и имени ветки

Если вы не собираетесь внедрять Firebase в проект или использовать TestFlight для тестирования, удалите следующие строки:

Для Firebase:

Код задачи для деплоя сборки в Firebase
Код задачи для деплоя сборки в Firebase

Для TestFlight:

Код задачи для деплоя сборки в TestFlight
Код задачи для деплоя сборки в TestFlight

Настройка [PROJECT repo]

Допустим, у нас уже есть готовый проект с развернутым линтером, нам потребуется выполнить следующие шаги:

  1. Убедиться, что проект настроен корректно

  2. Завести несколько переменных

  3. Завести новый пайплайн

  4. Создать для проекта раннеры

  5. Настроить Appfile

1. Убеждаемся, что проект настроен корректно

Теперь проверим, что проект настроен корректно.

Настройка схем и таргетов

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

Таргеты проекта, для которого разворачиваем CI/CD
Таргеты проекта, для которого разворачиваем CI/CD

Как ранее было упомянуто, таргет для тестирования в нашем случае отвечает за unit тесты. Однако, если у Вас появляется желание или необходимость развернуть и UI тесты, дополнительно заводится таргет и схема под него.

Для работы CI/CD с нашей версией проекта необходимо создать как минимум первые 2 схемы:
1. Схема, с которой будет собираться проект
2. Схема, с которой будут проходить тесты
3. Схема, в которой ведется разработка
Таким образом, мы имеем 3 рабочих схемы, стоит убедиться, что для всех них стоит галочка на shared, в противном случае - схема видна будет только вам.

Схемы проекта, для которого разворачивается CI/CD
Схемы проекта, для которого разворачивается CI/CD

Интеграция с Firebase

Мы подразумеваем, что проект уже привязан к Firebase, поэтому смело пропускаем эту секцию, если все готово или Firebase использовать не планируется.

Шаги по настройке интеграции с Firebase.

Для начала переходим на страницу Firebase.
Если вы еще не создали проект Firebase, нажмите “Add project”.
После создания проекта, ассоциируем его с нашим iOS проектом. Для этого нажимаем “Add app” внутри проекта и выбираем iOS проект.
На данном этапе отобразится 5 шагов, самым главным для нас является “Apple bundle ID” - его берем из настроек проекта в Xcode.
Скачайте GoogleService-Info.plist и добавьте его в корень репозитория с iOS проектом. После завершения всех указанных шагов проект готов к работе.

Добавим возможность выкладывать сборки в Firebase.

Переходим в "App Distribution" для добавления тестовых групп
Переходим в "App Distribution" для добавления тестовых групп

Перейдите во вкладку “Release & Monitor” и выберете “App Distribution”. В открывшемся окне, нажмите “Get started”. 

Настройка почти завершена.

Перейдите в таб “Testers & Groups” и добавьте группу для теста “Add group” (при желании можете добавить в нее себя).


На этом настройка тестовых групп завершена, скопируйте название группы - оно понадобится при настройке GitLab CI/CD

Теперь, перейдите в настройки проекта:

Переходим в "Project settings"
Переходим в "Project settings"

В табе “General” располагается информация о проекте и привязанных к нему приложениях. Нас интересует секция “Your apps” - в ней можно найти информацию о проекте и при необходимости ее скорректировать.
Отмечу, что Bundle ID менять для приложения крайне не рекомендую.  При необходимости провести данную процедуру - стоит привязать новое приложение.

Скопируйте значение “App ID” - оно потребуется далее при настройке.

2. Заводим переменные для проекта

Для корректной работы CI/CD потребуется в рамках проекта на Gitlab завести 3 переменные.
"SETTINGS" → "CI/CD" → "Variables" → "Expand" → "Add variable"

Настройки проекта, где планируется развернуть CI/CD. Добавляем переменные CI/CD
Настройки проекта, где планируется развернуть CI/CD. Добавляем переменные CI/CD

Переменная

Описание

Пример

PROJECT_NAME

Имя проекта

FastlaneProject

PROJECT_REPO_URL

Ссылка на репозиторий для использования git clone

https://gitlab.com/XXXXXX/YYYYYY.git вместо XXXXXX и YYYYYY подставьте ваши значения

XCWORKSPACE

Eсли установлены поды, то необходимо предоставить имя .xcworkspace файла с расширением

FastlaneProject.xcworkspace

Как итог, должно получиться подобное представление:

Переменные CI/CD в проекте
Переменные CI/CD в проекте

Устанавливать Masked не обязательно, это свойство определяет лишь для скрытия адреса в логах

3. Заводим пайплайн

Создаем /.gitlab-ci.yml в корне проекта и копируем в него следующие строки:

include:
- project: 'cicdXXXXXX/cicd'
  file: '/.gitlab-ci-template.yml'

Данный файл ссылается на .gitlab-ci-template.yml из проекта cicdXXXXXX/cicd, который в моем случае используется как [CI/CD repo], не забудьте заменить его на имя вашего проекта для CI/CD.

4. Настроим раннеры для проекта

В Gitlab проекте переходим в “Settings” -> “CI/CD” и открываем секцию “Runners

Настройки проекта, где планируется развернуть CI/CD. Добавляем раннеры
Настройки проекта, где планируется развернуть CI/CD. Добавляем раннеры

Пока мы не можем найти нужные нам раннеры с тегами - создадим их, нажмем на “New project runner”. Необходимо указать теги для раннера.
В качестве названия раннеров можно, но не обязательно, использовать следующую нотацию для упрощения их идентификации: PROJECT_NAME/JOB_NAME/NUMBER.

Добавление Gitlab раннера в проект
Добавление Gitlab раннера в проект

После нажатия Create runner будет предложено выполнить несколько команд в среде, где планируется запускать раннер - для корректной настройки этот шаг пропускать нельзя.
Выполнять команды будем на [CI/CD SERVER].
Во время регистрации раннеров важно не запускать команду под sudo - в дальнейшем это может привести к некорректной работе Раннера. В качестве executor выбираем shell.
По итогу будут зарегистрированы раннеры, файл настройки можно найти тут:

➜  ~ ls -ltrh ~/.gitlab-runner/config.toml
-rw-------@ 1 User  staff   900B May  4 19:55 /Users/CICDUser/.gitlab-runner/config.toml

Внутри config.toml мы можем увидеть записи, связанные с каждым созданным раннером, следующего вида:

concurrent = 1
check_interval = 0
 
[[runners]]
  name = "Runner_name"
  limit = 1
  id = XXXXXXXX
  url = "https://gitlab.com/"
  token = "xxxx-XXXXXXXXXXXXXXXXXXXX"
  executor = "shell"
  [runners.custom_build_dir]
    enabled = true
  [runners.cache]
    [runners.cache.s3]
    [runners.cache.gcs]
    [runners.cache.azure]

Пример заполнения:

[[runners]] 
name = "FastlaneProject/create_archive/1"
  limit = 1
  url = "https://gitlab.com"
  id = 36354822
  token = "XXXXXXXXXXXXX"
  token_obtained_at = 2024-05-07T14:48:23Z
  token_expires_at = 0001-01-01T00:00:00Z
  executor = "shell"
  builds_dir = "/Users/CICDUser/YYYYYY/ZZZZZZZ/cicd/builds"
  cache_dir = "/Users/CICDUser/YYYYYY/ZZZZZZZ/cicd/cache"
  [runners.custom_build_dir]
    enabled = true

Здесь стоит также отметить несколько параметров, а именно:

Параметр

Описание

concurrent = 1

ограничение на количество одновременно работающих раннеров

limit = 1

ограничение для раннера на количество одновременно работающих задач

builds_dir = "/Users/CICDUser/YYYYYY/ZZZZZZZ/cicd/builds"

путь, по которому будут выполняться задачи раннера. Обратите внимание, что ему предшествует директория cicd - это директория, где будет настроен весь процесс CI/CD

cache_dir = "/Users/CICDUser/YYYYYY/ZZZZZZZ/cicd/cache"

кэш раннера

5. Настройка Appfile

Для выполнения этого шага необходимо сперва донастроить [CI/CD SERVER].
Как получить и настроить Appfile будет указано ниже в секции “Настройка [CI/CD SERVER]".
Настроенный Appfile необходимо поместить в Secure Files [PROJECT repo]

Настройка [CI/CD SERVER]

1. Настройка Gitlab раннеров на локальной машинке (сервере)

Для настройки раннеров на локальной машине можно пользоваться официальной документацией GitLab. Переводим командную оболочку на bash, так как корректную работу на zsh GitLab не гарантирует. Проверяем текущий shell:

echo $SHELL

Eсли результат отличен от /bin/bash, то меняем следующей командой и перезапускаем терминал:

chsh -s /bin/bash

Если brew не установлен, то ставим:

/bin/bash -c "$(curl "https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh")"

Устанавливаем rbenv согласно шагам, описанным в описании к репо: инструкция по настройке rbenv
Ставим rbenv, чтобы использовать его вместо системного ruby:

brew install rbenv gitlab-runner 
brew services start gitlab-runner

Добавим rbenv в профайл:

echo 'if which rbenv > /dev/null; then eval "$(rbenv init -)"; fi' >> ~/.bash_profile
source ~/.bash_profile

проверяем версии ruby:

rbenv install -l 

И ставим актуальную версию ruby, на момент написания статьи - это версия 3.3.1:

rbenv install 3.3.1
rbenv global 3.3.1 

Корректируем .bashrc файл, добавив в него следующие строки. Не забудьте заменить заглушку CICDUser актуальным пользователем:

export PATH="/bin:/usr/bin:/usr/local/bin"
export LANG=en_US.UTF-8
export LANGUAGE=en_US.UTF-8
export LC_ALL=en_US.UTF-8
eval "$(rbenv init -)"
 
PATH=$PATH:/Users/CICDUser/bin:/usr/local/homebrew
PATH=$PATH:/Users/CICDUser/.rbenv/shims/
export PATH

Если не установлен Xcode - ставим его.
Для удобства работы с JSON файлами ставим утилиту jq (инструкция по настройке jq).

brew install jq

Выполняем установку gitlab runner согласно пункту 3 из Настройка [PROJECT repo]

2. Fastlane

Устанавливаем fastlane:

brew install fastlane

Теперь переходим по пути, где будет выполняться вся магия CI/CD - место для репозитория [CI/CD repo]. Мы его уже указывали в настройках раннеров - это родительская директория для кэша и билда раннеров:
"/Users/XXXXXX/YYYYYY/ZZZZZZZ/cicd/". Тут делаем клон репозитория [CI/CD repo] и разворачиваем fastlane (инструкция по установке fastlane).

cd /Users/CICDUser/YYYYYY/ZZZZZZZ/cicd/
git clone https://gitlab.com/AAAAAA/BBBBBB.git
...
fastlane init
...

Так как в данном репозитории нет еще проектов, получим следующее предупреждение:

[✔] 						
Источник: https://habr.com/ru/articles/821981/


Интересные статьи

Интересные статьи

Эта статья является продолжением краткого туториала по переводу приложений на .Net Aspire. В ее рамках будет рассмотрено развертывание Aspire приложения в локальном кластере Kubernetes.
Недавно закончила курс "Менеджер IT-проектов" от международной бизнес-школы Laba.Всего было 10 занятий.В этом посте делюсь впечатлениями от каждого из них в формате дневника — с юмором и пользой (иног...
В некоторых проектах сборке отводится роль Золушки. Основные усилия команда сосредоточивает на разработке кода. А самой сборкой могут заниматься люди, далёкие от разработки (например, отв...
Warning!Тут не будет очень глубокого разбора частот, волн и низкоуровневого преобразования звука. Это статья для тех, кто хочет быстро и максимально понятно, разобраться...
Предисловие Довольно часто пользователи, разработчики и администраторы СУБД MS SQL Server сталкиваются с проблемами производительности БД или СУБД в целом, поэтому весьма актуальным является мон...