Как я могу автоматически увеличивать версию сборки C# через нашу платформу CI (Hudson)?
Я и моя группа ужасно наращиваем номера версий сборок, и мы часто поставляем сборки с версиями 1.0.0.0. Очевидно, это вызывает много головных болей.
Мы становимся намного лучше с нашими практиками через нашу CI- платформу, и я действительно хотел бы настроить его на автоматическое увеличение значений в пределах assemblyinfo.cs
файл, так что версии наших сборок автоматически обновляются с изменениями кода в этой сборке.
Ранее я настроил (до того, как мы нашли Хадсона) способ увеличить значение либо msbuild
или командную строку (не помню), но с Hudson, который обновит SVN-репозиторий и запустит ДРУГОЙ билд. Это приведет к медленному бесконечному циклу, поскольку Хадсон опрашивает SVN каждый час.
Является ли увеличение числа версий Хадсоном плохой идеей? Каков был бы альтернативный способ сделать это?
В идеале мои критерии для решения должны быть такими, которые:
- Увеличивает номер сборки в
assemblyinfo.cs
перед сборкой - Увеличивает только номер сборки в сборках, которые изменились. Это может быть невозможно, так как Hudson стирает папку проекта каждый раз, когда выполняет сборку
- Передает измененный файл assemblyinfo.cs в репозиторий кода (в настоящее время VisualSVN).
- Не заставляет Hudson запускать новую сборку при следующем сканировании на наличие изменений
Работая над этим, я мог бы легко найти решение большинства проблем с помощью пакетных файлов / команд, но все мои идеи заставили бы Хадсон запустить новую сборку при следующем сканировании. Я не ищу кого-то, кто сделает все для меня, просто укажи мне правильное направление, возможно, метод, позволяющий Хадсону игнорировать определенные SVN-коммиты и т. Д.
Все, что я нашел до сих пор, - это просто статья, объясняющая, как автоматически увеличивать номер версии, ничто не учитывает платформу CI, которая может вращаться в бесконечном цикле.
12 ответов
Простая альтернатива состоит в том, чтобы позволить среде C# увеличивать для вас версию сборки, задав для атрибута версии значение major.minor.*
(как описано в шаблоне файла AssemblyInfo.)
Возможно, вы ищете более всеобъемлющее решение.
РЕДАКТИРОВАТЬ (ответ на вопрос в комментарии):
От AssemblyInfo.cs
:
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
Вот что я сделал для печати атрибута AssemblyFileVersion.
Удалил AssemblyFileVersion из AssemblyInfo.cs
Добавьте новый пустой файл с именем AssemblyFileInfo.cs в проект.
Установите набор инструментов для задач сообщества MSBuild на машине сборки hudson или как зависимость NuGet в вашем проекте.
Отредактируйте файл проекта (csproj), это просто файл msbuild и добавьте следующее.
Где-то там будет <PropertyGroup>
с указанием версии. Измените это так, например:
<Major>1</Major>
<Minor>0</Minor>
<!--Hudson sets BUILD_NUMBER and SVN_REVISION -->
<Build>$(BUILD_NUMBER)</Build>
<Revision>$(SVN_REVISION)</Revision>
Hudson предоставляет переменные env, которые вы видите там, когда проект построен на hudson (при условии, что он получен из subversion).
В нижней части файла проекта добавьте
<Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets" Condition="Exists('$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets')" />
<Target Name="BeforeBuild" Condition="Exists('$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets')">
<Message Text="Version: $(Major).$(Minor).$(Build).$(Revision)" />
<AssemblyInfo CodeLanguage="CS" OutputFile="AssemblyFileInfo.cs" AssemblyFileVersion="$(Major).$(Minor).$(Build).$(Revision)" AssemblyConfiguration="$(Configuration)" Condition="$(Revision) != '' " />
</Target>
При этом используются MSBuildCommunityTasks для генерации AssemblyFileVersion.cs для включения атрибута AssemblyFileVersion до построения проекта. Вы можете сделать это для любого / всех атрибутов версии, если хотите.
В результате, всякий раз, когда вы запускаете сборку hudson, полученная сборка получает AssemblyFileVersion версии 1.0.HUDSON_BUILD_NR.SVN_REVISION, например 1.0.6.2632, что означает 6-й сборочный номер # в hudson, buit из ревизии 2632 подрывной деятельности.
Вот элегантное решение, которое требует небольшой предварительной работы при добавлении нового проекта, но обрабатывает процесс очень легко.
Идея состоит в том, что каждый проект ссылается на файл решения, который содержит только информацию о версии сборки. Таким образом, процесс сборки должен обновлять только один файл, и все версии сборки извлекаются из одного файла после компиляции.
шаги:
- Добавьте класс к вашему файлу решения *.cs файл, я назвал min SharedAssemblyProperties.cs
- Удалите всю информацию CS из этого нового файла
- Вырежьте информацию о сборке из файла AssemblyInfo: [assembly: AssemblyVersion("1.0.0.0")] [Assembly: AssemblyFileVersion("1.0.0.0")]
- Добавьте оператор "using System.Reflection;" в файл, а затем вставьте данные в новый файл CS (например, SharedAssemblyProperties.cs)
- Добавить существующий элемент в ваш проект (подождите... прочтите, прежде чем добавлять файл)
- Выберите файл и, прежде чем нажать кнопку "Добавить", щелкните раскрывающийся список рядом с кнопкой "Добавить" и выберите "Добавить как ссылку".
- Повторите шаги 5 и 6 для всех существующих и новых проектов в решении
Когда вы добавляете файл как ссылку, он сохраняет данные в файле проекта и после компиляции извлекает информацию о версии сборки из этого одного файла.
В системе управления версиями вы добавляете файл bat или файл сценария, который просто увеличивает файл SharedAssemblyProperties.cs, и все ваши проекты будут обновлять информацию о своих сборках из этого файла.
Hudson можно настроить так, чтобы он игнорировал изменения определенных путей и файлов, чтобы он не вызывал новую сборку.
На странице конфигурации задания в разделе " Управление исходным кодом" нажмите кнопку " Дополнительно". В поле " Исключенные регионы" введите одно или несколько регулярных выражений для сопоставления исключений.
Например, чтобы игнорировать изменения в файле version.properties, вы можете использовать:
/MyProject/trunk/version.properties
Это будет работать для языков, отличных от C#, и позволит вам хранить информацию о вашей версии в Subversion.
.NET делает это для вас. В файле AssemblyInfo.cs установите версию сборки на major.minor.* (Например, 1.0.*).
Когда вы строите свой проект, версия генерируется автоматически.
Я считаю, что номера сборки и ревизии генерируются на основе даты, используя эпоху Unix. Сборка основана на текущем дне, а пересмотр основан на количестве секунд с полуночи.
Я никогда не видел, чтобы эта функция 1.0.* Работала в VS2005 или VS2008. Есть ли что-то, что нужно сделать, чтобы установить VS для увеличения значений?
Если AssemblyInfo.cs жестко запрограммирован с 1.0.*, То где хранится реальная сборка / ревизия?
После помещения 1.0.* В AssemblyInfo мы не можем использовать следующий оператор, потому что ProductVersion теперь имеет недопустимое значение - он использует 1.0.*, А не значение, назначенное VS:
Version version = new Version(Application.ProductVersion);
Вздох - кажется, это одна из тех вещей, о которых все спрашивают, но почему-то нет однозначного ответа. Несколько лет назад я видел решения для создания номера ревизии и сохранения его в AssemblyInfo как часть процесса после сборки. Я надеялся, что такой танец не понадобится для VS2008. Может VS2010?
Я предполагаю, что можно также сделать это с помощью текстового шаблона, где вы создаете атрибуты сборки на лету из среды, как это делает AssemblyVersion.tt ниже.
<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ output extension=".cs" #>
<#
var build = Environment.GetEnvironmentVariable("BUILD_NUMBER");
build = build == null ? "0" : int.Parse(build).ToString();
var revision = Environment.GetEnvironmentVariable("SVN_REVISION");
revision = revision == null ? "0" : int.Parse(revision).ToString();
#>
using System.Reflection;
[assembly: AssemblyVersion("1.0.<#=build#>.<#=revision#>")]
[assembly: AssemblyFileVersion("1.0.<#=build#>.<#=revision#>")]
В качестве продолжения ответа MikeS я хотел добавить, что для этой работы необходимо установить VS + Visual Studio SDK Visualization and Modeling SDK, а также изменить файл проекта. Следует также упомянуть, что я использую Jenkins в качестве сервера сборки, работающего на сервере Windows 2008 R2 с модулем версии, где я получаю BUILD_NUMBER.
Мой текстовый шаблон файла version.tt выглядит так
<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ output extension=".cs" #>
<#
var build = Environment.GetEnvironmentVariable("BUILD_NUMBER");
build = build == null ? "0" : int.Parse(build).ToString();
var revision = Environment.GetEnvironmentVariable("_BuildVersion");
revision = revision == null ? "5.0.0.0" : revision;
#>
using System.Reflection;
[assembly: AssemblyVersion("<#=revision#>")]
[assembly: AssemblyFileVersion("<#=revision#>")]
У меня есть следующее в группах свойств
<PropertyGroup>
<TransformOnBuild>true</TransformOnBuild>
<OverwriteReadOnlyOutputFiles>true</OverwriteReadOnlyOutputFiles>
<TransformOutOfDateOnly>false</TransformOutOfDateOnly>
</PropertyGroup>
после импорта Microsoft.CSharp.targets у меня это (в зависимости от того, где вы устанавливаете VS
<Import Project="C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\TextTemplating\v10.0\Microsoft.TextTemplating.targets" />
На моем сервере сборки у меня есть следующий скрипт для запуска преобразования текста перед фактической сборкой, чтобы получить последний номер набора изменений в TFS.
set _Path="C:\Build_Source\foo"
pushd %_Path%
"%ProgramFiles(x86)%\Microsoft Visual Studio 10.0\Common7\IDE\tf.exe" history . /r /noprompt /stopafter:1 /Version:W > bar
FOR /f "tokens=1" %%foo in ('findstr /R "^[0-9][0-9]*" bar') do set _BuildVersion=5.0.%BUILD_NUMBER%.%%foo
del bar
popd
echo %BUILD_NUMBER%
echo %_BuildVersion%
cd C:\Program Files (x86)\Jenkins\jobs\MyJob\workspace\MyProject
MSBuild MyProject.csproj /t:TransformAll
...
<rest of bld script>
Таким образом, я могу отслеживать сборки и наборы изменений, поэтому, если я ничего не проверил со времени последней сборки, последняя цифра не должна меняться, однако я мог внести изменения в процесс сборки, отсюда и необходимость второго последнего числа, Конечно, если вы сделаете несколько проверок перед сборкой, вы получите только последнее изменение, отраженное в версии. Я думаю, вы могли бы объединить это требуется.
Я уверен, что вы можете сделать что-то более интересное и вызвать TFS непосредственно из шаблона tt, однако это работает для меня.
Затем я могу получить свою версию во время выполнения, как это
Assembly assembly = Assembly.GetExecutingAssembly();
FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(assembly.Location);
return fvi.FileVersion;
Итак, у нас есть проект с одним решением, который содержит несколько проектов, имеющих сборки с разными номерами версий.
Изучив несколько из перечисленных выше методов, я только что реализовал шаг сборки для запуска сценария Powershell, который выполняет поиск и замену файла AssemblyInfo.cs. Я все еще использую номер версии 1.0. * В системе контроля версий, и Дженкинс просто обновляет номер версии вручную перед запуском msbuild.
dir **/Properties/AssemblyInfo.cs | %{ (cat $_) | %{$_ -replace '^(\s*)\[assembly: AssemblyVersion\("(.*)\.\*"\)', "`$1[assembly: AssemblyVersion(`"`$2.$build`")"} | Out-File $_ -Encoding "UTF8" }
dir **/Properties/AssemblyInfo.cs | %{ (cat $_) | %{$_ -replace '^(\s*)\[assembly: AssemblyFileVersion\("(.*)\.\*"\)', "`$1[assembly: AssemblyFileVersion(`"`$2.$build`")"} | Out-File $_ -Encoding "UTF8" }
Я добавил опцию -Encoding "UTF8", потому что git начал обрабатывать файл.cs как двоичные файлы, если я этого не сделал. Конечно, это не имело значения, так как я никогда не передавал результат; это просто появилось, когда я тестировал.
В нашей среде CI уже есть возможность связать сборку Jenkins с конкретным git commit (спасибо плагину Stash!), Поэтому я не волнуюсь, что git commit с номером версии, к которому он прикреплен, отсутствует.
Мое решение не требует добавления внешних инструментов или языков сценариев - оно в значительной степени гарантированно работает на вашей сборочной машине. Я решаю эту проблему в нескольких частях. Сначала я создал файл BUILD.BAT, который преобразует параметр Jenkins BUILD_NUMBER в переменную среды. Я использую функцию Дженкинса "Выполнить пакетную команду Windows", чтобы запустить командный файл сборки, введя следующую информацию для сборки Дженкинса:
./build.bat --build_id %BUILD_ID% -build_number %BUILD_NUMBER%
В среде сборки у меня есть файл build.bat, который начинается следующим образом:
rem build.bat
set BUILD_ID=Unknown
set BUILD_NUMBER=0
:parse_command_line
IF NOT "%1"=="" (
IF "%1"=="-build_id" (
SET BUILD_ID=%2
SHIFT
)
IF "%1"=="-build_number" (
SET BUILD_NUMBER=%2
SHIFT
)
SHIFT
GOTO :parse_command_line
)
REM your build continues with the environmental variables set
MSBUILD.EXE YourProject.sln
Сделав это, я щелкнул правой кнопкой мыши по проекту, который будет построен в панели обозревателя решений Visual Studio, и выбрал "Свойства", выбрал "События сборки" и ввел следующую информацию в качестве командной строки события перед сборкой, которая автоматически создает файл.cs. содержит информацию о номере сборки на основе текущих настроек переменных среды:
set VERSION_FILE=$(ProjectDir)\Properties\VersionInfo.cs
if !%BUILD_NUMBER%==! goto no_buildnumber_set
goto buildnumber_set
:no_buildnumber_set
set BUILD_NUMBER=0
:buildnumber_set
if not exist %VERSION_FILE% goto no_version_file
del /q %VERSION_FILE%
:no_version_file
echo using System.Reflection; >> %VERSION_FILE%
echo using System.Runtime.CompilerServices; >> %VERSION_FILE%
echo using System.Runtime.InteropServices; >> %VERSION_FILE%
echo [assembly: AssemblyVersion("0.0.%BUILD_NUMBER%.1")] >> %VERSION_FILE%
echo [assembly: AssemblyFileVersion("0.0.%BUILD_NUMBER%.1")] >> %VERSION_FILE%
Возможно, вам придется подстроиться под свой вкус. Я создаю проект вручную один раз, чтобы создать исходный файл Version.cs в каталоге Properties основного проекта. Наконец, я вручную включаю файл Version.cs в решение Visual Studio, перетаскивая его на панель обозревателя решений под вкладкой "Свойства" для этого проекта. В будущих сборках Visual Studio читает этот файл.cs во время сборки Jenkins и извлекает из него правильную информацию о номере сборки.
Это более простой механизм. Он просто включает в себя добавление шага сборки командной задачи Windows перед этапом MSBuild и использование простой программы поиска и замены (FART).
Пакетный Шаг
fart --svn -r AssemblyInfo.cs "[assembly: AssemblyVersion(\"1.0.0.0\")]" "[assembly: AssemblyVersion(\"1.0.%BUILD_NUMBER%.%SVN_REVISION%\")]"
if %ERRORLEVEL%==0 exit /b 1
fart --svn -r AssemblyInfo.cs "[assembly: AssemblyFileVersion(\"1.0.0.0\")]" "[assembly: AssemblyFileVersion(\"1.0.%BUILD_NUMBER%.%SVN_REVISION%\")]"
if %ERRORLEVEL%==0 exit /b 1
exit /b 0
Если вы используете управление исходным кодом, отличное от svn, измените параметр --svn на соответствующий для вашей среды scm.
Я решил использовать несколько методов, используя предварительно собранный скрипт Powershell ( https://gist.github.com/bradjolicoeur/e77c508089aea6614af3), чтобы увеличить каждую успешную сборку, а затем в Global.asax я собирался примерно так:
// We are using debug configuration, so increment our builds.
if (System.Diagnostics.Debugger.IsAttached)
{
string version = System.Reflection.Assembly.GetExecutingAssembly()
.GetName()
.Version
.ToString();
var psi = new ProcessStartInfo(@"svn", "commit -m \"Version: " + version + "\n \"");
psi.WorkingDirectory = @"C:\CI\Projects\myproject";
Process.Start(psi);
}
Я все еще думаю, что весь процесс слишком сложен, и я собираюсь изучить более эффективный метод достижения того же результата. Я хотел этого, главным образом, для передачи версии в SVN, а затем в Jenkin's без слишком большого количества дополнительных инструментов.