Używając msbuild chcę zaktualizować plik konfiguracyjny z wartościami z teamcity

Mam jakiś XML, który wygląda mniej więcej tak:

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

W TeamCity mam wiele właściwości systemu:

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

Jakiego rodzaju magii MsBuild mogę użyć, aby wcisnąć te wartości do mojego pliku XML? W sumie jest 20 lub tak przedmiotów.

Author: rjzii, 2011-12-28

2 answers

Właśnie o tym pisałem ( http://sedodream.com/2011/12/29/UpdatingXMLFilesWithMSBuild.aspx ) ale wkleję info tutaj również dla Ciebie.

Dzisiaj na StackOverflow pojawiło się pytanie, Jak zaktualizować plik XML za pomocą MSBuild podczas kompilacji CI wykonywanej z Team City.

Nie ma poprawnej pojedynczej odpowiedzi, istnieje kilka różnych sposobów aktualizacji pliku XML podczas kompilacji. Przede wszystkim:

  1. użyj SlowCheetah, aby przekształcić pliki dla ciebie
  2. użyj zadania TransformXml bezpośrednio
  3. Użyj wbudowanego (MSBuild 4.0) zadania XmlPoke
  4. użyj biblioteki zadań innych firm

1 Użyj SlowCheetah, aby przekształcić pliki za Ciebie

Zanim zaczniesz czytać zbyt daleko w tym poście pozwól mi przejść na opcję # 3 najpierw, ponieważ myślę, że jest to najprostsze podejście i najłatwiej utrzymać. Możesz pobrać mój SlowCheetah XML Transforms Visual Studio add in. Gdy zrobisz to dla swojego projekty pojawi się nowe polecenie menu, aby przekształcić plik w build (dla projektów internetowych w pakiecie/publikacji). Jeśli budujesz z wiersza poleceń lub z serwera CI, transformacje również powinny być uruchomione.

2 Użyj zadania TransformXml bezpośrednio

Jeśli chcesz mieć technikę, w której masz "główny" plik XML i chcesz być w stanie zawierać transformacje do tego pliku wewnątrz oddzielnego pliku XML, możesz użyć zadania TransformXml bezpośrednio. Po więcej informacji zobacz mój poprzedni wpis na blogu http://sedodream.com/2010/11/18/XDTWebconfigTransformsInNonwebProjects.aspx

3 Użyj wbudowanego zadania XmlPoke

Czasami nie ma sensu tworzyć pliku XML z transformacjami dla każdego pliku XML. Na przykład, jeśli Masz plik XML i chcesz zmodyfikować pojedynczą wartość, ale aby utworzyć 10 różnych plików, podejście transformacji XML nie skaluje się dobrze. W takim przypadku może być łatwiejsze użycie zadania XmlPoke. Uwaga to wymaga MSBuild 4.0.

Poniżej znajduje się zawartość próbki.xml (pochodzi z tzw. pytania).

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

Więc w tym przypadku chcemy zaktualizować wartości elementu value. Więc pierwszą rzeczą, którą musimy zrobić, to wymyślić poprawną XPath dla wszystkich elementów, które chcemy zaktualizować. W tym przypadku możemy użyć następujących wyrażeń XPath dla każdej wartości element.

  • /Provisioning.Lib.Przetwarzam.XmlConfig / item[key= 'HlrFtpPutDir'] / value
  • /Provisioning.Lib.Przetwarzam.XmlConfig / item[key= 'HlrFtpPutCopyDir'] / value Nie będę się zastanawiał, co trzeba zrobić, aby ustalić poprawny XPath, ponieważ nie jest to cel tego postu. Istnieje kilka zasobów związanych z XPath w interwebs. W dziale zasoby połączyłem się z testerem Online XPath, z którego zawsze korzystam.

Teraz, gdy mamy wymagane wyrażenia XPath musimy skonstruować nasze elementy MSBuild, aby wszystko było zaktualizowane. Oto ogólna technika:

  1. Umieść wszystkie informacje o wszystkich aktualizacjach XML w pozycji
  2. użyj XmlPoke wraz z MSBuild batching, aby wykonać wszystkie aktualizacje

Dla #2 jeśli nie jesteś tak zaznajomiony z MSBuild batching, polecam zakup mojej książki lub możesz spojrzeć na zasoby, które mam w Internecie związane z batowaniem (link znajduje się poniżej w zasobach sekcji). Poniżej znajdziesz prosty plik MSBuild, który stworzyłem, 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>

Częściami, na które należy zwrócić szczególną uwagę, jest pozycja XmlConfigUpdates i zawartość samego zadania UpdateXml. Jeśli chodzi o XmlConfigUpdates, ta nazwa jest dowolna możesz użyć dowolnej nazwy, możesz zobaczyć, że wartość Include (która zazwyczaj wskazuje na plik) jest po prostu pozostawiona w ConfigUpdates-SampleXml. Wartość atrybutu Include nie jest tutaj używana. Umieściłbym unikalny wartość atrybutu Include dla każdego aktualizowanego pliku. To po prostu ułatwia ludziom zrozumienie, do czego służy ta grupa wartości, a następnie można jej użyć do aktualizacji wsadowych. Pozycja XmlConfigUpdates ma te dwie wartości metadanych:

  • XPath -- Zawiera XPath wymagane do wybrania elementu, który ma być aktualizowany
  • NewValue -- Zawiera nową wartość dla elementu, który będzie aktualizowany Wewnątrz updatexml target you widać, że używamy zadania XmlPoke i przekazujemy XPath jako %(XmlConfigUpdate.XPath) i wartość jako %(XmlConfigUpdates.NewValue). Ponieważ używamy składni % ( ... ) na elemencie, rozpoczynamy grupowanie MSBuild. Porcjowanie jest tam, gdzie więcej niż jedna operacja jest wykonywana na "partii" wartości. W tym przypadku są dwie unikalne Partie (1 dla każdej wartości w XmlConfigUpdates), więc zadanie XmlPoke zostanie wywołane dwa razy. Dozowanie może być mylące, więc upewnij się, że zapoznałeś się z nim, jeśli nie jesteś znajome.

Teraz możemy użyć msbuild.exe, aby rozpocząć proces. Wynikowy plik XML to:

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

Więc teraz możemy zobaczyć, jak łatwo było użyć zadania XmlPoke. Przyjrzyjmy się teraz, jak możemy rozszerzyć ten przykład, aby zarządzać aktualizacjami tego samego pliku dla dodatkowego środowiska.

Jak zarządzać aktualizacjami do tego samego pliku dla wielu różnych wyników

Ponieważ stworzyliśmy element, który zachowa wszystkie potrzebne XPath, a także nowe wartości, mamy nieco większa elastyczność w zarządzaniu wieloma środowiskami. W tym scenariuszu mamy ten sam plik, który chcemy zapisać, ale musimy zapisać różne wartości w zależności od docelowego środowiska. Robienie tego jest całkiem proste. Spójrz na zawartość 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's execute all the XML transformations -->
    <XmlPoke XmlInputPath="$(DestXmlFiles)"
             Query="%(XmlConfigUpdates.XPath)"
             Value="%(XmlConfigUpdates.NewValue)"/>
  </Target>
</Project>

Różnice są dość proste, wprowadziłem nową właściwość, TargetEnvName, która pozwala nam wiedzieć, jakie jest środowisko docelowe. (Uwaga: właśnie wymyśliłem tę nazwę nieruchomości, używaj dowolnej nazwy). Można również zobaczyć, że istnieją dwa elementy ItemGroup zawierające różne elementy xmlconfigupdate. Każda grupa ItemGroup ma warunek oparty na wartości TargetEnvName, więc tylko jedna z dwóch wartości ItemGroup będzie używana. Teraz mamy jeden plik MSBuild, który ma wartości dla obu środowisk. Podczas budowy wystarczy przejść w właściwości TargetEnvName, na przykład msbuild .\UpdateXml02.proj / p: TargetEnvName=Env02. Kiedy wykonałem ten plik wynikowy zawiera:

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

Widzisz że plik został zaktualizowany o różne ścieżki w elemencie value.

4 Użyj biblioteki zadań innych firm

Jeśli nie używasz MSBuild 4, musisz użyć biblioteki zadań innej firmy, takiej jak MSBuild Extension Pack(link w zasobach).

Mam nadzieję, że to pomoże.

Zasoby

 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/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-18 00:04:29

Zmieniamy wartości konfiguracyjne dla naszych różnych środowisk kompilacji (np. dev, staging, production) za pomocą przekształceń konfiguracyjnych. Zakładam, że Config transforms prawdopodobnie nie będzie działać dla ciebie, ale jeśli jest taka możliwość, sprawdź Ta odpowiedź która pokazuje, jak zastosować.Net Config transforms do dowolnego pliku XML.

Alternatywą byłoby użycie zadania budowania FileUpdate z projektu MSBuild Community Task . To zadanie pozwala na użycie wyrażeń regularnych, aby znaleźć i zastąp zawartość pliku. Oto przykład:

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

Ponieważ przekazywałbyś właściwości systemu TeamCity do FileUpdate jeśli zdecydujesz się skorzystać z drugiej opcji, spójrz na to pytanie , aby zobaczyć, jak można odwoływać się do Właściwości systemu w skrypcie 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/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:09:56