Подтвердить что ты не робот

Использование msbuild Я хочу обновить файл конфигурации со значениями из teamcity

У меня есть XML, который выглядит примерно так:

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

В TeamCity у меня много свойств системы:

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

Какую магию MsBuild я могу использовать, чтобы подталкивать эти значения в свой XML файл? Всего около 20 предметов.

4b9b3361

Ответ 1

Я просто писал об этом (http://sedodream.com/2011/12/29/UpdatingXMLFilesWithMSBuild.aspx), но я также введу информацию здесь для вас.

Сегодня я только что увидел вопрос о StackOverflow о том, как обновить XML файл с помощью MSBuild во время сборки CI, выполненной из Team City.

Не существует правильного единого ответа, существует несколько разных способов обновления XML файла во время сборки. Наиболее заметно:

  • Используйте SlowCheetah для преобразования файлов для вас.
  • Используйте задачу TransformXml напрямую
  • Используйте встроенную (MSBuild 4.0) задачу XmlPoke
  • Использовать стороннюю библиотеку задач

1 Используйте SlowCheetah для преобразования файлов для вас

Прежде чем вы начнете слишком далеко заходить в этот пост, позвольте мне сначала выбрать вариант № 3, потому что я считаю его самым легким подходом и наиболее легко поддерживается. Вы можете загрузить мою SlowCheetah XML Transforms Visual Studio. Когда вы сделаете это для своих проектов, вы увидите новую команду меню для преобразования файла в сборку (для веб-проектов в пакете/публикации). Если вы создаете из командной строки или сервера CI, преобразования также должны выполняться.

2 Используйте задачу TransformXml напрямую

Если вам нужен метод, в котором у вас есть "основной" XML файл, и вы хотите иметь возможность содержать преобразования в этот файл внутри отдельного XML файла, вы можете напрямую использовать задачу TransformXml. Для получения дополнительной информации см. Предыдущее сообщение в блоге http://sedodream.com/2010/11/18/XDTWebconfigTransformsInNonwebProjects.aspx

3 Используйте встроенную задачу XmlPoke

Иногда не имеет смысла создавать XML файл с преобразованиями для каждого файла XML. Например, если у вас есть файл XML и вы хотите изменить одно значение, но для создания 10 разных файлов подход преобразования XML не очень хорошо масштабируется. В этом случае было бы проще использовать задачу XmlPoke. Обратите внимание, что это требует MSBuild 4.0.

Ниже приведено содержимое sample.xml(получено из вопроса 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>

Итак, в этом случае мы хотим обновить значения элемента value. Итак, первое, что нам нужно сделать, это придумать правильный XPath для всех элементов, которые мы хотим обновить. В этом случае мы можем использовать следующие выражения XPath для каждого элемента значения.

  • /Provisioning.Lib.Processing.XmlConfig/item [ключ = 'HlrFtpPutDir']/значение
  • /Provisioning.Lib.Processing.XmlConfig/item [ключ = 'HlrFtpPutCopyDir']/значение Я не собираюсь переходить к тому, что вам нужно сделать, чтобы понять правильный XPath, потому что это не цель этого сообщения. В средах есть множество ресурсов, связанных с XPath. В разделе ресурсов я связан с онлайн-тестером XPath, который я всегда использую.

Теперь, когда мы получили требуемые выражения XPath, нам нужно построить наши элементы MSBuild, чтобы все обновилось. Вот общая методика:

  • Поместите всю информацию обо всех обновлениях XML в элемент
  • Используйте XmlPoke вместе с пакетной загрузкой MSBuild для выполнения всех обновлений.

Для # 2, если вы не знакомы с пакетной загрузкой MSBuild, я бы рекомендовал купить мою книгу, или вы можете посмотреть ресурсы, которые у меня есть в Интернете, связанные с пакетной загрузкой (ссылка ниже в разделе ресурсов). Ниже вы найдете простой файл MSBuild, который я создал, 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 execute all the XML transformations -->
    <XmlPoke XmlInputPath="$(DestXmlFiles)"
             Query="%(XmlConfigUpdates.XPath)"
             Value="%(XmlConfigUpdates.NewValue)"/>
  </Target>
</Project>

Части, на которые нужно обратить пристальное внимание, - это элемент XmlConfigUpdates и содержимое самой задачи UpdateXml. Что касается XmlConfigUpdates, это имя произвольно, вы можете использовать любое имя, которое хотите, вы можете видеть, что значение Include (которое обычно указывает на файл) просто остается в ConfigUpdates-SampleXml. Значение атрибута Include здесь не используется. Я бы поместил уникальное значение для атрибута Include для каждого обновляемого файла. Это просто облегчает людям понимание того, для чего предназначена эта группа значений, и вы можете использовать ее позже для пакетных обновлений. Элемент XmlConfigUpdates имеет эти два значения метаданных:

  • XPath - Это содержит XPath, необходимый для выбора элемента, который будет обновляться.
  • NewValue - Это содержит новое значение для элемента, который будет обновляться Внутри цели UpdateXml вы можете увидеть, что мы используем задачу XmlPoke и передаем XPath как% (XmlConfigUpdate.XPath), а значение равно% (XmlConfigUpdates.NewValue). Поскольку мы используем синтаксис% (...) для элемента, этот пакет запускает MSBuild. Пакетирование - это когда более чем одна операция выполняется над "пакетом" значений. В этом случае есть две уникальные партии (по 1 для каждого значения в XmlConfigUpdates), поэтому задача XmlPoke будет вызываться два раза. Пакетирование может быть запутанным, поэтому обязательно прочитайте его, если вы не знакомы.

Теперь мы можем использовать msbuild.exe для запуска процесса. Полученный XML файл:

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

Итак, теперь мы видим, как легко было использовать задачу XmlPoke. Давайте теперь рассмотрим, как мы можем расширить этот пример, чтобы управлять обновлениями одного и того же файла для дополнительной среды.

Как управлять обновлениями одного и того же файла для нескольких разных результатов

Поскольку мы создали элемент, который сохранит все необходимые XPath, а также новые значения, у нас будет немного больше гибкости в управлении несколькими средами. В этом случае у нас есть тот же файл, который мы хотим выписать, но нам нужно записать разные значения на основе целевой среды. Делать это довольно легко. Взгляните на содержимое UpdateXml02.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>

  <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 execute all the XML transformations -->
    <XmlPoke XmlInputPath="$(DestXmlFiles)"
             Query="%(XmlConfigUpdates.XPath)"
             Value="%(XmlConfigUpdates.NewValue)"/>
  </Target>
</Project>

Различия довольно просты, я представил новое свойство TargetEnvName, которое позволяет нам узнать, что такое целевая среда. (примечание: я только что составил это имя свойства, использую любое имя, которое вам нравится). Также вы можете видеть, что есть два элемента ItemGroup, содержащие разные элементы XmlConfigUpdate. Каждая ItemGroup имеет условие, основанное на значении TargetEnvName, поэтому будет использоваться только одно из двух значений ItemGroup. Теперь у нас есть один файл MSBuild, который имеет значения для обеих сред. При создании просто передайте свойство TargetEnvName, например msbuild.\UpdateXml02.proj/p: TargetEnvName = Env02. Когда я выполнил это, полученный файл содержит:

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

Вы можете видеть, что файл был обновлен с различными путями в элементе value.

4 Используйте библиотеку сторонних задач

Если вы не используете MSBuild 4, вам понадобится использовать стороннюю библиотеку задач, такую ​​как пакет расширения MSBuild (ссылка в ресурсах).

Надеюсь, что это поможет.

Ресурсы

Ответ 2

Мы меняем значения конфигурации для наших разных сред сборки (например, dev, промежуточная, производственная) с использованием конфигурационных преобразований. Я предполагаю, что конфигурационные преобразования, вероятно, не сработают для вас, но если это возможно, посмотрите этот ответ, в котором показано, как применять .Net config преобразуется в любой файл XML.

Альтернативой может быть использование задачи сборки FileUpdate из проекта проекта MSBuild Community Tasks. Эта задача позволяет использовать регулярные выражения для поиска и замены содержимого в файле. Вот пример:

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

Поскольку вы будете передавать свойства системы TeamCity в FileUpdate, если вы решите пойти со вторым вариантом, взгляните на этот вопрос, чтобы узнать, как могут быть системные свойства ссылаться на MSBuild script.