.net - delivery - jenkins project
¿Cómo puedo incrementar automáticamente la versión del conjunto C#a través de nuestra plataforma CI(Hudson)? (12)
.NET hace esto por ti. En su archivo AssemblyInfo.cs, establezca 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 base a la fecha, usando la época Unix, creo. La construcción se basa en el día actual, y la revisión se basa en el número de segundos desde la medianoche.
Mi grupo y yo somos horrendos al incrementar los números de versión de ensamblaje y enviamos con frecuencia ensamblajes con versiones 1.0.0.0. Obviamente, esto causa muchos dolores de cabeza.
Estamos mejorando mucho con nuestras prácticas a través de nuestra plataforma CI y realmente me gustaría configurarlo para incrementar automáticamente los valores dentro del archivo assemblyinfo.cs
para que las versiones de nuestros ensamblajes se actualicen automáticamente con los cambios de código en esa asamblea.
Previamente había configurado (antes de encontrar a Hudson ) una forma de incrementar el valor a través de msbuild
o la línea de comando (no recuerdo), pero con Hudson, eso actualizará el repositorio SVN y activará OTRA compilación. Eso daría como resultado un ciclo infinito lento, ya que Hudson sondea SVN cada hora.
¿Tener a Hudson incrementa el número de versión es una mala idea? ¿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 en
assemblyinfo.cs
antes de una compilación - Solo incrementa el número de compilación en los ensamblados que han cambiado. Esto puede no ser posible ya que Hudson borra la carpeta del proyecto cada vez que hace una compilación
- Confirma el assemblyinfo.cs cambiado en el repositorio de código (actualmente VisualSVN )
- No hace que Hudson active una nueva construcción la próxima vez que busque cambios
Resolviendo esto en mi cabeza, podría encontrar una solución a la mayoría de esto a través de archivos / comandos por lotes, pero todas mis ideas harían que Hudson activara una nueva construcción la próxima vez que escaneara. 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 ciertos commits de SVN, etc.
Todo lo que he encontrado hasta ahora es solo un artículo explicando cómo hacer que el número de versión se incremente automáticamente, nada tiene en cuenta una plataforma de CI que podría convertirse en un ciclo infinito.
Aquí hay una solución elegante que requiere un poco de trabajo inicial cuando se agrega un nuevo proyecto, pero maneja el proceso muy fácilmente.
La idea es que cada proyecto se vincule a un archivo de Solución que solo contiene 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 ensamblaje se extraen de un archivo una vez compiladas.
Pasos:
- Agregue una clase a su archivo de solución * .cs archivo, llamé min SharedAssemblyProperties.cs
- Elimine toda la información de cs de ese archivo nuevo
- Cortar la información de ensamblado de un archivo de AssemblyInfo: [assembly: AssemblyVersion ("1.0.0.0")] [assembly: AssemblyFileVersion ("1.0.0.0")]
- Agregue la declaración "using System.Reflection;" al archivo y luego pegue datos en su nuevo archivo cs (ex SharedAssemblyProperties.cs)
- Agregue un elemento existente a su proyecto (espere ... lea antes de agregar el archivo)
- Seleccione el archivo y antes de hacer clic en Agregar, haga clic en el menú desplegable al lado del botón Agregar y seleccione "Agregar como enlace".
- Repita los pasos 5 y 6 para todos los proyectos existentes y nuevos en la solución
Cuando agrega el archivo como un enlace, almacena los datos en el archivo de proyecto y, una vez compilado, extrae la información de la versión de ensamblaje de este único archivo.
En su control de origen, agrega un archivo bat o script que simplemente incrementa el archivo SharedAssemblyProperties.cs y todos sus proyectos actualizarán su información de ensamblaje desde ese archivo.
Como continuación de la respuesta de MikeS, quería agregar que se necesita instalar VS + Visual Studio Visualization and Modeling SDK para que esto funcione, y también debe modificar el archivo del proyecto. También se debe mencionar que utilizo Jenkins como servidor de compilación que se ejecuta en un recuadro 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 parece a esto
<#@ 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 (depende 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 la siguiente secuencia de comandos 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 forma puedo realizar un seguimiento de compilaciones Y conjuntos de cambios, así que si no he verificado nada desde la última compilación, el último dígito no debería cambiar, sin embargo, podría haber hecho cambios en el proceso de compilación, por lo tanto, la necesidad del segundo . Por supuesto, si haces múltiples check-ins antes de una compilación, solo obtendrás el último cambio reflejado en la versión. Supongo que podrías concatenar eso.
Estoy seguro de que puedes 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 de esta manera
Assembly assembly = Assembly.GetExecutingAssembly();
FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(assembly.Location);
return fvi.FileVersion;
Decidí usar un par de métodos usando una secuencia de comandos de precompilación de Powershell ( https://gist.github.com/bradjolicoeur/e77c508089aea6614af3 ) para incrementar cada compilación exitosa y luego en Global.asax. He dicho algo así:
// 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);
}
Todavía creo 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.
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 realiza un descubrimiento y reemplazo en el archivo AssemblyInfo.cs. Sigo usando el número de versión 1.0. * En control de 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. Por supuesto, esto no importó, ya que nunca cometí el resultado; simplemente surgió cuando estaba probando.
Nuestro entorno de CI ya tiene una facilidad para asociar la compilación de Jenkins con el compromiso particular de git (¡gracias plugin de Stash!), Por lo que no me preocupa que no haya compromiso de git con el número de versión adjunto.
Este es un mecanismo más simple. Simplemente implica la adición de un paso de compilación de tareas del comando Windows Batch antes del paso MSBuild y el uso de un sencillo programa de búsqueda y reemplazo (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 usa un control de fuente que no sea svn, cambie la opción --svn por la apropiada para su entorno scm.
Esto es lo que hice, para marcar el atributo AssemblyFileVersion.
Se eliminó AssemblyFileVersion de AssemblyInfo.cs
Agregue un nuevo archivo vacío llamado AssemblyFileInfo.cs al proyecto.
Instale el conjunto de herramientas de tareas de comunidad de MSBuild en la máquina de construcción Hudson o como una dependencia de NuGet en su proyecto.
Edite el archivo del proyecto (csproj), solo es un archivo msbuild y agregue lo siguiente.
En algún lugar habrá un <PropertyGroup>
indicando la versión. Cambie eso para que lea, 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 de env que ves allí cuando el proyecto se basa en Hudson (suponiendo que se haya obtenido de la subversión).
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 compilar el proyecto. Puede hacer esto para cualquiera / todos los atributos de versión si lo desea.
El resultado es que cada vez que emite una compilación hudson, el ensamblado resultante obtiene una AssemblyFileVersion de 1.0.HUDSON_BUILD_NR.SVN_REVISION, por ejemplo, 1.0.6.2632, lo que significa la 6ª compilación # en hudson, basada en la revisión de subversión 2632.
Estoy asumiendo que uno también podría hacer esto con una plantilla de texto donde crea los atributos de ensamblado en cuestión sobre la marcha desde el entorno como 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#>")]
Hudson puede configurarse para ignorar los cambios en ciertas rutas y archivos para que no genere 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 , ingresa una o más expresiones regulares para que coincidan con las exclusiones.
Por ejemplo, para ignorar los cambios en el archivo version.properties que puede usar:
/MyProject/trunk/version.properties
Esto funcionará para idiomas distintos de C # y le permite almacenar su información de versión dentro de subversión.
Mi solución no requiere la adición de herramientas externas o lenguajes de scripting. Es casi seguro que funcione en su máquina de construcció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 de compilación por lotes al ingresar 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 derecho en el proyecto para construir en el panel Explorador de Soluciones de Visual Studio y las Propiedades seleccionadas, seleccioné Eventos de compilación e ingresé la siguiente información como Línea de comandos de eventos de preconstrucción, que crea automáticamente un archivo .cs que contiene información del número de compilación en función de 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 ajustarse al gusto de su 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 Explorador de soluciones, debajo de la pestaña Propiedades para ese proyecto. En futuras compilaciones, Visual Studio luego lee ese archivo .cs en el tiempo de compilación de Jenkins y obtiene la información correcta del número de compilación.
Nunca he visto ese trabajo 1.0. * En VS2005 o VS2008. ¿Hay algo que se deba hacer para configurar VS para incrementar los valores?
Si AssemblyInfo.cs está codificado con 1.0. *, ¿Dónde están almacenados los build / revision reales?
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 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 construcción. Esperaba que ese tipo de baile no fuera necesario para VS2008. Tal vez VS2010?
Una alternativa simple es permitir que el entorno de C # incremente la versión de ensamblaje estableciendo el atributo de versión en major.minor.*
(Como se describe en la plantilla de archivo de AssemblyInfo).
Sin embargo, es posible que esté buscando una solución más completa.
EDITAR (Respuesta a la pregunta en un comentario):
Desde 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.*")]