Автоматизация версионирования: Conventional Commits и Keep a Changelog

Эта статья поможет вам навести порядок в истории разработки: от оформления коммитов до автоматической генерации журналов изменений (Changelog).

Conventional Commits - Соглашение о коммитах

Conventional Commits - спецификация для оформления сообщений git-коммитов. Она делает историю проекта понятной не только людям, но и программам.

Структура сообщения

По стандарту сообщение состоит из 3 частей:

  • Заголовка
  • Тела
  • Футера.
<тип>[необязательная область]: <описание>
 
[необязательное тело]
 
[необязательный футер]

Заголовок(обязательно) - первая строка, до 72 символов.

В ней указывается:

  • Тип изменений - новый функционал, фикс бага и т.д.
  • Область - необязательно, контекст изменений: auth,api,profile-page
  • Описание - краткий лог изменений, в повелительном наклонении(например, add, а не added, или в русском языке - совершенный вид добавить, а не добавил).

Далее будет приведена таблица основных типов изменений и как они влияют на версирование проекта.

ТипЧто означаетВлияние на версию (SemVer)
featНовая функциональность (Feature)Minor (0.1.0 0.2.0)
fixИсправление ошибкиPatch (0.1.0 0.1.1)
docsИзменения только в документацииОбычно нет (или Patch)
styleПравки форматирования, пробелы (не меняет логику)Нет влияния
refactorИзменение кода, которое не правит баг и не добавляет фичуОбычно нет
perfИзменения для улучшения производительностиPatch
testДобавление или изменение тестовНет влияния
choreОбновление зависимостей, рутинные задачиНет влияния
Если нужно изменить major в версии (major.minor.patch), то к типу добавляется !.

Тело (Body) — Необязательно

Это основной блок текста, который отделяется от заголовка пустой строкой. Используется для детального объяснения причин и сути изменений.

  • Назначение: Описать «почему» сделано изменение и «как» оно работает (если это не очевидно). Здесь можно указать на контраст с предыдущим поведением кода.
  • Форматирование: Обычно ограничивается шириной в 72 символа для удобного чтения в терминале.
  • Стиль: Можно использовать маркированные списки и несколько абзацев. Важно сохранять фокус на технической ценности.

Футер (Footer) — Необязательно

Нижняя часть коммита, которая также отделяется пустой строкой от тела (или от заголовка, если тела нет). Используется для метаданных и связи с внешними инструментами.

  • BREAKING CHANGE: Самый важный элемент футера. Начинается со слов BREAKING CHANGE: (с двоеточием и пробелом), после чего идет описание того, что именно сломает совместимость. Это триггер для смены Major версии.
  • Связь с задачами (Refs): Здесь указываются ссылки на тикеты в Jira, GitHub Issues или другие задачи.
    • Пример: Fixes: #452, Closes: PROJECT-123.
  • Формат: Обычно строится по принципу Ключевое-слово: значение (например, Co-authored-by: Name <email>).

Пример правильного сообщения о коммите:

feat(api): добавить поддержку OAuth2 для авторизации             <-- Заголовок
 
Мы переходим с базовой авторизации на OAuth2, так как это        <-- Тело
безопаснее и позволяет интегрироваться с внешними сервисами.
Изменены эндпоинты /login и /refresh.
 
BREAKING CHANGE: старый метод AuthBasic больше не поддерживается. <-- Футер (Major)
Используйте заголовок Authorization: Bearer <token>.
 
Closes: #102, #105                                               <-- Футер (Ссылки)

Инструменты для правильного заполнения

Для vscode:

  1. Conventional Commits (автор vivaxy)
    • Как работает: Добавляет иконку в панель Git или вызывается через Ctrl+Shift+P. Он по очереди спрашивает: тип, область, описание, есть ли Breaking Change.
    • Плюс: Автоматически подставляет нужные префиксы и форматирует сообщение.
  2. GitLens Это «комбайн» для Git. В нем нет прямого мастера Conventional Commits, но можно настроить Commit Templates (шаблоны), которые будут подставляться в поле ввода сообщения.

Для терминала: Даже с расширениями можно случайно написать git commit -m "fix fast". Чтобы этого избежать, используют «контролеров».

  • Commitlint Это линтер для сообщений. Он проверяет текст коммита на соответствие стандарту. Если ты забыл двоеточие или написал непонятный тип, он выдаст ошибку.
  • Husky
    • Инструмент для создания Git-хуков. Обычно его используют вместе с Commitlint.
    • Сценарий: Ты жмешь Enter после коммита Husky запускает Commitlint Если сообщение плохое, коммит отменяется с подсказкой, что исправить.

Использование в проекте на nodejs:

