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


Tengo un XML que se ve algo como esto:

<?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 del 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 unos 20 artículos.

Author: rjzii, 2011-12-28

2 answers

Acabo de bloguear sobre esto ( http://sedodream.com/2011/12/29/UpdatingXMLFilesWithMSBuild.aspx) pero voy a pegar la información aquí para usted también.

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

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

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

1 Use SlowCheetah para transformar los archivos por usted

Antes de empezar a leer demasiado en este post déjame repasar la opción #3 primero porque creo que es el enfoque más fácil y el más fácil de mantener. Puede descargar my SlowCheetah XML Transforms Visual Studio add in. Una vez que usted hace esto para su proyectos verá un nuevo comando de menú para transformar un archivo en la compilación (para proyectos web en paquete/publicación). Si compila desde la línea de comandos o un servidor CI, las transformaciones también deberían ejecutarse.

2 Utilice la tarea TransformXml directamente

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

3 Utilice la tarea XmlPoke incorporada

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 crear 10 archivos diferentes, el enfoque de transformación XML no se escala bien. En este caso, podría ser más fácil utilizar la tarea XmlPoke. Nota: esto requiere MSBuild 4.0.

Abajo están los contenidos de la muestra.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>

Así que en este caso queremos actualizar los valores del elemento value. Así que lo primero que tenemos que hacer es llegar al XPath correcto para todos los elementos que queremos actualizar. En este caso podemos utilizar las siguientes expresiones XPath para cada valor elemento.

  • /Aprovisionamiento.Lib.Procesamiento.XmlConfig / item [key= 'HlrFtpPutDir'] / value
  • /Aprovisionamiento.Lib.Procesamiento.XmlConfig / item [key = 'HlrFtpPutCopyDir'] / value No voy a repasar lo que tienes que hacer para averiguar el XPath correcto porque ese no es el propósito de este post. Hay un montón de recursos relacionados con XPath en las interwebs. En la sección de recursos he enlazado al probador XPath en línea que siempre uso.

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

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

Para #2 si no está tan familiarizado con el procesamiento por lotes de MSBuild, le recomendaría comprar mi libro o puede echar un vistazo a los recursos que tengo en línea relacionados con el procesamiento por lotes (el enlace está a continuación en recursos apartado). A continuación encontrará un simple archivo 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 hay que prestar mucha atención son el elemento XmlConfigUpdates y el contenido de la propia tarea UpdateXml. Con respecto a XmlConfigUpdates, ese nombre es arbitrario puede usar el nombre que desee, puede ver que el valor Include (que normalmente apunta a un archivo) simplemente se deja en ConfigUpdates-SampleXml. El valor del atributo Include no se utiliza aquí. Me gustaría colocar un único valor del atributo Include para cada archivo que esté actualizando. Esto solo hace que sea más fácil para las personas entender para qué sirve ese grupo de valores, y puede usarlo más tarde para actualizaciones por lotes. El elemento XmlConfigUpdates tiene estos dos valores de metadatos:

  • XPath -- Contiene el XPath necesario para seleccionar el elemento que se va a actualizar
  • newValue -- Este contiene el nuevo valor para el elemento que se va a actualizar Dentro del objetivo de UpdateXml puede ver que estamos usando la tarea XmlPoke y pasando el XPath como %(XmlConfigUpdate.XPath) y el valor como %(XmlConfigUpdates.newValue). Dado que estamos usando 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 será invocada dos veces. El procesamiento por lotes puede ser confuso, así que asegúrese de leer sobre él si no lo está familiarizados.

Ahora podemos usar msbuild.exe para iniciar 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>

Así que ahora podemos ver lo fácil que era usar la tarea XmlPoke. Ahora echemos un vistazo a cómo podemos extender este ejemplo para administrar actualizaciones al mismo archivo para un entorno adicional.

Cómo administrar las actualizaciones del mismo archivo para múltiples resultados diferentes

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

<?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, he introducido una nueva propiedad, TargetEnvName que nos permite saber cuál es el entorno de destino. (nota: Acabo de inventar el nombre de la propiedad, usa el nombre que quieras). También puede ver que hay dos elementos ItemGroup que contienen diferentes elementos 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 solo archivo MSBuild que tiene los valores para ambos entornos. Al construir simplemente pase la propiedad TargetEnvName, por ejemplo msbuild .\UpdateXml02.proj / p: TargetEnvName = Env02. Cuando ejecuté esto, 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>

Puedes ver que el archivo se ha actualizado con diferentes rutas en el elemento value.

4 Usar una biblioteca de tareas de terceros

Si no está utilizando MSBuild 4, entonces necesitará usar una biblioteca de tareas de terceros como el Paquete de extensiones de MSBuild (enlace en recursos).

Espero que eso ayude.

Recursos

 69
Author: Sayed Ibrahim Hashimi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-18 00:04:29

Cambiamos los valores de configuración para nuestros diferentes entornos de compilación (por ejemplo, dev, staging, production) usando transformaciones de configuración. Supongo que las transformaciones de configuración probablemente no funcionen para usted, pero si es una posibilidad, consulte esta respuesta que muestra cómo aplicar transformaciones de configuración de.Net a cualquier archivo XML.

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

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

Dado que estaría pasando las propiedades del sistema de TeamCity a FileUpdate si decide optar por la segunda opción, eche un vistazo a esta pregunta para ver cómo se puede hacer referencia a las propiedades del sistema en un script de MSBuild.

 4
Author: Jonathan McIntire,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:09:56