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


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

мы получаем намного лучше с нашими практиками через наш CI платформа и я действительно хотел бы настроить его на автоматическое увеличение значений в пределах assemblyinfo.cs файл, Чтобы версии наших сборок автоматически обновлялись с изменениями кода в этой сборке.

Я ранее настройка (прежде чем мы нашли Гудзон) способ увеличить значение через любой msbuild или в командной строке (не помню), но с Hudson, который будет обновлять репозиторий SVN и запускать другую сборку. Это приведет к медленному бесконечному циклу, поскольку Хадсон опрашивает SVN каждый час.

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

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

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

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

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

12 108

12 ответов:

простой альтернативой является позволить среде C# увеличить версию сборки для вас, установив атрибут version в 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.*")]

вот что я сделал, для штамповки атрибута 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'th build # в hudson, buit из версии 2632 subversion.

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

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

действия:

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

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

в системе управления версиями вы добавляете файл bat или файл сценария, который просто увеличивает Поделились своими вещами.файл CS и все ваши проекты будут обновлять информацию о сборке из этого файла.

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

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

например, чтобы игнорировать изменения версия.свойства

.NET делает это за вас. В файле AssemblyInfo.cs-файл, установите версию сборки в major.незначительный.* (например: 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#>")]

как продолжение ответа микрофоны, я хотел бы добавить, что против + визуальная студия визуализации и моделирования СДК должен быть установлен для этого, чтобы работать, и вы должны изменить файл проекта, а также. Следует также упомянуть, что я использую 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.цели, у меня есть это (в зависимости от того, где вы устанавливаете 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;

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

     ./build.bat --build_id %BUILD_ID% -build_number %BUILD_NUMBER%

в сборке окружающая среда, у меня есть сборка.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 и выбранных свойствах, выберите события сборки и введите следующую информацию в качестве командной строки события предварительной сборки, которая автоматически создает a .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%

возможно, вам придется приспособиться к вашей сборке вкус. Я строю проект вручную один раз, чтобы создать начальную версию.cs-файл в каталоге свойств основного проекта. Наконец, я вручную включаю версию.cs-файл в решение Visual Studio, перетащив его на панель Обозреватель решений под вкладкой свойства для этого проекта. В будущих сборках Visual Studio затем читает это .cs-файл во время сборки Jenkins и получает из него правильную информацию о номере сборки.

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

после изучения нескольких из вышеперечисленных методов я только что реализовал шаг сборки для запуска сценария Powershell, который выполняет поиск и замену в AssemblyInfo.cs-файл. Я все еще использую 1.0.* номер версии в системе управления версиями, и Дженкинс просто вручную обновляет номер версии перед запуском msbuild.

dir **/Properties/AssemblyInfo.cs | %{ (cat $_) | %{$_ -replace '^(\s*)\[assembly: AssemblyVersion\("(.*)\.\*"\)', "`[assembly: AssemblyVersion(`"`.$build`")"} | Out-File $_ -Encoding "UTF8" }
dir **/Properties/AssemblyInfo.cs | %{ (cat $_) | %{$_ -replace '^(\s*)\[assembly: AssemblyFileVersion\("(.*)\.\*"\)', "`[assembly: AssemblyFileVersion(`"`.$build`")"} | Out-File $_ -Encoding "UTF8" }

Я добавил-кодировка "UTF8" вариант, потому что git начал лечение.cs-файл в виде двоичных файлов, Если я этого не сделал. конечно, это не имело значения, так как я никогда не фиксировал результат; он просто появился, когда я тестировал.

наша среда CI уже имеет возможность связать сборку Jenkins с конкретным git commit (спасибо Stash plugin!), поэтому я не волнуюсь, что нет git commit с номером версии, прикрепленным к нему.

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

  // 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, а затем в Дженкин без много дополнительных инструментов.