npm install --save-dev @commitlint/config-conventional @commitlint/cli husky
 
# Создаст папку .husky, не забудьте добавить в gitignore и dockerignore
npx husky init 

Настройка Commitlint

Создать файл конфигурации .commitlintrc.json в корне проекта. Он скажет линтеру, что мы используем стандарт Conventional Commits.

{
  "extends": ["@commitlint/config-conventional"]
}

Создание хука (перехватчика)

Теперь нужно сказать Husky: «Перед тем как завершить коммит, проверь сообщение через commitlint». Необходимо создать файл .husky/commit-msg и добавь туда команду проверки:

#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
 
npx --no -- commitlint --edit "$1"

Использование в проекте на python:

pip install pre-commit

Если используется uv просто пишем:

uv add --dev pre-commit

Создаем файл .pre-commit-config.yaml:

repos:
  - repo: https://github.com/compilerla/conventional-pre-commit
    rev: v3.1.0
    hooks:
      - id: conventional-pre-commit
        stages: [commit-msg]
        args: []

Активируем хук:

pre-commit install --hook-type commit-msg
# или
uv tool run pre-commit install --hook-type commit-msg

Итог:

Таким образом наши коммиты будут автоматически проверяться на корректность заполнения commit message.

Keep a Changelog

Хороший журнал изменений пишется для людей. В качестве стандарта здесь будет описан Keep a Changelog 1.1.0.

Основные правила:

  1. Свежие версии — сверху.
  2. Группировка изменений по категориям: Added, Changed, Fixed, Removed.
  3. Версии должны быть ссылками на сравнение (Diff).

Группы изменений (Секции)

Все изменения внутри версии делятся на 6 стандартных категорий:

  • Added — для новых функций.
  • Changed — для изменений в существующей функциональности.
  • Deprecated — для функций, которые скоро будут удалены.
  • Removed — для функций, удаленных в этой версии.
  • Fixed — для любых исправлений ошибок.
  • Security — в случае исправления уязвимостей.

Пример корректного заполнения

Вот как выглядит идеально оформленный CHANGELOG.md по версии 1.1.0:

# Changelog

