¿Cómo puedo incrementar automáticamente la versión de ensamblado de C # a través de nuestra plataforma CI (Hudson)?

112

Mi grupo y yo somos horrendos a la hora de incrementar los números de versión de ensamblajes y con frecuencia enviamos ensamblajes con versiones 1.0.0.0. Evidentemente, esto provoca muchos dolores de cabeza.

Estamos mejorando mucho con nuestras prácticas a través de nuestra plataforma de CI y realmente me gustaría configurarlo para que aumente automáticamente los valores dentro del assemblyinfo.csarchivo para que las versiones de nuestros ensamblados se actualicen automáticamente con los cambios de código en ese ensamblado.

Previamente había configurado (antes de encontrar Hudson ) una forma de incrementar el valor a través demsbuild la línea de comando (no recuerdo), pero con Hudson, eso actualizará el repositorio SVN y activará OTRA compilación. Eso resultaría en un bucle infinito lento ya que Hudson sondea SVN cada hora.

¿Es mala idea que Hudson incremente el número de versión? ¿Cuál sería una forma alternativa de hacerlo?

Idealmente, mi criterio para una solución sería uno que:

  • Incrementa el número de compilación assemblyinfo.csantes de una compilación
  • Solo incrementa el número de compilación en ensamblados que han cambiado. Esto puede no ser posible ya que Hudson borra la carpeta del proyecto cada vez que realiza una compilación.
  • Confirma el assemblyinfo.cs modificado en el repositorio de código (actualmente VisualSVN )
  • No hace que Hudson active una nueva compilación la próxima vez que busque cambios

Resolviendo esto en mi cabeza, podría encontrar fácilmente una solución para la mayor parte de esto a través de archivos / comandos por lotes, pero todas mis ideas harían que Hudson active una nueva compilación la próxima vez que escanee. No estoy buscando a alguien que haga todo por mí, solo apúntame en la dirección correcta, tal vez una técnica para hacer que Hudson ignore ciertas confirmaciones de SVN, etc.

Todo lo que he encontrado hasta ahora es solo un artículo que explica cómo aumentar el número de versión automáticamente, nada tiene en cuenta una plataforma de CI que podría girar en un bucle infinito.

Arroz Allen
fuente

Respuestas:

63

Una alternativa simple es dejar que el entorno C # incremente la versión del ensamblado por usted estableciendo el atributo de versión en major.minor.*(como se describe en la plantilla de archivo AssemblyInfo).

Sin embargo, es posible que esté buscando una solución más completa.

EDITAR (Respuesta a la pregunta en un comentario):

De 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.*")]
Greg D
fuente
Nunca me había encontrado con esto antes, ¿podría entrar en un poco más de detalle sobre lo que hace? ¿Solo funciona dentro de un IDE o funciona en todo un equipo de desarrolladores con una plataforma de CI?
Allen Rice
ahhh lo he visto antes, que puede ser una solución aceptable, pero el # built no se almacena dentro de la subversión, etc. Tengo la configuración de Hudson para archivar los archivos y de esa manera se almacena para que pueda ser aceptable. Tendré que investigar un poco más sobre cómo funciona ese mecanismo, ¡gracias! No sabría cómo determina qué poner como valores, ¿verdad?
Allen Rice
1
Vea mi respuesta a continuación para obtener la respuesta a su pregunta. Los valores se determinan en función del tiempo de construcción.
Kyle Trauberman
Vaya, creo que esto funcionará. No estoy seguro de cómo pasamos por alto una solución tan simple
Allen Rice
Espero que lo haga, me alegro de haber podido ayudar. ¿Por qué hacer algo por las malas cuando la forma fácil y rápida también es la correcta? :)
Greg D
65

Esto es lo que hice para estampar el atributo AssemblyFileVersion.

Se quitó AssemblyFileVersion de AssemblyInfo.cs

Agregue un archivo nuevo y vacío llamado AssemblyFileInfo.cs al proyecto.

Instale el conjunto de herramientas de tareas de la comunidad de MSBuild en la máquina de compilación de hudson o como una dependencia de NuGet en su proyecto.

Edite el archivo del proyecto (csproj), es solo un archivo msbuild y agregue lo siguiente.

En algún lugar habrá una <PropertyGroup>indicación de la versión. Cambie eso para que diga, por ejemplo

 <Major>1</Major>
 <Minor>0</Minor>
 <!--Hudson sets BUILD_NUMBER and SVN_REVISION -->
 <Build>$(BUILD_NUMBER)</Build>
 <Revision>$(SVN_REVISION)</Revision>

Hudson proporciona esas variables env que ve allí cuando el proyecto se basa en hudson (suponiendo que se obtenga de Subversion).

