Эта статья о том, как я наладил семантическое версионирование (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>Все готово!
Заключение
- Следует помнить, что каждый раз, во время сборки проекта, файл AssemblyInfo.cs будет генерироваться автоматически, поэтому его можно(надо) исключить из кодовой базы добавив в .hgignore.
- Так же не рекомендуется вносить в файл AssemblyInfo.cs любые изменения. Все изменения надо делать в шаблоне AssemblyInfo.cs.template.
- Исходники
- Wiki
- Semantic Versioning
- AssemblyVersion и git. Давайте жить дружно.
- Полуавтоматическое выставление номера версии с помощью git
Всем приятного использования!