Версионирование. Автоматизация. А может всё вместе?
Всё уже придумано, просто настрой под себя и пользуйся.
Всем привет, я Дмитрий Валеев — DevOps инженер в команде разработки фреймворка для тестирования устройств на заводах Аквариус. И это моя первая маленькая статья на Хабр :)
В любом проекте настаёт день «икс», когда вы понимаете, что необходимо внедрять какое-либо версионирование. Вот и в нашем случае настал такой момент. Вариант с хэш-коммитами казался просто хаосом и нужно было привести вид компонент в порядок
Введение
Как показывает практика, часто версионирование просто игнорируется. Но любой, кто так или иначе пытался организовать свою разработку (в том числе пет-проекты), приходил к мысли о его необходимости.
В этой статье описан очень простой подход к автоматизации присвоения версий и написания патч-ноутов для проектов. Семантическое версионирование, а также соглашение о коммитах с открытой спецификацией и документаций поможет не придумывать новые стандарты.
Сразу обозначу, в качестве VCS (Version Control System) выбран «GitLab». Поэтому почти всё описанное взаимодействие в статье будет происходить с данным инструментом.
Структура версий
Структура версии
1 — Major версия
Мажорная версия изменяется только в случае обратно несовместимых изменений
2 — Minor версия
Минорная версия изменяется только в случае добавления нового функционала БЕЗ обратно несовместимых изменений
3 — Патч версия
Патч версия изменяется в случае выпуска каких-либо исправлений, незначительных улучшений
dev — Название prerelease/разрабатываемых состояний проекта
4 — Номер билда/коммита для пререлиз состояний версии
»-dev. 4» используется в любых видах пререлиз веток (Например: alpha; beta; dev; rc…).
Спецификация по написанию коммитов
Спецификации по написанию коммитов — это предмет договоренности внутри вашей команды. Она может быть изменена любым удобным способом. Однако, существует так называемая договоренность о коммитах (conventional commits), рекомендую ознакомиться с ней, хотя бы как с базовым примером того, как это может выглядеть.
Перейдём к краткому описанию правил коммитов:
fix: something
fix (PROJECT-999): fix something
feat: something
feat (ui): Add something
feat!: something
BREAKING CHANGE: something
«BREAKING CHANGE» находится именно в сноске коммит месседжа. Восклицательный знак в данном случае предупреждает о наличии «BREAKING CHANGE» в сноске коммита. Так выглядит в гитлабе:
Коммит в гитлабе
Можно использовать и другие типы коммитов. Все, кроме вышеописанных, не влияют на изменение версии релиза (здесь описаны лишь некоторые):
ci: Изменения в наших конфигурационных файлах и скриптах CI.
docs: Изменения документации.
refactor: Рефакторинг кода, не затрагивающий баг-фиксы и не добавляющий функциональность.
style: Изменения, которые не влияют на смысл кода (пробелы, форматирование, отсутствие точек с запятой и т.д.).
test: Добавление недостающих или исправление существующих тестов.
Рекомендую ознакомиться с одним из таких соглашений по ссылкам: на английском и на русском.
Инструмент «Semantic-release»
Напишем своё или возьмём уже готовое?
Рекомендую обратить внимание на инструмент semantic-release. Это решение на базе проекта с открытым кодом, написанное на node.js, позволяющее без особых проблем писать CHANGELOG, создавать теги или релизы, вносить любые автоматизированные изменения в репозиторий от имени бота с выпуском новой версии. И всё это без участия человека.
Решение также помогает поддерживать legacy. Например:
Заморозили старую версию 1.X.X, но вносим в неё необходимые фиксы и патчи в legacy ветке.
Ведем версию 2.X.X в master ветке.
Для обеих версий доступны dev ветки/каналы для разработки.
Ещё один плюс данного инструмента — наличие в арсенале разнообразных плагинов. Например: changelog, docker, exec и.т.д., которые открывают возможность создавать GitLab/GitHub Release вместе с тегом, автоматизировать ваши собственные изменения при выпуске версии и многое другое.
Как работает semantic-release
При каждом коммите инструмент анализирует его содержимое и существующие версии. На основе этого анализа принимается решение — выпускать новую версию или нет. (См. спецификацию по написанию коммитов)
Каждая новая версия автоматически создаёт тег.
Теги в репозитории
Теги в репозитории
А так выглядят коммиты самого бота с выпуском тега:
Полный коммит бота
Как изменяются версии пре-релиз веток (на примере «dev»)
При внесении изменений в dev-ветку версия изменяется в зависимости от целевого коммита.
То есть, если мы ведем dev ветку с версии 1.0.0, у нас есть 2 коммита «feat» и 3 «fix» в любом порядке, то текущая версия в dev будет 1.1.0-dev. 5. Неважно, сколько новых фич или фиксов было добавлено в dev, изменение версии отражает, что было добавлено хотя бы одно улучшение. Оно же показывает, какого рода были самые критичные изменения. Для наглядности картинка ниже:
Как добавить semantic-release к себе в проект
Не буду переписывать инструкцию, а просто оставлю ссылку на удобную документацию от разработчиков и покажу один из вариантов настройки:
1. Соберем контейнер для раннера и положим туда semantic-release с нужными плагинами
Как пример базовой сборки:
FROM node:18.14.2-alpine3.17
COPY certs/. /etc/ssl/certs/.
RUN apk --no-cache add curl; \
apk --no-cache add git; \
apk --no-cache add ca-certificates && \
update-ca-certificates -v
RUN npm config set prefix /usr/local; \
npm install -g @semantic-release/gitlab@v10.0.1; \
npm install -g @semantic-release/exec; \
npm install -g @semantic-release/git; \
npm install -g @semantic-release/changelog; \
npm install -g semantic-release
ENTRYPOINT [""]
CMD [""]
2. Создадим бота в GitLab и токен для него с правами api, write_repository. Это нужно для того, чтобы бот мог коммитить в репозиторий автоматические изменения.
3. Передадим токен через ci/cd variables. Можете добавить сразу в группу репозиториев.
Токен в ci/cd variables
4. Напишем базовую джобу
a. Если не используете ci templates:
stages:
- semantic-release
release-job:
image: *your_registry*/semantic_release:latest
stage: semantic-release
only:
refs:
# Release branch
- master
# Pre-release branch
- dev
script:
- npx semantic-release
tags:
- *your_tag*
b. Если пользуетесь ci templates:
.release-job:
stage: semantic-release
image: *your_registry*/semantic_release:latest
only:
refs:
# Release branch
- master
# Pre-release branch
- dev
script:
- npx semantic-release
tags:
- *your_tag*
artifacts:
reports:
dotenv: release_version.env
.gitlab-ci.yml в проекте:
include:
project: path/to/your/templates
file: path/to/your/template.yml
stages:
- semantic-release
semantic-release:pypi:
extends: .release-job
stage: semantic-release
5. Добавим в репозиторий .releaserc.json
{
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
["@semantic-release/exec",{
"prepareCmd": "if [ -f .release.override ]; then echo \"Detected .release.override\" && VERSION=${nextRelease.version} && source .release.override; else sed -i \"s/__version__ *= *.*/__version__ = \\\"${nextRelease.version}\\\"/\" ./setup.py; fi",
"publishCmd": "echo NEW_VERSION=${nextRelease.version} >> release_version.env"
}],
["@semantic-release/changelog",{
"changelogFile": "CHANGELOG.md"
}],
["@semantic-release/git", {
"assets": ["CHANGELOG.md", "setup.py"],
"message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
}
]
],
"branches": [
"master",
{"name": "dev", "prerelease": true}
],
"tagFormat": "v${version}"
}
Здесь, например, мы можем внести любое автоматическое изменение в prepareCmd, а в assets указать файлы, которые будут участвовать в коммите бота. На выходе мы будем иметь коммит и тег от бота, с уже измененными файлами.
Автоматизация CHANGELOG (-а)
Вернемся к данному коммиту:
fix (PROJECT-999): fix something
В скобках, согласно соглашению, указывается изменяемая компонента кода. И это очень удобный подход, но мы решили указывать в скобках решаемую задачу из таск-менеджера (их может быть и несколько через пробел). Таким образом, мы получили более богатый авто-генерируемый changelog и повысили общую читаемость. Вот пример:
CHANGELOG
Опять же, changelog пишется автоматически, если мы оставляем в assets «CHANGELOG.md», а также устанавливаем и добавляем необходимый плагин (см. настройку semantic-release пункт 5).
Самый простой сценарий использования
Коммит разработчика в формате semver.
Автоматическое вычисление версии с помощью semantic-release.
Автоматическое изменение файла setup.py и CHANGELOG.md.
Сборка и последующая доставка пакета.
Коммит/выпуск тега (релиза) с уже измененной версией.
В заключении
Семантическое версионирование вполне вяжется с подходом к разработке gitflow. Мы можем вести все пререлиз ветки с версиями.
Большинство наших репозиториев и компонент версионируется именно в таком формате, на них также завязаны job (-ы) по сборкам и публикации/деплою. Всё это избавляет нас от большого объема ручной работы и прививает в команде единый стандарт написания коммитов.
В действительности такая автоматизация настраивается очень быстро и не требует трудоемких операций. Попробуйте:)