En la parte inferior del archivo del proyecto, agregue

 <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>

Esto usa MSBuildCommunityTasks para generar AssemblyFileVersion.cs para incluir un atributo AssemblyFileVersion antes de que se compile el proyecto. Puede hacer esto para cualquiera o todos los atributos de la versión si lo desea.

El resultado es que, cada vez que emite una compilación de hudson, el ensamblado resultante obtiene una versión de archivo de ensamblaje de 1.0.HUDSON_BUILD_NR.SVN_REVISION, por ejemplo, 1.0.6.2632, lo que significa el número de compilación número 6 en hudson, compilado de la revisión de subversión 2632.

nos
fuente
1
Entonces, solo para actualizar esto: el método funciona para C #. Lo he estado usando durante algún tiempo. Pero los ensamblados de C ++ (es decir, C ++ / CLI) siguen siendo un problema. Por lo que puedo decir, la tarea AssemblyInfo no produce C ++ válido. Además, creo que este método tiene una pequeña desventaja, ya que es un poco opaco para que otros desarrolladores comprendan lo que está sucediendo. Lástima que no pueda insertar números de versión directamente en MSBuild como una propiedad ...
CJBrew
@CJBrew Puede crear un pequeño archivo .bat que produzca el código C ++ para AssemblyInfo y hacer que msbuild inicie ese scipt. No estoy seguro de lo que quiere decir con insertarlo como una propiedad, sin duda podría colocar las cadenas de la versión en cualquier propiedad que desee; no es necesario que use la versión principal / menor / compilación / revisión que usé aquí.
nos
¿Se gana algo usando esta ruta en lugar de simplemente comentar AssemblyFileVersion y dejar que se configure para que coincida con [assembly: AssemblyVersion ("1.0. *")] Automáticamente?
cchamberlain
@ColeChamberlain Eso se incrementará automáticamente si lo construye desde Visual Studio en su propia PC, no desde Hudson, y no hay relación con el número de versión y una compilación particular y revisión del código fuente.
nos
42

Esta es una solución elegante que requiere un poco de trabajo por adelantado al agregar un nuevo proyecto, pero maneja el proceso con mucha facilidad.

La idea es que cada proyecto se vincule a un archivo de solución que solo contenga la información de la versión del ensamblado. Por lo tanto, su proceso de compilación solo tiene que actualizar un solo archivo y todas las versiones de ensamblado se extraen de un archivo al compilarse.

Pasos:

  1. Agregue una clase a su archivo de solución * archivo .cs, llamé min SharedAssemblyProperties.cs
  2. Elimine toda la información de cs de ese nuevo archivo
  3. Corte la información de ensamblaje de un archivo AssemblyInfo: [ensamblaje: AssemblyVersion ("1.0.0.0")] [ensamblaje: AssemblyFileVersion ("1.0.0.0")]
  4. Agregue la declaración "using System.Reflection;" al archivo y luego pegue los datos en su nuevo archivo cs (por ejemplo, SharedAssemblyProperties.cs)
  5. Agregue un elemento existente a su proyecto (espere ... siga leyendo antes de agregar el archivo)
  6. Seleccione el archivo y antes de hacer clic en Agregar, haga clic en el menú desplegable junto al botón Agregar y seleccione "Agregar como enlace".
  7. Repita los pasos 5 y 6 para todos los proyectos nuevos y existentes en la solución

Cuando agrega el archivo como un enlace, almacena los datos en el archivo del proyecto y, al compilarlo, extrae la información de la versión del ensamblado de este archivo.

En su control de fuente, agrega un archivo bat o un archivo de secuencia de comandos que simplemente incrementa el archivo SharedAssemblyProperties.cs y todos sus proyectos actualizarán su información de ensamblaje desde ese archivo.

Sondlerd
fuente
gracias, Mark. Perdón por el enlace muerto, resulta que el servidor de la comunidad no es tan fácil de mover. Debo buscar ayuda sobre ese tema ...
sondlerd
11

Hudson se puede configurar para ignorar los cambios en ciertas rutas y archivos para que no solicite una nueva compilación.

En la página de configuración del trabajo, en Administración de código fuente , haga clic en el botón Avanzado . En el cuadro Regiones excluidas , ingrese una o más expresiones regulares para hacer coincidir las exclusiones.

Por ejemplo, para ignorar los cambios en el archivo version.properties , puede usar:

/MyProject/trunk/version.properties

Esto funcionará para idiomas distintos de C # y le permite almacenar la información de su versión dentro de subversion.

Matthew Blackford
fuente
1
Hudson también puede ignorar las confirmaciones de ciertos usuarios o no activar una compilación según el mensaje de confirmación. De esta forma, puede ignorar todas las confirmaciones de Hudson.
Peter Schuetze
9

