Привет, Хабр! Меня зовут Паша Филимонов и я Android-разработчик в Учи.ру. Наша небольшая команда занимается разработкой мобильных приложений — «Учи.ру» и «Учи.ру для учителей». Мы сторонники автоматизации и считаем, что настройка CI/CD необходима  для улучшения процессов разработки продукта. Каждый раз, приступая к новой сборке приложения, нам приходилось вручную прописывать ее код версии. Это довольно рутинная задача, которую мы решили автоматизировать. Рассказываю, как мы это сделали. Наши билды собираются с помощью сервисов Github Actions. У нас есть 3 основных типа сборки:  stage — для тестирования фичи;  release candidate — stage сборка — релиз кандидат для регрессионного тестирования; release — готовая сборка для публикации в Play Store.  Когда мы делали новую версию приложений, то вручную прописывали новый воркфлоу в гитхабе, указывая необходимые параметры: versionName — версия приложения, в которую пойдет новый функционал; versionCode — код версии (инкремент последней сборки); releaseNotes — описание того, что изменилось в этой сборке. Код версии для каждой новой сборки уникален, его значение равно инкременту предыдущей сборки (исключением являются релизные сборки). В примере ниже показана трансформация versionCode, который указан в скобках: После того как сборка была готова, в канал нашей команды в Slack приходило соответствующее уведомление с вышеуказанными параметрами и автором инициализации воркфлоу. Здесь и возникает проблема: каждый раз, инициируя новую сборку, нам нужно знать versionCode предыдущей.  Самый просто способ его выяснить — открыть канал в Slack, посмотреть код версии последней сборки и в уме прибавить к нему единицу. Это задача постоянно отнимала время, поэтому мы решили автоматизировать процесс. К тому же мы не застрахованы от race condition — ситуации, когда несколько человек могут создать сборку с одинаковыми значениями кода версии. Так выглядел наш набор параметров, необходимых для создания сборки: поля Version code и Version name являлись обязательными для заполнения. Мы решили избавиться от ручного указания номера и кода версии при создании новой сборки.  Для этого нам было необходимо определить мастер-систему (хранилище), в которой будут храниться актуальные данные о последней сборке. Это позволило бы автоматически получать данные о сборке и вычислять номер новой. При этом мы хотели оставить возможность указывать номер и код версии вручную, чтобы при проблемах с доступом в хранилище можно было самостоятельно указывать необходимые данные и не останавливать процесс разработки. А также в частном порядке создавать сборки с нужными версиями, не меняя ничего в хранилище. Один из вариантов решения — хранить эти данные в файле в текущем проекте. Но этот подход заведомо обречен, поскольку feature-ветки не будут знать про изменения версии в других ветках. А при объединении веток будут постоянные конфликты из-за изменений в одном файле с версией. Поэтому в первую очередь было необходимо найти хранилище, которое: бесплатное; имеет возможность записывать/читать данные без ограничений (или с адекватными лимитами); обеспечивает безопасное хранение данных; позволяет просматривать и редактировать данные в реальном времени; простое во внедрении. Нам важно, чтобы внедряемая база данных требовала минимальных усилий для поддержания ее работоспособности. Вот какие решения мы рассматривали. Варианты хранилища: Попросить DBA «поднять» отдельную базу данных для наших артефактов Отличный вариант для тех, у кого есть такая возможность. Плюсы: артефакты будут храниться в рамках вашей  компании; неограниченный круг возможностей в проектировании. Минусы: зависимость от других отделов/людей, у которых не всегда будет время на наши нужды. 2. Использовать отдельный репозиторий Плюсы: прост в использовании. Минусы: неудобство ручного редактирования данных. Для изменения данных вручную, придется коммитить и пушить в этот репозиторий, что также может привести к конфликтам. Использовать Github gists Плюсы:  прост в использовании. Минусы:  Github gists могут быть привязаны только к пользователю. Чтобы не привязывать их к определенному члену команды, потребуется создать дополнительный аккаунт с доступом к репозиторию. Внедрить стороннюю онлайн базу данных Плюсы:  независимое хранилище. Минусы:  дополнительная точка отказа; большинство сервисов предлагают свои услуги, которые требуют инвестиций. Использовать Firebase Его можно отнести к предыдущему варианту, но я выделю его отдельно, потому как в нашем проекте мы активно используем сервисы от Firebase, и он не станет для нас новой точкой отказа. Плюсы:  несколько способов доступа к данным (с помощью cUrl и пользовательский интерфейс); бесплатный пакет; прост в подключении и использовании. Минусы:  лимиты в бесплатной версии, но для нашей задачи их хватит с головой (на момент публикации статьи: 1 гб. на хранение данных, 10 гб. в месяц на загрузку данных). Так как мы активно используем сервисы Firebase, мы решили, что интегрировать их базы данных будет рационально: нам не придется подключать к процессу сторонний сервис. Firebase предоставляет два типа баз данных: Realtime Database и Cloud Firestore. Различия между ними можете прочитать здесь и здесь.  На текущий момент наши потребности просты: записывать и читать данные строго для последней сборки. Поэтому мы остановились на самом подходящем варианте — Realtime Database. Бесплатный план имеет лимиты, но в нашем случае нам этого хватает. Документация по Realtime Database и Cloud Firestore. Пример, как можно обновить данные в Realtime Database с помощью cUrl: Основной флоу на CI/CD у нас уже был настроен. Нам оставалось добавить получение, инкрементацию и сохранение сборки в Firebase. Мы создаем повторно используемый воркфлоу, чтобы переиспользовать его логику в двух приложениях — «Учи.ру» и «Учи.ру для учителя». Затем объявляем выходные параметры VERSION_NAME и VERSION_CODE, связываем их с выходными параметрами задачи InitAppVersion. На вход передаем Uri базы данных, к которой будем обращаться за данными. Объявляем задачу, которая будет выполнять инициализацию значений текущей версии, а также — выходные параметры этой задачи: Далее разбиваем задачу на шаги:  Получаем из базы последние значения versionName и versionCode и объявляем их как выходные параметры этого шага. Он выполняется, если нет введенных вручную номера или кода версии. Все запросы к хранилищу мы выполняем с помощью cURL запросов. Записываем данные в переменные окружения. Введенные вручную значения мы оставляем приоритетными, чтобы у нас всегда была возможность задать руками версию приложения. Сохраняем новое значение кода версии в БД только в том случае, если код версии не был введен вручную. Мы не сохраняем номер версии (versionName) в БД из рабочего процесса (подробнее ниже). Инициализируем выходные параметры задачи. Так выглядит наш набор параметров, необходимых для создания сборки, после изменений: Теперь поля Version code и Version name стали необязательными: заполнять их нет необходимости. Данные автоматически подтягиваются из БД, если не введены вручную. Однако все еще осталась нерешенной проблема с сохранением номера версии: при работе над несколькими релизами одновременно придется постоянно указывать номер вручную, поскольку в БД хранится только одно его значение. Релизные сборки также подверглись небольшим изменениям. Теперь перед тем как запустить релизную сборку, мы создаем Release Candidate(RC). После этого — тег для коммита, для которого запускается сборка вида: v1.1(256). Ее название не имеет значения, главное — она должна содержать номер (versionName) и код (versionCode) версии. При создании релизной сборки из того же коммита, что и RC, мы можем получить тег. Затем вытащить из него нужную информацию по номеру и коду версии и присвоить их релизной сборке. Важно лишь указать то, как мы получаем эти данные из тега: Например, для версии v1.1(256): Таким образом, нам удалось решить проблему ручной инкрементации сборки, избавиться от проблем при асинхронных действиях разработчиков и делегировать рутинные операции машине. Для этого мы использовали инструменты автоматизации от Firebase. В статье мы поделились и альтернативными вариантами решения этой проблемы. В будущем мы планируем решить проблему с сохранением номера версии, а также тщательно проработать идею формирования changelog’а из описания пул-реквестов или номера задач в Jira. Полный код воркфлоу на Github. Если ты разделяешь наш подход к аналитике, присоединяйся, чтобы вместе с нами развивать школьный EdTech.  Android developer