fix(release): extract only current version section for release notes #27

Merged
slaid098 merged 2 commits from fix/release-extract-version-section into master 2026-08-12 17:05:41 +03:00
Owner

Что сделано

  • scripts/create_release.py: f.read() заменён на extract_changelog_section(path, tag) (импорт из нового модуля scripts/release_notes.py)
  • Экстрактор: regex-поиск секции по тегу, срез заголовка/даты/шапки/trailing-links
  • Fallback: секция не найдена → весь CHANGELOG + warning; CHANGELOG отсутствует → пустая строка; tag не похож на версию → весь CHANGELOG
  • Модуль scripts/release_notes.py вынесен отдельно — импорт функции не триггерит module-level env-код create_release.py (GITHUB_* vars), тестируемо
  • scripts/__init__.py добавлен — mypy видит scripts как пакет (иначе "found twice under different module names")
  • Тесты tests/test_create_release.py: 12 cases (happy, тире/дефис, not-found, missing, empty, шапка, links, multisection, trailing-ws, tag без v, tag не версия)

Почему

Forgejo уже показывает тег и дату релиза в UI — дублирование шапки # Changelog, boilerplate про Keep a Changelog и self-referential ссылки [0.1.0]: https://.../releases/tag/v0.1.0 (ссылка на страницу, где уже находится пользователь) в release notes. Тот же фикс что в video_uniq issue #9.

Watch out

  • Идемпотентность (delete-then-create) не тронута
  • Загрузка zip-attachment не тронута
  • Формат заголовка покрывает и - (дефис) и (тире): regex ^## \[VERSION\]\s*[-—]\s*\d{4}-\d{2}-\d{2}
  • Ruff W292 в scripts/gen_phrases.py — pre-existing (на master), не трогал

Pending

Closes #24

## Что сделано - `scripts/create_release.py`: `f.read()` заменён на `extract_changelog_section(path, tag)` (импорт из нового модуля `scripts/release_notes.py`) - Экстрактор: regex-поиск секции по тегу, срез заголовка/даты/шапки/trailing-links - Fallback: секция не найдена → весь CHANGELOG + warning; CHANGELOG отсутствует → пустая строка; tag не похож на версию → весь CHANGELOG - Модуль `scripts/release_notes.py` вынесен отдельно — импорт функции не триггерит module-level env-код `create_release.py` (GITHUB_* vars), тестируемо - `scripts/__init__.py` добавлен — mypy видит `scripts` как пакет (иначе "found twice under different module names") - Тесты `tests/test_create_release.py`: 12 cases (happy, тире/дефис, not-found, missing, empty, шапка, links, multisection, trailing-ws, tag без v, tag не версия) ## Почему Forgejo уже показывает тег и дату релиза в UI — дублирование шапки `# Changelog`, boilerplate про Keep a Changelog и self-referential ссылки `[0.1.0]: https://.../releases/tag/v0.1.0` (ссылка на страницу, где уже находится пользователь) в release notes. Тот же фикс что в video_uniq issue #9. ## Watch out - Идемпотентность (delete-then-create) не тронута - Загрузка zip-attachment не тронута - Формат заголовка покрывает и `-` (дефис) и `—` (тире): regex `^## \[VERSION\]\s*[-—]\s*\d{4}-\d{2}-\d{2}` - Ruff W292 в `scripts/gen_phrases.py` — pre-existing (на master), не трогал ## Pending — Closes #24
style(release): apply ruff format to release_notes and tests
All checks were successful
CI / lint (pull_request) Successful in 1m31s
CI / complexity (pull_request) Successful in 1m31s
CI / typecheck (pull_request) Successful in 1m32s
CI / test (pull_request) Successful in 4m8s
43c8064061
Author
Owner

Code Review Summary

PR заменяет f.read() в scripts/create_release.py на вызов extract_changelog_section() из нового модуля scripts/release_notes.py. Экстрактор находит секцию текущей версии в CHANGELOG.md, срезает заголовок/дату/шапку/trailing self-referential links. Качество кода высокое, тесты покрывают все граничные случаи.

Что проверено

  • git diff master...HEAD — 4 файла, +222/-2
  • uv run ruff check scripts/release_notes.py tests/test_create_release.py scripts/create_release.py — All checks passed
  • uv run mypy scripts/ — Success: no issues found in 4 source files
  • uv run pytest tests/test_create_release.py — 12 passed
  • uv run pytest (весь набор) — 223 passed, coverage 78.29%
  • uv run ruff check scripts/ tests/ — единственная ошибка W292 в scripts/gen_phrases.py — pre-existing на master (PR не модифицирует этот файл, git diff master -- scripts/gen_phrases.py пусто)
  • uv run xenon scripts/release_notes.py — rank C для функции, но CI запускает xenon только на src/ (uv run xenon ... src/), не блокирует
  • Cross-file: rg "release_notes|extract_changelog_section|CHANGELOG" — writer↔reader в одном PR (create_release.py обновлён совместно), внешних readers нет

