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

112

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

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

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

Плохая идея, если Хадсон увеличивает номер версии? Что было бы альтернативным способом сделать это?

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

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

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

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

Аллен Райс
источник

Ответы:

63

Простая альтернатива - позволить среде 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.*")]
Грег Д.
источник
Я никогда не сталкивался с этим раньше, не могли бы вы рассказать немного подробнее о том, что он делает. Работает ли он только в одной среде IDE или на платформе CI работает целая команда разработчиков?
Аллен Райс,
аааа, я видел это раньше, это может быть приемлемым решением, но # built не хранится в subversion и т. д. У меня есть настройка Hudson для архивирования файлов, и таким образом она сохраняется, что может быть приемлемо. Мне нужно будет еще раз изучить, как работает этот механизм, спасибо! Вы бы не знали, как он определяет, что вводить в качестве значений, не так ли?
Аллен Райс,
1
Смотрите мой ответ ниже, чтобы узнать ответ на ваш вопрос. Значения определяются на основе времени сборки.
Кайл Трауберман
Вау, думаю, это сработает. Не уверен, как мы упустили из виду такое простое решение
Аллен Райс
Надеюсь, что это так, рад, что смог помочь. Зачем делать что-то трудным, если легкий и быстрый путь тоже правильный? :)
Greg D
65

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

нет
источник
1
Итак, чтобы обновить это: метод работает для C #. Пользуюсь уже некоторое время. Но сборки C ++ (т.е. C ++ / CLI) все еще остаются проблемой. Насколько я могу судить, задача AssemblyInfo не создает корректный C ++. Кроме того, я думаю, что у этого метода есть небольшой недостаток в том, что другим разработчикам непонятно, что происходит. Жаль, что вы не можете
поместить
@CJBrew Вы можете просто создать небольшой .bat-файл, который создает код C ++ для AssemblyInfo, и запустить этот скрипт с помощью msbuild. Я не уверен, что вы имеете в виду, продвигая его как свойство, вы, безусловно, можете поместить строки версии в любое свойство, которое вам нравится - вам не нужно использовать major / minor / build / revision, которые я использовал здесь.
nos
Можно ли получить что-нибудь с помощью этого маршрута или просто закомментировать AssemblyFileVersion и позволить ему автоматически установить соответствие [assembly: AssemblyVersion ("1.0. *")]?
cchamberlain
@ColeChamberlain Это будет автоматически увеличиваться, если вы создадите его из Visual Studio на своем собственном ПК, а не из Hudson - и нет никакого отношения к номеру версии и конкретной версии сборки и исходного кода.
42

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

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

шаги:

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

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

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

Sondlerd
источник
спасибо, Марк. Извините за мертвую ссылку, оказалось, что сервер сообщества не так-то просто переместить. Я должен поискать помощь по этой теме ...
sondlerd
11

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

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

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

/MyProject/trunk/version.properties

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

Мэтью Блэкфорд
источник
1
Hudson также может игнорировать коммиты определенных пользователей или не запускать сборку в зависимости от сообщения о фиксации. Таким образом вы можете игнорировать все коммиты от Hudson.
Peter Schuetze
9

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

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

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

Кайл Трауберман
источник
21
<кольцо, кольцо> "привет, служба поддержки, чем я могу помочь?" <клиент> "у меня ошибка" <поддержка> "хорошо, какая у вас версия?" <заказчик> "версия одна точка два редакция восемь пять два пять три семь четыре сборка семь четыре шесть три пять два девять ..." <поддержка> "подождите, просто напечатайте это ... хммм ... пожалуйста, повторите версию числа, похоже, у нас нет в списке этой сборки и версии ... "- GRRR!
Джимбо
хаха хороший комментарий. Я тоже не фанат этой системы приращения: p
Джошуа Хейс
3
Автоинкремент в visual studio - отстой.
Kugel
8
@Jimbo: Хотя мы все согласны с тем, что ваш комментарий был забавным, на практике это не имеет значения. Когда вы говорите об установке VS, у вас есть Visual Studio 2008 SP1 или VS2008 9.0.30729.1 SP? Использование автоинкремента номеров сборки является очень распространенной схемой, и ее можно очень легко «исправить» путем увеличения номера основной / дополнительной версии при выходе сборки выпуска.
Марек
самый высокий, который мы достигли с номером сборки, составляет 678 перед сбросом обратно на 0 для увеличения незначительной версии (конечно, cruisecontrol, казалось, легче сбросить, чем hudson, так как в cruisecontrol вы просто вошли и сохранили его обратно в 0 в проекте , но все остальное в Гудзоне лучше)
Дин Хиллер
8

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

TonyG
источник
10
Вам необходимо удалить AssemblyFileVersion. Кроме того, для нас это замечательно, это принятый ответ.
Аллен Райс,
1
Да, удаление AssemblyFileVersion позволяет версии обновляться, и больше никаких ошибок с версией. Ницца. Примечание: две операции сборки увеличивают ревизию только один раз, но при повторной сборке ревизия обновляется. Как сказал ktrauberman, это похоже на build.revision = date.time, что объясняет, почему данные не хранятся нигде, кроме сборки. Теперь мне нужно получить стандартную установку MSI для создания нового ProductCode при обновлении основного выходного проекта. Настройки не допускают пересмотра, только сборку. Я хочу выполнить установку поверх существующей установки, чтобы выполнить обновление. Необходимо исследование.
TonyG
5

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

В продолжение ответа MikeS я хотел бы добавить, что для этого необходимо установить VS + Visual Studio Visualization and Modeling SDK, а также изменить файл проекта. Также следует упомянуть, что я использую Jenkins в качестве сервера сборки, работающего на сервере Windows 2008 R2 с модулем версии, где я получаю BUILD_NUMBER.

Файл My Text Template 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;
aggaton
источник
1

Мое решение не требует добавления внешних инструментов или языков сценариев - оно почти гарантированно будет работать на вашей машине сборки. Решаю эту проблему в нескольких частях. Во-первых, я создал файл 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, перетаскивая его в панель «Обозреватель решений» под вкладкой «Свойства» для этого проекта. В будущих сборках Visual Studio затем считывает этот CS-файл во время сборки Jenkins и получает из него правильную информацию о номере сборки.

оборота johnwbyrd
источник
1

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

Изучив несколько из вышеперечисленных методов, я просто реализовал этап сборки для запуска сценария Powershell, который выполняет поиск и замену файла 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 с прикрепленным к нему номером версии.

Джоннибот
источник
0

Это более простой механизм. Он просто включает добавление этапа сборки задачи Windows Batch перед этапом 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

sweetfa
источник
0

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

Нетферрет
источник