Как я могу автоматически увеличивать версию сборки С# через нашу платформу CI (Hudson)?

Я и моя группа ужасны при увеличении числа версий сборки, и мы часто отправляем сборки с версиями 1.0.0.0. Очевидно, это вызывает много головных болей.

Мы значительно улучшаем наши практики с помощью нашей платформы Hudson) способ увеличить значение через msbuild или в командной строке ( не помню), но с Hudson, который будет обновлять SVN-репозиторий и запускать ANOTHER build. Это приведет к медленному бесконечному циклу, поскольку Hudson опроса SVN каждый час.

Имеет ли Хадсон увеличение номера версии плохую идею? Каким будет альтернативный способ сделать это?

В идеале, мои критерии для решения будут такими:

  • Увеличивает номер сборки в assemblyinfo.cs перед сборкой
  • Увеличивает только номер сборки в сборках, которые были изменены. Это может быть невозможно, поскольку Хадсон вытирает папку проекта каждый раз, когда выполняет сборку
  • Задает измененный файл assemblyinfo.cs в репозитории кода (в настоящее время VisualSVN)
  • Не вызывает Hudson для запуска новой сборки при следующем сканировании изменений.

Используя это в моей голове, я мог бы легко найти решение большинства из этого с помощью командных файлов/команд, но все мои идеи заставили бы Хадсона запускать новую сборку при следующем сканировании. Я не ищу, чтобы кто-то делал все для меня, просто укажите мне в правильном направлении, может быть, метод, чтобы Хадсон проигнорировал некоторые обязательства SVN и т.д.

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

Ответ 1

Простая альтернатива - позволить среде С# увеличить версию сборки для вас, установив атрибут версии major.minor.* (как описано в шаблоне файла AssemblyInfo.)

Возможно, вы ищете более комплексное решение.

EDIT (ответ на вопрос в комментарии):

От 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.*")]

Ответ 2

Вот что я сделал, для штамповки атрибута 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 (при условии, что он получен из подрывной работы).

В нижней части файла проекта добавьте

 <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 подрывной версии.

Ответ 3

Вот элегантное решение, которое требует небольшой работы при добавлении нового проекта, но очень легко обрабатывает процесс.

Идея состоит в том, что каждый проект связан с файлом решения, который содержит только информацию о версии сборки. Таким образом, процесс сборки должен обновлять только один файл, а все версии сборки извлекаются из одного файла при компиляции.

Шаги:

  • Добавьте класс к файлу *.cs файла решения, я назвал min SharedAssemblyProperties.cs
  • Удалите всю информацию cs из этого нового файла
  • Вырезать информацию сборки из файла AssemblyInfo: [сборка: AssemblyVersion ( "1.0.0.0" )] [сборка: AssemblyFileVersion ( "1.0.0.0" )]
  • Добавить инструкцию "using System.Reflection;" в файл, а затем вставьте данные в новый файл cs (ex SharedAssemblyProperties.cs)
  • Добавьте существующий элемент в проект (подождите... прочитайте перед добавлением файла)
  • Выберите файл и перед тем, как нажать кнопку "Добавить", выберите раскрывающийся список рядом с кнопкой "Добавить" и выберите "Добавить как ссылку".
  • Повторите шаги 5 и 6 для всех существующих и новых проектов в решении.

Когда вы добавляете файл в качестве ссылки, он сохраняет данные в файле проекта, а после компиляции выводит информацию о версии сборки из этого одного файла.

В исходном управлении вы добавляете файл bat или файл script, который просто увеличивает файл SharedAssemblyProperties.cs, и все ваши проекты будут обновлять информацию об их сборке из этого файла.

Ответ 4

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

На странице конфигурации задания в разделе Управление исходными кодами нажмите кнопку Дополнительно. В поле Исключенные регионы вы вводите одно или несколько регулярных выражений для соответствия исключениям.

Например, чтобы игнорировать изменения в файле version.properties, вы можете использовать:

/MyProject/trunk/version.properties

Это будет работать на языках, отличных от С#, и позволяет хранить информацию о вашей версии в рамках подрывной деятельности.

Ответ 5

.NET делает это для вас. В файле AssemblyInfo.cs установите версию сборки в файл major.minor. * (Например: 1.0. *).

Когда вы создаете проект, версия автоматически создается.

Строки и ревизионные номера создаются на основе даты, используя, по-моему, эпоху unix. Строка основывается на текущем дне, а ревизия основана на количестве секунд с полуночи.

Ответ 6

Я никогда не видел, чтобы функция 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?

Ответ 7

Я предполагаю, что также можно было бы сделать это с текстовым шаблоном, где вы создаете атрибуты сборки, о которых идет речь, из среды, такой как 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#>")]

Ответ 8

В качестве продолжения ответа MikeS я хотел добавить, что для этого нужно установить VSD Visual Studio Visualization и 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" />

На моем сервере сборки у меня есть следующий script, чтобы запустить преобразование текста до фактической сборки, чтобы получить последний номер набора изменений в 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;

Ответ 9

Мое решение не требует добавления внешних инструментов или языков сценариев - почти гарантированно работает на вашей машине сборки. Я решаю эту проблему в нескольких частях. Во-первых, я создал файл BUILD.BAT, который преобразует параметр Jenkins BUILD_NUMBER в переменную среды. Я использую Jenkins "Выполнять команду пакетной команды Windows" для запуска командного файла сборки, введя следующую информацию для сборки Jenkins:

     ./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 в папке "Свойства" основного проекта. Наконец, я вручную включаю файл Version.cs в решение Visual Studio, перетащив его в панель Solution Explorer, под вкладкой "Свойства" для этого проекта. В будущих сборках Visual Studio затем считывает этот файл .cs в время сборки Jenkins и получает из него правильную информацию о номере сборки.

Ответ 10

Итак, у нас есть проект с одним решением, которое содержит несколько проектов, имеющих сборки с разными номерами версий.

После изучения нескольких из вышеперечисленных методов я только что реализовал шаг сборки для запуска Powershell script, который выполняет поиск и замену в файле AssemblyInfo.cs. Я все еще использую номер версии 1.0. * В исходном элементе управления, а Jenkins просто обновляет номер версии до запуска 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 (спасибо плагину Stash!), поэтому я не беспокоюсь, что нет git commit с номером версии, прикрепленным к нему.

Ответ 11

Это более простой механизм. Это просто связано с добавлением шага создания задачи командной строки 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.

Загрузить Fart

Ответ 12

Я решил использовать несколько методов, используя prebuild Powershell script (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 без слишком большого количества дополнительных инструментов.