xml msbuild teamcity slowcheetah

xml - Usando msbuild quiero actualizar un archivo de configuración con valores de teamcity



slowcheetah (2)

Acabo de escribir en un blog acerca de esto ( http://sedodream.com/2011/12/29/UpdatingXMLFilesWithMSBuild.aspx ) pero también pegaré la información aquí para ti.

Hoy acabo de ver una pregunta publicada en StackOverflow preguntando cómo actualizar un archivo XML usando MSBuild durante una compilación CI ejecutada desde Team City.

No hay una sola respuesta correcta, hay varias formas diferentes de actualizar un archivo XML durante una compilación. Más destacado:

  1. Usa SlowCheetah para transformar los archivos por ti
  2. Usa la tarea TransformXml directamente
  3. Use la tarea incorporada (MSBuild 4.0) XmlPoke
  4. Use una biblioteca de tareas de terceros

1 Usa SlowCheetah para transformar los archivos por ti

Antes de comenzar a leer demasiado en esta publicación, permítaseme repasar la opción n. ° 3 porque creo que es el enfoque más fácil y el más fácil de mantener. Puede descargar mi complemento SlowCheetah XML Transforms de Visual Studio. Una vez que haga esto para sus proyectos, verá un nuevo comando de menú para transformar un archivo en compilación (para proyectos web en paquete / publicación). Si compila desde la línea de comando o un servidor de CI, las transformaciones también deberían ejecutarse.

2 Usa la tarea TransformXml directamente

Si desea una técnica donde tenga un archivo XML "principal" y desee poder contener transformaciones para ese archivo dentro de un archivo XML separado, entonces puede usar la tarea TransformXml directamente. Para obtener más información, consulte mi publicación de blog anterior en http://sedodream.com/2010/11/18/XDTWebconfigTransformsInNonwebProjects.aspx

3 Usa la tarea integrada XmlPoke

A veces no tiene sentido crear un archivo XML con transformaciones para cada archivo XML. Por ejemplo, si tiene un archivo XML y desea modificar un solo valor, pero para crear 10 archivos diferentes, el enfoque de transformación XML no se adapta bien. En este caso, podría ser más fácil usar la tarea XmlPoke. Tenga en cuenta que esto requiere MSBuild 4.0.

A continuación se muestran los contenidos de sample.xml (proviene de la pregunta SO).

<Provisioning.Lib.Processing.XmlConfig instancetype="XmlConfig, Processing, Version=1.0.0.0, Culture=neutral"> <item> <key>IsTestEnvironment</key> <value>True</value> <encrypted>False</encrypted> </item> <item> <key>HlrFtpPutDir</key> <value>C:/DevPath1</value> <encrypted>False</encrypted> </item> <item <key>HlrFtpPutCopyDir</key> <value>C:/DevPath2</value> <encrypted>False</encrypted> </item> </Provisioning.Lib.Processing.XmlConfig>

Entonces, en este caso, queremos actualizar los valores del elemento de valor. Entonces, lo primero que debemos hacer es encontrar el XPath correcto para todos los elementos que queremos actualizar. En este caso, podemos usar las siguientes expresiones XPath para cada elemento de valor.

  • /Provisioning.Lib.Processing.XmlConfig/item[key=''HlrFtpPutDir'']/value
  • /Provisioning.Lib.Processing.XmlConfig/item[key=''HlrFtpPutCopyDir'']/value No voy a repasar lo que necesita hacer para descubrir el XPath correcto porque ese no es el propósito de esta publicación. Hay un montón de recursos relacionados con XPath en las interwebs. En la sección de recursos que he vinculado al probador XPath en línea que siempre uso.

Ahora que tenemos las expresiones XPath necesarias, necesitamos construir nuestros elementos MSBuild para que todo esté actualizado. Aquí está la técnica general:

  1. Coloque toda la información para todas las actualizaciones XML en un elemento
  2. Use XmlPoke junto con el procesamiento en lotes MSBuild para realizar todas las actualizaciones

Para el número 2, si no está familiarizado con el procesamiento en lotes de MSBuild, le recomendaría comprar mi libro o puede consultar los recursos que tengo en línea relacionados con el procesamiento por lotes (el enlace se encuentra más abajo en la sección de recursos). A continuación encontrará un archivo simple de MSBuild que creé, UpdateXm01.proj.

<?xml version="1.0" encoding="utf-8"?> <Project ToolsVersion="4.0" DefaultTargets="UpdateXml" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <PropertyGroup> <SourceXmlFile>$(MSBuildProjectDirectory)/sample.xml</SourceXmlFile> <DestXmlFiles>$(MSBuildProjectDirectory)/result.xml</DestXmlFiles> </PropertyGroup> <ItemGroup> <!-- Create an item which we can use to bundle all the transformations which are needed --> <XmlConfigUpdates Include="ConfigUpdates-SampleXml"> <XPath>/Provisioning.Lib.Processing.XmlConfig/item[key=''HlrFtpPutDir'']/value</XPath> <NewValue>H:/ReleasePath1</NewValue> </XmlConfigUpdates> <XmlConfigUpdates Include="ConfigUpdates-SampleXml"> <XPath>/Provisioning.Lib.Processing.XmlConfig/item[key=''HlrFtpPutCopyDir'']/value</XPath> <NewValue>H:/ReleasePath2</NewValue> </XmlConfigUpdates> </ItemGroup> <Target Name="UpdateXml"> <Message Text="Updating XML file at $(DestXmlFiles)" /> <Copy SourceFiles="$(SourceXmlFile)" DestinationFiles="$(DestXmlFiles)" /> <!-- Now let''s execute all the XML transformations --> <XmlPoke XmlInputPath="$(DestXmlFiles)" Query="%(XmlConfigUpdates.XPath)" Value="%(XmlConfigUpdates.NewValue)"/> </Target> </Project>

Las partes a las que se debe prestar especial atención son el elemento XmlConfigUpdates y el contenido de la tarea UpdateXml. En cuanto a XmlConfigUpdates, ese nombre es arbitrario, puede usar el nombre que desee, puede ver que el valor Incluir (que normalmente apunta a un archivo) simplemente se deja en ConfigUpdates-SampleXml. El valor para el atributo Incluir no se usa aquí. Yo colocaría un valor único para el atributo Incluir para cada archivo que está actualizando. Esto simplemente facilita que las personas comprendan para qué es ese grupo de valores, y usted puede usarlo más tarde para realizar actualizaciones por lotes. El elemento XmlConfigUpdates tiene estos dos valores de metadatos:

  • XPath: contiene la XPath requerida para seleccionar el elemento que se va a actualizar
  • NewValue: contiene el nuevo valor para el elemento que se va a actualizar. Dentro del destino UpdateXml, puede ver que estamos usando la tarea XmlPoke y pasando el XPath como% (XmlConfigUpdate.XPath) y el valor como% (XmlConfigUpdates) .Nuevo valor). Dado que estamos utilizando la sintaxis% (...) en un elemento, esto inicia el procesamiento por lotes de MSBuild. El procesamiento por lotes es donde se realiza más de una operación sobre un "lote" de valores. En este caso, hay dos lotes únicos (1 para cada valor en XmlConfigUpdates) por lo que la tarea XmlPoke se invocará dos veces. El procesamiento por lotes puede ser confuso, así que asegúrese de leerlo si no está familiarizado.

