web-dev-qa-db-ja.com

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

これらの値をXMLファイルにプッシュするには、どのようなMsBuildマジックを使用できますか?全部で20個ほどのアイテムがあります。

40
Loofer

これについてブログに書いたところです( http://sedodream.com/2011/12/29/UpdatingXMLFilesWithMSBuild.aspx )が、ここにも情報を貼り付けます。

今日、私はStackOverflowに投稿された質問を見て、Team Cityから実行されたCIビルド中にMSBuildを使用してXMLファイルを更新する方法を尋ねました。

正解は1つではありません。ビルド中にXMLファイルを更新する方法はいくつかあります。最も注目すべき点:

  1. SlowCheetahを使用してファイルを変換します
  2. TransformXmlタスクを直接使用する
  3. 組み込み(MSBuild 4.0)XmlPokeタスクを使用する
  4. サードパーティのタスクライブラリを使用する

1 SlowCheetahを使用してファイルを変換します

この投稿を読み始める前に、オプション3についてまず説明します。これが最も簡単な方法であり、最も簡単に維持できると思うからです。 SlowCheetah XML Transforms Visual Studioアドインをダウンロードできます。プロジェクトに対してこれを実行すると、ビルド時にファイルを変換するための新しいメニューコマンドが表示されます(パッケージ/パブリッシュ上のWebプロジェクトの場合)。コマンドラインまたは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[key='HlrFtpPutDir']/value
  • /Provisioning.Lib.Processing.XmlConfig/item[key='HlrFtpPutCopyDir']/valueこの投稿の目的ではないため、正しいXPathを理解するために必要なことについては説明しません。インターウェブには、XPath関連のリソースがたくさんあります。リソースセクションでは、私が常に使用しているオンラインXPathテスターに​​リンクしています。

必要なXPath式を取得したので、MSBuild要素を作成してすべてを更新する必要があります。全体的なテクニックは次のとおりです。

  1. すべてのXML更新のすべての情報をアイテムに配置する
  2. 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's 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アイテムには、次の2つのメタデータ値があります。

  • XPath-更新される要素を選択するために必要なXPathが含まれています
  • NewValue-これには、更新される要素の新しい値が含まれます。UpdateXmlターゲットの内部では、XmlPokeタスクを使用し、XPathを%(XmlConfigUpdate.XPath)として渡し、値を%(XmlConfigUpdatesとして渡します。 .NewValue)。アイテムで%(…)構文を使用しているため、これによりMSBuildバッチ処理が開始されます。バッチ処理では、値の「バッチ」に対して複数の操作が実行されます。この場合、2つの一意のバッチ(XmlConfigUpdatesの各値に1つ)があるため、XmlPokeタスクが2回呼び出されます。バッチ処理は混乱を招く可能性があるため、慣れていない場合は、必ずバッチ処理を読んでください。

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

違いは非常に単純です。新しいプロパティTargetEnvNameを導入しました。これにより、ターゲット環境を知ることができます。 (注:私はそのプロパティ名を作成しました。好きな名前を使用してください)。また、異なるXmlConfigUpdate項目を含む2つのItemGroup要素があることがわかります。各ItemGroupにはTargetEnvNameの値に基づく条件があるため、2つのItemGroup値のうちの1つだけが使用されます。これで、両方の環境の値を持つ単一の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 Extension Pack(リソースのリンク)などのサードパーティタスクライブラリを使用する必要があります。

お役に立てば幸いです。

資源

72

構成変換を使用して、さまざまなビルド環境(開発、ステージング、本番など)の構成値を変更します。私はおそらくconfig変換が機能しないと思いますが、可能であれば、チェックアウトしてください この答え .Net config変換をXMLファイルに適用する方法を示しています。

別の方法は、 MSBuild Community Tasks プロジェクトのFileUpdateビルドタスクを使用することです。このタスクでは、正規表現を使用してファイル内のコンテンツを検索および置換できます。次に例を示します。

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

2番目のオプションを選択する場合は、TeamCityシステムプロパティをFileUpdateに渡すため、MSBuildスクリプトでシステムプロパティを参照する方法について、 この質問 を参照してください。

5