vars .net tfs msbuild continuous-integration config

.net - vars - desencadenar la transformación de configuración en TFS 2010 o msbuild



variables vsts build (6)

Aquí hay una respuesta más fácil para ti. :)

http://social.msdn.microsoft.com/Forums/en-US/tfsbuild/thread/d5c6cc7b-fbb1-4299-a8af-ef602bad8898/

Desde el enlace (en caso de que se mueva / 404 / etc):

Así es como resolví esto. La clave fue editar el archivo * .csproj en el sitio web y agregar lo siguiente al objetivo de AfterBuild (asegúrese de mover el comentario final arriba). Esto es para nuestro proyecto de creación de sitio web en Team Foundation Server.

<Target Name="AfterBuild"> <TransformXml Condition="Exists(''$(OutDir)/_PublishedWebsites/$(TargetName)'')" Source="Web.config" Transform="$(ProjectConfigTransformFileName)" Destination="$(OutDir)/_PublishedWebsites/$(TargetName)/Web.config" /> </Target>

Para evitar que se publique web.debug.config, web.release.config, etc ... asegúrese de configurar la "Acción de compilación" en la ventana de propiedades para cada uno de los archivos de transformación de configuración en "Ninguno". Solo el web.config principal debe tener una "Acción de compilación" de "Contenido"

Una forma fácil de editar el archivo csproj es cargar la extensión "PowerCommands para Visual Studio 2010" o "Productivity Power Tools" a Visual Studio 2010 disponible en la Galería de Visual Studio. Una vez cargado, todo lo que tiene que hacer es hacer clic derecho en el proyecto en su solución y seleccionar "Descargar proyecto". Luego, puede hacer clic derecho nuevamente y seleccionar "Editar ..." para editar el archivo XML csproj directamente. Luego, cuando termines, haz clic derecho nuevamente y selecciona "Recargar proyecto".

Estoy intentando hacer uso de las transformaciones de configuración en un entorno de integración continua.

Necesito una manera de decirle al agente de compilación de TFS que realice las transformaciones. Tenía la esperanza de que funcionara después de descubrir los archivos de transformación de configuración (web.qa-release.config, web.production-release.config, etc ...). Pero no lo hace.

Tengo una definición de compilación TFS que crea las configuraciones correctas (qa-release, production-release, etc.) y tengo algunos archivos .proj específicos que se construyen dentro de estas definiciones y que contienen algunos parámetros específicos del entorno, por ejemplo:

<PropertyGroup Condition=" ''$(Configuration)''==''production-release'' "> <TargetHost Condition=" ''$(TargetHost)''=='''' ">qa.web</TargetHost> ... </PropertyGroup> <PropertyGroup Condition=" ''$(Configuration)''==''qa-release'' "> <TargetHost Condition=" ''$(TargetHost)''=='''' ">production.web</TargetHost> ... </PropertyGroup>

Sé por la salida que se están construyendo las configuraciones correctas. Ahora solo necesito aprender a activar las transformaciones de configuración. ¿Hay algún hocus pocus que pueda agregar al .proj final en la compilación para iniciar la transformación y eliminar los archivos de transformación individuales?


Encontré otra forma de lograr esto en lugar de crear una actividad personalizada. Solo necesita modificar el archivo de proyecto de Visual Studio de la aplicación web que se está construyendo.

Agregue lo siguiente (se puede encontrar un marcador de posición para el objetivo ''AfterBuild'' hacia el final del archivo de proyecto):

<Target Name="AfterBuild" Condition="$(IsAutoBuild)==''True''"> <ItemGroup> <DeleteAfterBuild Include="$(WebProjectOutputDir)/Web.*.config" /> </ItemGroup> <TransformXml Source="Web.config" Transform="$(ProjectConfigTransformFileName)" Destination="$(WebProjectOutputDir)/Web.config"/> <Delete Files="@(DeleteAfterBuild)" /> </Target>

Luego solo tiene que agregar /p:IsAutoBuild="True" al campo ''Argumentos de MSBuild'' que se encuentra en la sección ''Avanzado'' de la definición de compilación.

Esto obligará a TFS 2010 a realizar las transformaciones en web.config cuando TFS realice la compilación.