Ahora podemos usar msbuild.exe para comenzar el proceso. El archivo XML resultante es:

<Provisioning.Lib.Processing.XmlConfig instancetype="XmlConfig, Processing, Version=1.0.0.0, Culture=neutral"> <item> <key>IsTestEnvironment</key> <value>True</value> <encrypted>False</encrypted> </item> <item> <key>HlrFtpPutDir</key> <value>H:/ReleasePath1</value> <encrypted>False</encrypted> </item> <item> <key>HlrFtpPutCopyDir</key> <value>H:/ReleasePath2</value> <encrypted>False</encrypted> </item> </Provisioning.Lib.Processing.XmlConfig>

Entonces ahora podemos ver lo fácil que fue usar la tarea XmlPoke. Veamos ahora cómo podemos extender este ejemplo para administrar actualizaciones en el mismo archivo para un entorno adicional.

Cómo administrar actualizaciones en el mismo archivo para múltiples resultados diferentes

Dado que hemos creado un artículo que mantendrá todos los XPath necesarios así como los nuevos valores, tenemos un poco más de flexibilidad en la administración de múltiples entornos. En este escenario, tenemos el mismo archivo que queremos escribir, pero necesitamos escribir diferentes valores basados ​​en el entorno objetivo. Hacer esto es bastante fácil. Echa un vistazo a los contenidos de UpdateXml02.proj a continuación.

