Глава 23 · Часть VI · Выпускаем первую версию

Документация, версия и первый релиз

MAJOR.MINOR.PATCH, CHANGELOG.md, Git tag и GitHub Release дают проекту точку, к которой можно вернуться и на которую можно ссылаться.

SafeSort · Часть 6 из 6
Git и GitHubПланированиеПроектРеализацияТесты и CIРелиз

Проект готов, проверен тестами и подключён к автоматической проверке. Мы закончили первую пригодную для использования версию SafeSort. Теперь ей нужен номер, чтобы отличать её от будущих изменений. Этот номер мы уже записали в pyproject.toml ещё в части III, где он был просто техническим полем. Сейчас разберём, что он означает.

0MAJOR: несовместимыеизменения1MINOR: новаявозможность, староеповедение сохранено0PATCH: исправлениеошибки
В 0.1.0 каждое из трёх чисел отвечает за свой тип изменений

Семантическое версионирование

Эта схема называется семантическим версионированием (Semantic Versioning, SemVer), то есть соглашение о записи номера версии как MAJOR.MINOR.PATCH:

Часть номераМеняется, когда
MAJORнесовместимые изменения; старый способ использования перестаёт работать
MINORдобавлена новая возможность, старое поведение сохранено
PATCHисправлена ошибка, поведение по сути не изменилось
Соглашение, а не закон физики
Семантическое версионирование задаёт общепринятую договорённость, но не встроено в инструменты правило: ничто технически не мешает нарушить его смысл. Пользы от него ровно столько, сколько сам проект последовательно ему следует. Это ориентир для тех, кто устанавливает пакет и хочет понимать, чего ждать от новой версии.

Первая версия SafeSort имеет номер 0.1.0. Минорная версия ниже 1 традиционно означает «интерфейс ещё может измениться без отдельного согласования».

Что изменилось в этой версии: CHANGELOG

CHANGELOG.md
## [0.1.0]

### Added

- scan, plan, apply, duplicates, undo commands

### Safety

- dry-run by default (scan/plan/duplicates never modify files)
- no automatic duplicate deletion
- no silent overwrite of existing files
CHANGELOG описывает только реальные версии
CHANGELOG не должен придумывать более ранние версии, которых не существовало. Он описывает выпущенные изменения, начиная с первой версии проекта.

Собираем wheel и sdist

Релиз начинается с исходного дерева, но пользователю передают distribution artifacts. Команда python -m build создаёт оба стандартных формата:

Терминал (корень репозитория SafeSort)
python -m pip install --upgrade build
python -m build
dist/
safesort-0.1.0-py3-none-any.whl
safesort-0.1.0.tar.gz
wheel содержит готовый архив Python distribution; sdist содержит исходники для сборки
АртефактНазначение
wheel (.whl)предсобранный архив distribution: установка обычно не запускает сборку проекта
sdist (.tar.gz)архив исходников и метаданных, из которого инструмент может собрать wheel

Smoke test в чистом окружении

Editable install проверяет разработку, но не доказывает, что собранный wheel содержит нужные файлы и console script. Поэтому устанавливаем именно artifact в новое окружение:

POSIX shell
python -m venv .release-smoke
source .release-smoke/bin/activate
python -m pip install dist/safesort-0.1.0-py3-none-any.whl
python -c "import safesort; print(safesort.__file__)"
safesort --help
deactivate

На Windows активация выполняется командой .release-smoke\Scripts\activate. Проверяется та же установленная distribution, а не исходники из src/.

GitHubGitHub Release строится поверх тега

Git tag и GitHub Release: разные объекты

Тег (tag) хранит Git-ссылку на конкретный коммит и обычно соответствует номеру версии. Технически тег можно передвинуть или удалить, но опубликованные release-теги проект по политике считает неизменяемыми. GitHub Release служит отдельным объектом GitHub, созданным вокруг тега. У него есть страница с описанием и прикреплёнными artifacts. Push тега сам по себе Release не создаёт.

Тег версии относится ко всему репозиторию целиком, а не к одной его части. Поэтому SafeSort живёт в собственном репозитории Cartesian-School/safesort, а не только в подкаталоге курса: git tag v0.1.0 здесь однозначно означает «версия 0.1.0 SafeSort», без двусмысленности.

~/safesort $ git tag -a v0.1.0 -m "SafeSort 0.1.0 — first release"
~/safesort $ git push origin v0.1.0
To https://github.com/Cartesian-School/safesort.git
* [new tag] v0.1.0 -> v0.1.0

Затем в веб-интерфейсе GitHub откройте Releases → Draft a new release, выберите существующий тег v0.1.0, добавьте выдержку из CHANGELOG и прикрепите оба файла из dist/. После этого у тега появляется человекочитаемая страница релиза и загружаемые artifacts.

Страница релиза SafeSort 0.1.0 на GitHub: заголовок, метка Latest, описание, команда pip install, прикреплённые файлы wheel и sdist
Релиз SafeSort 0.1.0: тег, описание изменений и собранные пакеты wheel и sdist.
Релиз создан только после того, как всё остальное было готово
Тег и релиз появились на последнем шаге, когда CI уже был зелёным, CHANGELOG.md описывал реальные изменения, а editable install, сборка wheel/sdist и команда safesort --help были заново проверены на чистом окружении. Без этих проверок номер версии ничего не гарантирует.
Публикация в PyPI остаётся за рамками версии 0.1.0
Установка через pip install git+https://github.com/Cartesian-School/safesort.git@v0.1.0 достаточна для разработки и личного использования. Публикация пакета в PyPI, чтобы его можно было установить командой pip install safesort без ссылки на репозиторий, остаётся отдельной темой с собственными требованиями к учётной записи и публикации. Версия 0.1.0 сознательно её не касается.

Коротко

  • MAJOR.MINOR.PATCH задаёт соглашение о номере версии, а не встроенное в инструменты правило.
  • python -m build создаёт wheel и sdist; чистое окружение проверяет установку wheel и console script.
  • Git tag и GitHub Release представляют разные объекты; Release создаётся вокруг тега и хранит artifacts.
Официальная документация
Packaging flow
Packaging Python Projects — Generating distribution archives
Semantic Versioning 2.0.0
Managing releases in a repository