Más detalles se pueden encontrar en el blog de Kevin Daly .


Finalmente logré que esto funcionara. Estoy usando TFS 2008, pero también estoy usando MSBuild 4.0, así que debería funcionar para ti.

Primero, agregue esta importación a TFSBuild.proj:

<Import Project="$(MSBuildExtensionsPath)/Microsoft/VisualStudio/v10.0/WebApplications/Microsoft.WebApplication.targets" />

A continuación, agregue un destino BeforeDropBuild:

<Target Name="BeforeDropBuild"> <TransformXml Source="$(SolutionRoot)/MySite/Web.config" Transform="$(SolutionRoot)/MySite/Web.QA.config" Destination="$(OutDir)/_PublishedWebsites/MySite/Web.QA.config.transformed" /> </Target>

Luego puede copiar el Web.QA.config.transformado a donde lo necesite para ir.


La funcionalidad de transformación web.config que se agregó a Proyectos de sitio web en Visual Studio 2010 está deshabilitada de forma predeterminada para las compilaciones de línea de comandos y TFS.

Hay dos soluciones relativamente sencillas:

Opción 1 : edite la definición de compilación y agregue lo siguiente al campo "Argumentos de MSBuild":

/p:UseWPP_CopyWebApplication=true /p:PipelineDependsOnBuild=false

UseWPP_CopyWebApplication hará que se active el nuevo Canal de publicación en la Web (WPP) para la compilación. WPP realiza las transformaciones web.config y también se puede usar para bloquear cosas como, por ejemplo, que los archivos .PDB se copien en la carpeta bin.

Opción 2 : Tanto MSBuild como WPP son completamente extensibles. Cree un nuevo archivo XML en el mismo directorio que su proyecto y use la extensión ".targets", por ejemplo, ProjectName.custom.targets. Coloque el siguiente código de MSBuild en el archivo de objetivos:

<?xml version="1.0" encoding="utf-8"?> <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <PropertyGroup> <UseWPP_CopyWebApplication>True</UseWPP_CopyWebApplication> <PipelineDependsOnBuild>False</PipelineDependsOnBuild> </PropertyGroup> </Project>

Haga clic derecho en su sitio web y seleccione "Descargar proyecto". Haga clic derecho en el proyecto descargado y seleccione Editar. Desplácese hasta la parte inferior del archivo de proyecto y busque estas líneas:

<Import Project="$(MSBuildBinPath)/Microsoft.CSharp.targets" /> <Import Project="$(MSBuildExtensionsPath32)/Microsoft/VisualStudio/v10.0/WebApplications/Microsoft.WebApplication.targets" />

Estas líneas son donde se engancha el proceso de compilación de C # y proyecto web. Inserte una importación en sus extensiones de compilación personalizadas (archivo de destino) inmediatamente antes de la importación de CSharp:

<Import Project="ProjectName.custom.targets"/> <Import Project="$(MSBuildBinPath)/Microsoft.CSharp.targets" /> <Import Project="$(MSBuildExtensionsPath32)/Microsoft/VisualStudio/v10.0/WebApplications/Microsoft.WebApplication.targets" />

Eso es todo, ya puedes irte. El método de personalización de MSBuild es un poco más de trabajo para configurar, pero la ventaja es que puede usar el nuevo archivo de objetivos para "enganchar" en el proceso de compilación y tener mucho más control sobre cómo se realiza la compilación en el servidor. Por ejemplo, podría enlazar tareas para realizar la compresión de CSS y JS.

También recomendaría ver "objetivos de wpp": si nombra otro archivo de MSBuild con el nombre específico "ProjectName.wpp.targets", puede controlar todo el proceso de publicación del sitio web. Lo usamos para eliminar los archivos de documentación javascript -vsdoc cuando se copia la salida del sitio web publicado:

<ItemGroup> <ExcludeFromPackageFiles Include="Scripts/**/*-vsdoc.js;Resources/Scripts/**/-vsdoc.js"> <FromTarget>Project</FromTarget> </ExcludeFromPackageFiles> </ItemGroup>

