Зачем?

При разработке гемов по git-flow я столкнулся с некоторыми проблемами при версионировании. Rubygems.org не позволит вам запушить два гема с одинаковой версией (что логично), поэтому использование 0.1.0-dev для нестабильных релизов является неприемлимым (точнее, приемлимым, но только если публикация нестабильной версии происходит только один раз между релизами).

А что, если сборка гема и публикация происходит автоматически? Вечные конфликты. Именно поэтому нужно после любой добавленной фичи изменять версию, чтобы этих конфликтов избежать.

В этом посте я бы хотел описать схему версионирования, к которой я пришел после некоторого гугления и экспериментирования.

Данная схема актуальна не только для разработки гемов, но и любого продукта, у которого нужно проставлять версии после каждой добавленной фичи.

Несколько ссылок

  • Небольшая заметка о semver в rubygems гайдах: http://guides.rubygems.org/patterns/#semantic-versioning
  • Сайт о semantic versionning: http://semver.org/
  • Статья о git-flow на Хабре: https://habrahabr.ru/post/106912/
  • Модуль git-flow: http://danielkummer.github.io/git-flow-cheatsheet/index.ru_RU.html

Ветки репозитория

В репозитории есть две ветки: develop и master.

  • master указывает на текущий стабильный релиз. Если мы хотим где-то развернуть наш продукт или дать его конечному пользователю, то, вероятно, мы дадим ему именно код из этой ветки.
  • develop является указателем на промежуточные версии между основными релизами. Все фичи вливаются именно в него.
  • Также возможно наличие в репозитории веток feature/some-feature, hotfix/..., release/.... Эти ветки там нужны исключительно если мы разрабатываем некую функциональность совместно, либо если нужно иметь возможность открыть ветку и посмотреть коммиты именно этой ветки (соответсвенно, в данном сценарии эта ветка никак не модифицируется).

Как изменяется версия

Давайте предположим, что мы инициализировали наш репозиторий и выставили версию ‘0.1.0’.

Сейчас у нас обе ветки указывают на один и тот же инит-коммит. Давайте добавим какую-нибудь фичу:

git flow feature start some-feature

Локально у нас появилась ветка feature/some-feature. Сразу же меняем версию проекта:

VERSION = '0.2.0-some.feature'

Rubygems использует semver. Наличие -some.feature говорит ему о том, что версия предрелизная, а значит, что нестабильная, поэтому выбирая между 0.1.0 и 0.2.0-some.feature команда gem install <product> выберет первый вариант. Я предлагаю в качестве постфикса использовать название ветки, в качестве разделителя используя точку.

Почему именно точку? Не могу сейчас вспомнить, но с использованием тире и нижнего подчеркивания возникали какие-то проблемы с Rubygems.org, поэтому я пришел к тому, что лучше всего использовать именно точку. Тем более, ее написать проще, чем нижнее подчеркивание и тире.

Итак, закончив разработку фичи, мы выполняем:

git flow feature finish some-feautre

После этой команды ветка влита в develop и удалена.

По точно такому же сценарию создаем вторую фичу. Теперь у нас есть целых две фичи и мы считаем, что готовы выпустить релиз. Для этого делаем:

git flow release start v0.2.0

Локально имеем ветку release/v0.2.0. Первым же делом меняем версию на 0.2.0 без постфикса. Теперь можем коммитить какие-нибудь мелкие правки, вроде апдейтов в README.md, документации и пр.

Финишируем:

git flow release finish v0.2.0

Эта команда вливает ветку в develop и master, а после удаляет ее.

Отношение между master и develop выглядит примерно так:

   master   develop
1) 0.1.0
2)          0.2.0-some.feature
3)          0.2.0-another.feature
4) 0.2.0