Все заметные изменения в этом проекте будут описаны в этом файле.
Формат основан на [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
и этот проект придерживается [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.1.0] - 2026-01-17

### Added
- Новый класс `UserManager` для обработки профилей пользователей.
- Метод `verify_email` в API аутентификации.
- Возможность экспорта отчетов в формате PDF.

### Changed
- Обновлена логика кэширования: теперь используется Redis вместо локальной памяти.
- Оптимизирован SQL-запрос в функции `get_stats`, что ускорило загрузку на 30%.

### Fixed
- Исправлена ошибка валидации пароля при регистрации (ранее пропускал пустые строки).
- Решена проблема с утечкой памяти в фоновом воркере.

## [1.0.1] - 2026-01-10

### Fixed
- Исправлен баг с отображением даты в футере сайта.

## [1.0.0] - 2026-01-01

### Added
- Первый стабильный релиз проекта.
- Базовая система авторизации (JWT).
- CRUD для работы с заметками.

[1.1.0]: https://github.com/user/project/compare/v1.0.1...v1.1.0
[1.0.1]: https://github.com/user/project/compare/v1.0.0...v1.0.1
[1.0.0]: https://github.com/user/project/releases/tag/v1.0.0

Автоматизация через git-cliff:

git-cliff — это генератор чейнджлогов на Rust.

Установка на nodejs

npm install -g @orhun/git-cliff
# или в проект
npm install --save-dev @orhun/git-cliff

Добавьте в package.json

"scripts": { "changelog": "git-cliff -o CHANGELOG.md" }

Установка на python

pip install git-cliff
# или в uv
uv add --dev git-cliff

Настройка шаблона (cliff.toml)

Этот конфиг строго следует стандарту Keep a Changelog и убирает лишние эмодзи и метаданные.

[changelog]
header = """
# Changelog
 
Все заметные изменения в этом проекте будут описаны в этом файле.
 
Формат основан на [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
и этот проект придерживается [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
"""
 
body = """
{% if version %}\
    ## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }}
{% else %}\
    ## [Unreleased]
{% endif %}\
{% for group, commits in commits | group_by(attribute="group") %}
    ### {{ group }}
    {% for commit in commits %}
        - {{ commit.message | upper_first }}\
        {% if commit.body %}
          {{ commit.body | indent(prefix="  ") }}\
        {% endif %}
    {% endfor %}
{% endfor %}
"""
 
footer = """
"""
 
trim = true
render_always = true
 
[git]
conventional_commits = true
trim_meta = true
filter_unconventional = true
sort_commits = "newest"
 
commit_parsers = [
    { message = "^Initial commit", group = "Added" },
    { message = "^feat", group = "Added" },
    { message = "^fix", group = "Fixed" },
    { message = "^doc", group = "Added" },
    { message = "^perf", group = "Changed" },
    { message = "^refactor", group = "Changed" },
    { message = "^style", group = "Changed" },
    { message = "^test", group = "Changed" },
    { message = "^chore", group = "Changed" },
    { message = "^revert", group = "Fixed" },
]

Использование Добавьте скрипт в свой проект:

  • В package.json: "changelog": "git-cliff -o CHANGELOG.md"
  • В терминале (Python/uv): uv run git-cliff -o CHANGELOG.md

Автоматическое управление версиями

Чтобы не менять версию в package.json или pyproject.toml вручную, используются инструменты, которые анализируют ваши коммиты и сами решают: поднять Patch, Minor или Major.

Для Node.js

Release-it — самый популярный инструмент в экосистеме JS.

Установка:

npm install --save-dev release-it

Настройка (.release-it.json):

{
  "hooks": {
    "before:init": "npm test",
    "after:bump": "npx git-cliff -o CHANGELOG.md && git add CHANGELOG.md",
    "after:release": "echo Successfully released ${version}."
  }
}

Теперь при запуске npx release-it программа сама обновит версию, запустит git-cliff, создаст коммит и повесит тег.

Разбор конфига:

  1. "before:init": "npm test" Это пре-проверка. Хук срабатывает в самом начале, еще до того, как инструмент что-либо изменит в проекте.
    • Что делает: Запускает вашу тестовую среду.
    • Зачем это нужно: Если тесты упадут (команда вернет ненулевой код ошибки), процесс релиза немедленно прервется. Это страховка от того, чтобы вы случайно не выпустили в релиз сломанный код.
  2. "after:bump": "npx git-cliff -o CHANGELOG.md && git add CHANGELOG.md" Это этап документирования. Хук срабатывает сразу после того, как release-it обновил версию в package.json (этот процесс называется bump), но до того, как будет создан Git-коммит релиза.
    • Что делает:
      1. npx git-cliff -o CHANGELOG.md — вызывает генератор чейнджлогов. Он читает ваши коммиты и записывает изменения в файл согласно вашему шаблону.
      2. && git add CHANGELOG.md — добавляет обновленный файл в индекс Git (staging area).
    • Зачем это нужно: Благодаря этому обновленный CHANGELOG.md попадет в тот же самый коммит, в котором изменилась версия в package.json. История будет выглядеть чистой и логичной.
  3. "after:release": "echo Successfully released ${version}." Это завершающий этап. Хук срабатывает, когда всё уже успешно завершено: версия поднята, тег создан, изменения отправлены в удаленный репозиторий (push).
    • Что делает: Просто выводит в консоль сообщение об успехе.
    • Переменная ${version}: Это встроенная переменная release-it. Вместо нее подставится актуальный номер версии (например, 2.0.0).
    • Зачем это нужно: Для уведомления пользователя или для интеграции с другими системами (например, отправка сообщения в Slack или Telegram через curl).

Для Python

В Python (особенно с использованием uv) отлично подходит bump-my-version.

Установка:

uv add --dev bump-my-version

Настройка (pyproject.toml):

[tool.bumpversion]
current_version = "0.1.0"
parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"
serialize = ["{major}.{minor}.{patch}"]
search = "version = \"{current_version}\""
replace = "version = \"{new_version}\""
files = ["pyproject.toml"]
commit = true
tag = true
 
# Аналог "after:bump" из nodejs
# Этот хук запустится ПОСЛЕ обновления версии в файлах, но ДО создания коммита
pre_commit_hooks = [
    "git-cliff --tag v{new_version} -o CHANGELOG.md",
    "git add CHANGELOG.md"
]

Разбор конфига:

В bump-my-version (и обновленном bump2version) логика работы с хуками выглядит так: pre_commit_hooks: Выполняются сразу после того, как утилита изменила версию в pyproject.toml, но до выполнения команды git commit. - git-cliff --tag v{new_version} -o CHANGELOG.md: Генерирует лог, подставляя вместо {new_version} ту версию, на которую вы переходите. - git add CHANGELOG.md: Обязательный шаг. Если не добавить файл в индекс, bumpversion создаст коммит только с измененным pyproject.toml, а изменения в логе останутся “незакоммиченными”.

Команды для релиза:

# Для патча (0.1.0 -> 0.1.1)
uv run bump-my-version bump patch
 
# Для минорной версии (0.1.0 -> 0.2.0)
uv run bump-my-version bump minor
 
# Для майорной версии (1.0.0 -> 2.0.0)
uv run bump-my-version bump major