.NET hace esto por ti. En su archivo AssemblyInfo.cs, configure su versión de ensamblado en major.minor. * (Por ejemplo: 1.0. *).

Cuando construyes tu proyecto, la versión se genera automáticamente.

Los números de compilación y revisión se generan en función de la fecha, utilizando la época de Unix, creo. La compilación se basa en el día actual y la revisión se basa en el número de segundos desde la medianoche.

Kyle Trauberman
fuente
21
<ring, ring> "hola, soporte del producto, ¿cómo puedo ayudar?" <cliente> "tengo un error" <support> "ok, ¿qué versión estás usando?" <cliente> "versión uno punto dos revisión ocho cinco dos cinco tres siete cuatro construir siete cuatro seis tres cinco dos nueve ..." <support> "espere, simplemente escribiendo eso ... hmmm ... por favor repita la versión números, no parece que tengamos esa compilación y revisión en la lista ... "- ¡GRRR!
Jimbo
jaja buen comentario. Tampoco soy fanático de ese sistema incremental: p
Joshua Hayes
3
El autoincremento en Visual Studio es una mierda.
Kugel
8
@Jimbo: Si bien todos estamos de acuerdo en que su comentario fue gracioso, en la práctica no importa. Cuando habla de su instalación de VS, ¿tiene Visual Studio 2008 SP1 o VS2008 9.0.30729.1 SP? El uso de números de compilación de autoincremento es un esquema muy común y se puede "arreglar" muy fácilmente incrementando los números de versión mayor / menor cuando aparece una compilación de lanzamiento.
Marek
lo más alto que hemos ido con un número de compilación es 678 antes de restablecerlo de nuevo a 0 para un aumento en la revisión menor (por supuesto, cruisecontrol, parecía más fácil de restablecer que hudson como en cruisecontrol, simplemente ingresó y lo guardó nuevamente en 0 en el proyecto , pero todo lo demás en hudson es mejor)
Dean Hiller
8

En realidad, nunca he visto que la característica 1.0. * Funcione en VS2005 o VS2008. ¿Hay algo que deba hacerse para configurar VS para incrementar los valores?

Si AssemblyInfo.cs está codificado con 1.0. *, ¿Dónde se almacena la versión / revisión real?

Después de poner 1.0. * En AssemblyInfo, no podemos usar la siguiente declaración porque ProductVersion ahora tiene un valor no válido: está usando 1.0. * Y no el valor asignado por VS:

Version version = new Version(Application.ProductVersion);

Suspiro, esta parece ser una de esas cosas por las que todo el mundo pregunta, pero de alguna manera nunca hay una respuesta sólida. Hace años vi soluciones para generar un número de revisión y guardarlo en AssemblyInfo como parte de un proceso posterior a la compilación. Esperaba que ese tipo de baile no fuera necesario para VS2008. ¿Quizás VS2010?

TonyG
fuente
10
Tienes que eliminar AssemblyFileVersion. Aparte de eso, está funcionando de maravilla para nosotros, es la respuesta aceptada.
Allen Rice
1
Sí, eliminar AssemblyFileVersion permite que la versión se actualice y no más errores con Version. Agradable. Nota: dos operaciones de compilación solo incrementan la revisión una vez, pero si reconstruye, la revisión se actualiza. Como dijo ktrauberman, se parece a build.revision = date.time, lo que explica por qué los datos no se almacenan en ningún otro lugar excepto en el ensamblaje. Ahora necesito obtener una configuración estándar de MSI para generar un nuevo ProductCode cuando se actualice el proyecto de salida principal. Las configuraciones no permiten revisión, solo compilación. Quiero instalar sobre una instalación existente para hacer una actualización. Necesito investigar.
TonyG
5

Supongo que uno también podría hacer esto con una plantilla de texto en la que crea los atributos de ensamblaje en cuestión sobre la marcha desde el entorno, como lo hace AssemblyVersion.tt a continuación.

<#@ 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
fuente
3

Como continuación de la respuesta de MikeS, quería agregar que VS + Visual Studio Visualization and Modeling SDK debe instalarse para que esto funcione, y también debe modificar el archivo del proyecto. También debería mencionarse que uso Jenkins como servidor de compilación que se ejecuta en una caja de servidor de Windows 2008 R2 con módulo de versión, donde obtengo el BUILD_NUMBER.

Mi archivo de plantilla de texto version.tt se ve así

<#@ 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#>")]

Tengo lo siguiente en los grupos de propiedades

<PropertyGroup>
    <TransformOnBuild>true</TransformOnBuild>
    <OverwriteReadOnlyOutputFiles>true</OverwriteReadOnlyOutputFiles>
    <TransformOutOfDateOnly>false</TransformOutOfDateOnly>