Todo lo dicho, probablemente sea mejor dejar de lado por completo los componentes web.configs de producción. Colocamos las transformaciones directamente en nuestra máquina de implementación de producción y usamos powershell para transformar a medida que implementamos la aplicación.


Para hacer esto en WorkFlow, debe crear una actividad personalizada. Hay un buen artículo al respecto here

Para esta actividad específica, debe crear un Proyecto de actividad (cambiarlo de .Net 4 Client profile a .Net 4 ) y hacer referencia a Microsoft.Build.Framework y Microsoft.Build.Utilities.v4.0 del GAC y luego a Microsoft.Web .Publicaciones. Las tareas de % programfiles% / msbuild / Microsoft / VisualStudio / v10.0 / WebApplications (% programfiles (x86)% si está en un sistema de 64 bits).

Cuando hayas terminado, agregas estas dos clases:

Primero, hay un talón

internal class BuildEngineStub : IBuildEngine { public bool BuildProjectFile(string projectFileName, string[] targetNames, System.Collections.IDictionary globalProperties, System.Collections.IDictionary targetOutputs) { throw new NotImplementedException(); } public int ColumnNumberOfTaskNode { get { throw new NotImplementedException(); } } public bool ContinueOnError { get { throw new NotImplementedException(); } } public int LineNumberOfTaskNode { get { throw new NotImplementedException(); } } public void LogCustomEvent(CustomBuildEventArgs e) { } public void LogErrorEvent(BuildErrorEventArgs e) { } public void LogMessageEvent(BuildMessageEventArgs e) { } public void LogWarningEvent(BuildWarningEventArgs e) { } public string ProjectFileOfTaskNode { get { throw new NotImplementedException(); } } }

Luego está la propia clase de actividad:

[BuildActivity(HostEnvironmentOption.Agent)] public sealed class WebConfigTransform : CodeActivity { private const string WEB_CONFIG = "Web.config"; private const string WEB_CONFIG_TRANSFORM_FORMAT = "Web.{0}.config"; private IBuildEngine _buildEngine { get { return new BuildEngineStub(); } } [RequiredArgument] public InArgument<string> TransformationName { get; set; } [RequiredArgument] public InArgument<string> SourceFolder { get; set; } [RequiredArgument] public InArgument<string> DestinationFolder { get; set; } protected override void Execute(CodeActivityContext context) { var transformationName = context.GetValue(this.TransformationName); var sourceFolder = context.GetValue(this.SourceFolder); var destinationFolder = context.GetValue(this.DestinationFolder); var source = Path.Combine(sourceFolder, WEB_CONFIG); var destination = Path.Combine(destinationFolder, WEB_CONFIG); var destinationbackup = string.Format("{0}.bak", destination); var transform = Path.Combine(sourceFolder, string.Format(WEB_CONFIG_TRANSFORM_FORMAT, transformationName)); if(!File.Exists(source)) throw new ArgumentException("Web.config file doesn''t exist in SourceFolder"); if (!File.Exists(transform)) throw new ArgumentException("Web.config transformation doesn''t exist in SourceFolder"); if (File.Exists(destination)) { File.Copy(destination, destinationbackup); File.Delete(destination); } var transformation = new TransformXml(); transformation.Source = new TaskItem(source); transformation.Destination = new TaskItem(destination); transformation.Transform = new TaskItem(transform); transformation.BuildEngine = _buildEngine; if (transformation.Execute()) { File.Delete(destinationbackup); } else { File.Copy(destinationbackup, destination); File.Delete(destinationbackup); } } }

El motivo de BuildEngineStub es que la clase TransformXml lo usa para hacer el registro.

Lo único que debe tener cuidado es que la función TransformXml.Execute bloquea el archivo de configuración de origen hasta que finaliza el proceso de compilación.


Todo lo que debe hacer es configurar qué configuración debe usarse dentro de la definición de compilación de TFS.

  1. Ir a Team Explorer> Builds
  2. Edita la definición de tu compilación (o crea una nueva)
  3. En el paso "Proceso" hay una configuración para "Configuraciones para construir".

En mi caso, he configurado una configuración específicamente para CI que luego realiza las transformaciones web.config correctas. Asegúrate de haber agregado el archivo de transformación "CI" y estar listo.