Соответствие критериям приёмки issue #24

  • f.read() заменён на экстрактор (create_release.py:37)
  • Экстрактор: regex ^## [VERSION]\s*[-—]\s*\d{4}-\d{2}-\d{2}, срез заголовка (lines[1:]), шапки (6 фильтров RU+EN), trailing links (^\[.*\]:\s*https://.*$)
  • Fallback: секция не найдена → text.strip() + logger.warning
  • Fallback: CHANGELOG отсутствует → return "" (не падает)
  • Доп. fallback: tag не похож на версию → весь файл + warning
  • Тесты: 12 cases — happy, em-dash, hyphen, not-found, missing, empty, шапка, links, multisection, trailing-ws, tag без v, tag не версия
  • ruff + mypy проходят
  • Тесты проходят (223 passed)

Positives

  • Экстрактор вынесен в отдельный модуль — импорт не триггерит module-level env-код create_release.py (GITHUB_* vars), тестируемо
  • Фильтр шапки покрывает RU + EN boilerplate (Keep a Changelog)
  • Regex покрывает и дефис - и тире через [-—]
  • scripts/__init__.py решает mypy "found twice under different module names"
  • Идемпотентность (delete-then-create) и загрузка zip не тронуты
  • PR body структурирован (Что сделано, Почему, Watch out, Pending)

Suggestions (info, not blocking)

  • scripts/release_notes.py:7 [style] Используется logging модуль вместо loguru (конвенция проекта для src/voice_assistant/). Для scripts/ tooling это допустимо — create_release.py уже использует print(), а logging.getLogger(__name__) — стандартный паттерн для library-функций. Не блокер.
  • scripts/release_notes.py:53-60 [complexity] List comprehension с 6 условиями фильтрации даёт McCabe rank C. CI не проверяет complexity для scripts/ (только src/), но при расширении фильтров стоит вынести в именованную функцию _is_boilerplate(line).

Verdict: APPROVE

## Code Review Summary PR заменяет `f.read()` в `scripts/create_release.py` на вызов `extract_changelog_section()` из нового модуля `scripts/release_notes.py`. Экстрактор находит секцию текущей версии в CHANGELOG.md, срезает заголовок/дату/шапку/trailing self-referential links. Качество кода высокое, тесты покрывают все граничные случаи. ### Что проверено - `git diff master...HEAD` — 4 файла, +222/-2 - `uv run ruff check scripts/release_notes.py tests/test_create_release.py scripts/create_release.py` — All checks passed - `uv run mypy scripts/` — Success: no issues found in 4 source files - `uv run pytest tests/test_create_release.py` — 12 passed - `uv run pytest` (весь набор) — 223 passed, coverage 78.29% - `uv run ruff check scripts/ tests/` — единственная ошибка W292 в `scripts/gen_phrases.py` — pre-existing на master (PR не модифицирует этот файл, `git diff master -- scripts/gen_phrases.py` пусто) - `uv run xenon scripts/release_notes.py` — rank C для функции, но CI запускает xenon только на `src/` (`uv run xenon ... src/`), не блокирует - Cross-file: `rg "release_notes|extract_changelog_section|CHANGELOG"` — writer↔reader в одном PR (create_release.py обновлён совместно), внешних readers нет ### Соответствие критериям приёмки issue #24 - ✅ `f.read()` заменён на экстрактор (`create_release.py:37`) - ✅ Экстрактор: regex `^## [VERSION]\s*[-—]\s*\d{4}-\d{2}-\d{2}`, срез заголовка (`lines[1:]`), шапки (6 фильтров RU+EN), trailing links (`^\[.*\]:\s*https://.*$`) - ✅ Fallback: секция не найдена → `text.strip()` + `logger.warning` - ✅ Fallback: CHANGELOG отсутствует → `return ""` (не падает) - ✅ Доп. fallback: tag не похож на версию → весь файл + warning - ✅ Тесты: 12 cases — happy, em-dash, hyphen, not-found, missing, empty, шапка, links, multisection, trailing-ws, tag без v, tag не версия - ✅ ruff + mypy проходят - ✅ Тесты проходят (223 passed) ### Positives - Экстрактор вынесен в отдельный модуль — импорт не триггерит module-level env-код `create_release.py` (GITHUB_* vars), тестируемо - Фильтр шапки покрывает RU + EN boilerplate (Keep a Changelog) - Regex покрывает и дефис `-` и тире `—` через `[-—]` - `scripts/__init__.py` решает mypy "found twice under different module names" - Идемпотентность (delete-then-create) и загрузка zip не тронуты - PR body структурирован (Что сделано, Почему, Watch out, Pending) ### Suggestions (info, not blocking) - **scripts/release_notes.py:7** [style] Используется `logging` модуль вместо loguru (конвенция проекта для `src/voice_assistant/`). Для `scripts/` tooling это допустимо — `create_release.py` уже использует `print()`, а `logging.getLogger(__name__)` — стандартный паттерн для library-функций. Не блокер. - **scripts/release_notes.py:53-60** [complexity] List comprehension с 6 условиями фильтрации даёт McCabe rank C. CI не проверяет complexity для `scripts/` (только `src/`), но при расширении фильтров стоит вынести в именованную функцию `_is_boilerplate(line)`. ### Verdict: APPROVE
slaid098 merged commit 826ee34405 into master 2026-08-12 17:05:41 +03:00
slaid098 deleted branch fix/release-extract-version-section 2026-08-12 17:05:41 +03:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
slaid098/voice_assistant!27
No description provided.