Feel Good.

28 марта 2012

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

Эта статья о том, как я наладил семантическое версионирование (semantic versioning) в своем проекте на базе DVCS Mercurial (можно читать как Git). На хабре есть отличный перевод про Семантическое управление версиями, советую начать с него.

В качестве хранилища кода, я выбрал mercurial. Помимо функции хранилища кода, mercurial будет выступать еще и как провайдер версий, это означает, что запрашивать текущую семантическую версию (Major.Minor.Patch) мы будем именно у него.

Итак, рассмотрим пару Major.Minor. Обычно эта пара всегда задается вручную в момент релиза, и самый простой способ это сделать - пометить руками нужный сhangeset тегом, содержащим версию релиза (например: 1.4, или v1.4, но не 1.4.3, так как path мы договоримся вычислять).

С Patch все намного сложнее. Наша цель, добиться чтобы при каждой фиксации(commit) изменений кодовой базы автоматически бы инкрементировался path-номер. Это примерно означало бы, что path будет равен числу фиксаций в текущем релизе (например: 1.4.17 - 17-ая фиксация в релизе 1.4, но в то же время 1.5.0 уже новый релиз, с новой path-нумерацией). Но здесь не стоит забывать один факт: мы ведь работаем с DVCS и у нас нет единого "брокера" path-номеров...

В интернете я нашел несколько вариантов решения этой задачи (SO, RSDN), где в основном предлагалось использовать число, полученное как {latesttagdistance}, но можно заметить, что {latesttagdistance} перестает работать в общем случае, когда тег может содержать произвольный текст. Но эта идея мне очень понравилась, и я начал искать универсальное решение. И вскоре решение было найдено.

Я предлагаю в качестве path-номера брать разницу между номером ревизии текущего changeset-а (замечу, что это не всегда tip) и номером ревизии changeset-а, имеющего тэг с релизной версией и являющегося ближайшим предком по отношению к текущему changeset-у.

На текущий момент, при таком подходе мне удалось достичь желаемого семантического версионирования своих проектов.

Теперь осталось все это как-то оформить. В конечном же счете, я преследовал цель автоматической генерации файла AssemblyInfo.cs на основе номера версии полученной от mercurial до начала сборки проекта, и с последующей сборкой. Очевидным решением здесь было сделать Custom MSBuild Task/Target, и в итоге появился проект Devme.MSBuildTasks.


Коротко о Devme.MSBuildTasks (текущая версия 0.3.0)

Q: Какие системы контроля версий поддерживает библиотека?

A: Пока только для mercurial.

Q: Как я могу начать использовать это в своем проекте?

A: Необходимо выполнить 3 простых шага:

Шаг 1. Используя Nuget, установить/скачать библиотеку к себе в проект.

Шаг 2. Создать в каталоге Properties (если использовали nuget, то файл шаблон будет создан автоматически) файл шаблон следующего содержания (за основу можно взять Ваш текущий файл AssemblyInfo.cs):

using System.Reflection;
 
[assembly: AssemblyTitle("todo")]
[assembly: AssemblyDescription("todo")]
[assembly: AssemblyCompany("todo")]
[assembly: AssemblyProduct("todo")]
[assembly: AssemblyCopyright("Copyright © todo")]
 
[assembly: AssemblyVersion("${major}.${minor}.${path}")]
[assembly: AssemblyFileVersion("${major}.${minor}.${path}")]
[assembly: AssemblyInformationalVersionAttribute("${major}.${minor}.${path}-${branch} ${hash|short}")]
Формат шаблона думаю Вам понятен, здесь в качестве ${some} будут подставлены соответствующие значения взятые из mercurial (см wiki). У себя я называю этот файл AssemblyInfo.cs.template.

Шаг 3. Открыть в блокноте файл проекта (*.csproj) и добавить в самом конце пару тэгов (или расширить существующую цель BeforeBuild), указав правильный путь к Devme.MSBuildTasks.dll:

<UsingTask 
    TaskName="SemanticVersioningTask" 
    AssemblyFile="ПУТЬ К ФАЙЛУ Devme.MSBuildTasks.dll" />
<Target Name="BeforeBuild">
    <semanticversioningtask 
        TemplateFilePath="Properties/AssemblyInfo.cs.template" 
        OutputFilePath="Properties/AssemblyInfo.cs" />
</Target>
Все готово!

Заключение
  1. Следует помнить, что каждый раз, во время сборки проекта, файл AssemblyInfo.cs будет генерироваться автоматически, поэтому его можно(надо) исключить из кодовой базы добавив в .hgignore.
  2. Так же не рекомендуется вносить в файл AssemblyInfo.cs любые изменения. Все изменения надо делать в шаблоне AssemblyInfo.cs.template.
  3. Исходники
  4. Wiki
Ссылки:
  1. Semantic Versioning
  2. AssemblyVersion и git. Давайте жить дружно.
  3. Полуавтоматическое выставление номера версии с помощью git

Всем приятного использования!

Комментариев нет:

Отправить комментарий