</PropertyGroup>

después de la importación de Microsoft.CSharp.targets, tengo esto (dependiendo de dónde instale VS

<Import Project="C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\TextTemplating\v10.0\Microsoft.TextTemplating.targets" />

En mi servidor de compilación, tengo el siguiente script para ejecutar la transformación de texto antes de la compilación real, para obtener el último número de conjunto de cambios en 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>

De esta manera puedo realizar un seguimiento de las compilaciones Y conjuntos de cambios, por lo que si no he verificado nada desde la última compilación, el último dígito no debería cambiar, sin embargo, podría haber realizado cambios en el proceso de compilación, de ahí la necesidad del penúltimo número . Por supuesto, si realiza varios controles antes de una compilación, solo obtendrá el último cambio reflejado en la versión. Supongo que se puede concatenar eso.

Estoy seguro de que puede hacer algo más elegante y llamar a TFS directamente desde la plantilla tt, sin embargo, esto funciona para mí.

Entonces puedo obtener mi versión en tiempo de ejecución como este

Assembly assembly = Assembly.GetExecutingAssembly();
FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(assembly.Location);
return fvi.FileVersion;
aggatón
fuente
1

Mi solución no requiere la adición de herramientas externas o lenguajes de secuencias de comandos; está prácticamente garantizado que funcionará en su máquina de compilación. Resuelvo este problema en varias partes. Primero, he creado un archivo BUILD.BAT que convierte el parámetro BUILD_NUMBER de Jenkins en una variable de entorno. Utilizo la función "Ejecutar comando por lotes de Windows" de Jenkins para ejecutar el archivo por lotes de compilación ingresando la siguiente información para la compilación de Jenkins:

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

En el entorno de compilación, tengo un archivo build.bat que comienza de la siguiente manera:

     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

Una vez que hice eso, hice clic con el botón derecho en el proyecto que se compilaría en el panel del Explorador de soluciones de Visual Studio y seleccioné Propiedades, seleccioné Eventos de compilación e ingresé la siguiente información como la Línea de comando de evento previo a la compilación, que crea automáticamente un archivo .cs que contiene información sobre el número de compilación basada en la configuración actual de las variables de entorno:

     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%

Es posible que deba adaptarse a su gusto de construcción. Construyo el proyecto manualmente una vez para generar un archivo Version.cs inicial en el directorio de Propiedades del proyecto principal. Por último, incluyo manualmente el archivo Version.cs en la solución de Visual Studio arrastrándolo al panel del Explorador de soluciones, debajo de la pestaña Propiedades para ese proyecto. En compilaciones futuras, Visual Studio lee ese archivo .cs en el momento de compilación de Jenkins y obtiene la información correcta del número de compilación.

johnwbyrd
fuente
1

Entonces, tenemos un proyecto con una solución que contiene varios proyectos que tienen ensamblajes con diferentes números de versión.

Después de investigar varios de los métodos anteriores, acabo de implementar un paso de compilación para ejecutar un script de Powershell que busca y reemplaza el archivo AssemblyInfo.cs. Sigo usando el número de versión 1.0. * En el control de código fuente, y Jenkins simplemente actualiza manualmente el número de versión antes de que se ejecute 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" }

Agregué la opción -Encoding "UTF8" porque git comenzó a tratar el archivo .cs como archivos binarios si no lo hacía. De acuerdo, esto no importaba, ya que nunca cometí el resultado; simplemente surgió mientras estaba probando.

Nuestro entorno de CI ya tiene la posibilidad de asociar la compilación de Jenkins con el git commit en particular (¡gracias al complemento Stash!), Así que no me preocupa que no haya git commit con el número de versión adjunto.

Jonnybot
fuente
0

Este es un mecanismo más simple. Simplemente implica la adición de un paso de compilación de la tarea de comando de Windows Batch antes del paso de MSBuild y el uso de un programa simple de buscar y reemplazar (FART).

El paso por lotes

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

Si está utilizando un control de código fuente distinto de svn, cambie la opción --svn por la adecuada para su entorno scm.

Descarga Fart

dulcefa
fuente
0

Decidí usar un par de métodos usando un script de Powershell precompilado ( https://gist.github.com/bradjolicoeur/e77c508089aea6614af3 ) para incrementar en cada compilación exitosa y luego en Global.asax voy a algo como esto:

  // 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); 
  }

Sigo pensando que todo el proceso es demasiado complicado y voy a buscar un método más eficiente para lograr el mismo resultado. Quería esto principalmente para pasar la versión a SVN y luego a Jenkin sin demasiadas herramientas adicionales.

Netferret
fuente