Маленькой команде не составляет труда поддерживать историю изменений приложения в ручном режиме. Но, когда команда начинает расширяться, такой файл как changelog, находящийся в системе контроля версий, становится «узким горлышком» и приводит к постоянным конфликтам и росту напряжения в команде.
На помощь, как всегда, приходит автоматизация. Если интересно узнать о том, как автоматизировать генерацию changelog в gradle проекте, добро пожаловать под кат.
Как было в маленькой команде?
Для того, чтобы тестировщик всегда знал, что было сделано в конкретной сборке приложения, в системе контроля версий находился специальный файл с названием changelog.md
, в который каждый разработчик в Pull Request обязан был добавить короткую суть своих изменений. Обычно, у каждой записи в этом файле есть свой номер, который берется из Task Tracker. Выглядит это примерно так:
# Changelog
## 3.7.3
- PRJ-2982: Убрать кнопку конвертации с экрана рублевого счета
- PRJ-3021: Поменять навигацию после обмена валюты на главный экран
/* другие записи */
Этот файл прикладывался к каждой сборке в CI/CD при выкладке сборки в сервис доставки сборок тестировщикам.
Естественно, с ростом количества членов команды, в этом файле начали постоянно появляться merge-конфликты, что приводило к нездоровой атмосфере в команде, когда психологически разработчику хочется замержить свои изменения первым без конфликтов, а если не успел, то придется разруливать эти конфликты самому и опять ждать, когда пройдут стадии pipeline CI/CD.
Также сервисы доставки сборки обычно имеют ограничения на длину changelog.md
, что приводило еще к обязанности разработчику следить за удалением из этого файла устаревших записей. А удаленные ID записей складывать в специальную секцию под названием Folded
# Changelog
## 3.7.3
- PRJ-2982: Убрать кнопку конвертации с экрана рублевого счета
- PRJ-3021: Поменять навигацию после обмена валюты на главный экран
/* много записей */
## Folded
PRJ-2834 PRJ-2835 /* другие ID */
Решено было убрать этот файл из системы контроля версий и генерировать его автоматически из истории комитов. Так родился плагин для Gradle, который занимается генерацией changelog.md
с учетом GitFlow, который практиковался в нашей команде.
Почему свой велосипед?
Быстрое изучение готовых решений не увенчалось успехом. Каким бы хорошим решение ни было, чего-то в нем не хватало и никак не "натягивалось" на наш процесс разработки. А нам нужны были такие фичи:
- Плагин должен уметь собирать историю комитов от вершины текущей релизной ветки, до корня предыдущей, либо от вершины текущей ветки до корня предыдущей релизной ветки
- Плагин должен уметь пропускать комиты без номеров задач, т.е. комиты без "№: " не должны попадать в
changelog.md
- Плагин должен уметь автоматически следить за лимитом файла и схлопывать устаревшие записи в секцию
Folded
- Плагин должен давать возможность задать свой шаблон рендеринга
changelog.md
- Плагин должен уметь работать с приватными репозиториями Azure, Github и GitLab
Как пользоваться плагином?
Для начала добавьте плагин в build.gradle
plugins {
id 'com.a65apps.changelog' version '1.1.10'
}
Теперь нам остается только настроить плагин:
changelog {
def token = System.getenv().get("TOKEN") // PAT токен для доступа к приватному Git репозиторию(требуется для Azure, Github и GitLab)
currentVersion = '1.1' // Текущее имя релиза, по умолчанию - 'Unreleased'
lastReleaseBranch = "releases/1" // Последняя ветка релиза, обязательное поле
templateFile = "template/changelog.mustache" // Шаблон для рендеринга changelog.md, обязательное поле
accessToken = token // Токен для доступа к приватному Git репозиторию, по умолчанию - пустой
userName = "my_user_name" // Опциональное имя пользователя для доступа к приватному Git репозиторию(требуется для GitLab), по умолчанию - пустой
developBranch = 'master' // Общая ветка разработки, по умолчанию - 'develop'
}
Шаблон рендеринга использует движок mustache, к примеру, он может быть таким:
# Changelog
## {{title}}
{{#entries}}
{{message}}
{{/entries}}
## Folded
{{#shortEntries}}
{{foldId}}
{{/shortEntries}}
Описание полей для шаблона:
Поле | Описание |
---|---|
title |
значение поля currentVersion |
entries |
лист записей |
message |
короткая запиись из Git комита |
shortEntries |
лист схлопнутых записей |
foldId |
ID задачи(первые 8 символов хэша комита) |
Для корректной работы плагина есть еще дополнительное условие — в системе CI/CD, на задачи, требующие генерацию changelog.md
, должна быть выставлена опция для git — вытягивать историю всех веток проекта. Иначе плагин не сможет определить путь от головы текущей ветки до корня последней релизной ветки в проекте.
Теперь мы можем запускать генерацию changelog.md
одной командой:
./gradlew changelog
Сгенерированный файл changelog.md
будет по умолчанию в $buildDir/outputs/changelog.md
Дополнительные параметры настройки
Для более точной настройки можно воспользоваться дополнительными параметрами плагина:
changelog {
currentReleaseBranch = "releases/2" // Определяет текущую ветку релиза
local = true // Только для локальной отладки(если у Вас уже склонирован репозиторий)
characterLimit = 10_000 // Ограничивает лимит файла до этого значения, когда количество записей выйдет за пределы лимита, старые записи будут отправляться в Folded секцию
outputFile = "$buildDir/path/to/changelog.md" // Можно определить свой путь, куда будет сохраняться сгенерированный файл
entryDash = "*" // Свой символ записи в буллет листе, по умолчанию - '-'
templateExtraCharactersLength = 29 // В шаблоне есть статические символы - их можно посчитать и прописать здесь, для более точной работы лимитов
order = LogOrder.LAST_TO_FIRST // Порядок записей. По умолчанию LogOrder.FIRST_TO_LAST
minEntryCount = 10 // Минимальное количество полных записей в логе, нужно на случай если релиз очень большой и даже схлопнутые записи уже отъедают все возможные лимиты для changelog
}
Исходники плагина можно посмотреть здесь
Резюме
Благодаря автоматизации и удобной системе плагинов Gradle, нам удалось решить проблему со скучностью поддержания в ручном режиме такого важного файла как changelog.md
.
Это уменьшило вероятность ошибиться при обновлении лога изменений, т.к. убрало человеческий фактор из этого порцесса.
Уже несколько лет плагин успешно применяется на проектах и стал неотъемлемой частью DevOps культуры команды.