<?xml version="1.0" encoding="utf-8"?> <Project ToolsVersion="4.0" DefaultTargets="UpdateXml" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <PropertyGroup> <SourceXmlFile>$(MSBuildProjectDirectory)/sample.xml</SourceXmlFile> <DestXmlFiles>$(MSBuildProjectDirectory)/result.xml</DestXmlFiles> </PropertyGroup> <PropertyGroup> <!-- We can set a default value for TargetEnvName --> <TargetEnvName>Env01</TargetEnvName> </PropertyGroup> <ItemGroup Condition=" ''$(TargetEnvName)'' == ''Env01'' "> <!-- Create an item which we can use to bundle all the transformations which are needed --> <XmlConfigUpdates Include="ConfigUpdates"> <XPath>/Provisioning.Lib.Processing.XmlConfig/item[key=''HlrFtpPutDir'']/value</XPath> <NewValue>H:/ReleasePath1</NewValue> </XmlConfigUpdates> <XmlConfigUpdates Include="ConfigUpdates"> <XPath>/Provisioning.Lib.Processing.XmlConfig/item[key=''HlrFtpPutCopyDir'']/value</XPath> <NewValue>H:/ReleasePath2</NewValue> </XmlConfigUpdates> </ItemGroup> <ItemGroup Condition=" ''$(TargetEnvName)'' == ''Env02'' "> <!-- Create an item which we can use to bundle all the transformations which are needed --> <XmlConfigUpdates Include="ConfigUpdates"> <XPath>/Provisioning.Lib.Processing.XmlConfig/item[key=''HlrFtpPutDir'']/value</XPath> <NewValue>G:/SomeOtherPlace/ReleasePath1</NewValue> </XmlConfigUpdates> <XmlConfigUpdates Include="ConfigUpdates"> <XPath>/Provisioning.Lib.Processing.XmlConfig/item[key=''HlrFtpPutCopyDir'']/value</XPath> <NewValue>G:/SomeOtherPlace/ReleasePath2</NewValue> </XmlConfigUpdates> </ItemGroup> <Target Name="UpdateXml"> <Message Text="Updating XML file at $(DestXmlFiles)" /> <Copy SourceFiles="$(SourceXmlFile)" DestinationFiles="$(DestXmlFiles)" /> <!-- Now let''s execute all the XML transformations --> <XmlPoke XmlInputPath="$(DestXmlFiles)" Query="%(XmlConfigUpdates.XPath)" Value="%(XmlConfigUpdates.NewValue)"/> </Target> </Project>

Las diferencias son bastante simples, introduje una nueva propiedad, TargetEnvName que nos permite saber cuál es el entorno de destino. (nota: acabo de inventar ese nombre de propiedad, usar el nombre que desee). También puede ver que hay dos elementos de ItemGroup que contienen diferentes elementos de XmlConfigUpdate. Cada ItemGroup tiene una condición basada en el valor de TargetEnvName, por lo que solo se utilizará uno de los dos valores de ItemGroup. Ahora tenemos un único archivo MSBuild que tiene los valores para ambos entornos. Al construir simplemente pase en la propiedad TargetEnvName, por ejemplo, msbuild. / UpdateXml02.proj / p: TargetEnvName = Env02. Cuando lo ejecuté, el archivo resultante contiene:

<Provisioning.Lib.Processing.XmlConfig instancetype="XmlConfig, Processing, Version=1.0.0.0, Culture=neutral"> <item> <key>IsTestEnvironment</key> <value>True</value> <encrypted>False</encrypted> </item> <item> <key>HlrFtpPutDir</key> <value>G:/SomeOtherPlace/ReleasePath1</value> <encrypted>False</encrypted> </item> <item> <key>HlrFtpPutCopyDir</key> <value>G:/SomeOtherPlace/ReleasePath2</value> <encrypted>False</encrypted> </item> </Provisioning.Lib.Processing.XmlConfig>

Puede ver que el archivo se ha actualizado con diferentes rutas en el elemento de valor.

4 Utilice una biblioteca de tareas de terceros

Si no está utilizando MSBuild 4, necesitará usar una biblioteca de tareas de terceros como MSBuild Extension Pack (enlace en recursos).

Espero que ayude.

Recursos

Tengo un XML que se ve así:

<?xml version="1.0" encoding="utf-8"?> <XmlConfig instancetype="XmlConfig, Processing, Version=1.0.0.0, Culture=neutral"> <item> <key>IsTestEnvironment</key> <value>True</value> <encrypted>False</encrypted> </item> <item> <key>HlrFtpPutDir</key> <value>C:/DevPath1</value> <encrypted>False</encrypted> </item> <item> <key>HlrFtpPutCopyDir</key> <value>C:/DevPath2</value> <encrypted>False</encrypted> </item> .... </Provisioning.Lib.Processing.XmlConfig>

En TeamCity tengo muchas propiedades de sistema:

system.HlrFtpPutDir H:/ReleasePath1 system.HlrFtpPutCopyDir H:/ReleasePath2

¿Qué tipo de magia de MsBuild puedo usar para insertar estos valores en mi archivo XML? En total hay 20 o más elementos.


Cambiamos los valores de configuración para nuestros diferentes entornos de compilación (por ejemplo, desarrollo, puesta en escena, producción) usando transformaciones de configuración. Supongo que las transformaciones de configuración probablemente no funcionarán para usted, pero si es posible, consulte esta respuesta que muestra cómo aplicar .Net config se transforma en cualquier archivo XML.

Una alternativa sería usar la tarea de compilación FileUpdate del proyecto MSBuild Community Tasks . Esta tarea le permite usar expresiones regulares para buscar y reemplazar contenido en un archivo. Aquí hay un ejemplo:

<FileUpdate Files="version.txt" Regex="(/d+)/.(/d+)/.(/d+)/.(/d+)" ReplacementText="$1.$2.$3.123" />

Debido a que pasaría las propiedades del sistema TeamCity a FileUpdate si decide ir con la segunda opción, eche un vistazo a esta pregunta para ver cómo se pueden hacer referencia a las propiedades del sistema en un script de MSBuild.