使用msbuild我想用teamcity中的值更新configuration文件
我有一些这样的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
我可以使用什么types的MsBuild魔法将这些值推送到我的XML文件中? 总共有20个左右的项目。
我只是在这个博客( http://sedodream.com/2011/12/29/UpdatingXMLFilesWithMSBuild.aspx ),但我也将粘贴在这里为您的信息。
今天我刚刚看到一个问题发布在StackOverflow上,询问如何在从Team City执行的CI构build过程中使用MSBuild更新XML文件。
没有正确的单一答案,您可以通过几种不同的方式在构build过程中更新XML文件。 最为显着地:
- 使用SlowCheetah为您转换文件
- 直接使用TransformXml任务
- 使用内置的(MSBuild 4.0)XmlPoke任务
- 使用第三方任务库
1使用SlowCheetah为您转换文件
在开始阅读这篇文章之前,先让我先select第三个选项,因为我认为这是最简单的方法,也是最容易维护的。 您可以下载我的SlowCheetah XML转换Visual Studio插件。一旦您为您的项目执行此操作,您将看到一个新的菜单命令来转换构build文件(用于包装/发布上的Web项目)。 如果从命令行或CI服务器构build,转换也应该运行。
2直接使用TransformXml任务
如果你想要一种技术,你有一个“主”XML文件,你想能够包含转换到一个单独的XML文件中的文件,那么你可以直接使用TransformXml任务。 欲了解更多信息,请参阅我以前的博客文章http://sedodream.com/2010/11/18/XDTWebconfigTransformsInNonwebProjects.aspx
3使用内置的XmlPoke任务
有时,为每个XML文件创build一个包含转换的XML文件是没有意义的。 例如,如果您有一个XML文件,并且您想要修改一个值,但要创build10个不同的文件,则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。 在这种情况下,我们可以为每个值元素使用以下XPathexpression式。
- /Provisioning.Lib.Processing.XmlConfig/item[key='HlrFtpPutDir']/value
- /Provisioning.Lib.Processing.XmlConfig/item[key='HlrFtpPutCopyDir']/value我不会仔细考虑你需要做什么来找出正确的XPath,因为这不是这篇文章的目的。 interwebs上有一堆与XPath相关的资源。 在资源部分,我已经链接到我一直使用的在线XPathtesting器。
现在我们已经获得了所需的XPathexpression式,我们需要构造我们的MSBuild元素来更新所有的东西。 这是整体技术:
- 将所有XML更新的所有信息放入一个项目中
- 使用XmlPoke和MSBuild批处理来执行所有更新
对于#2,如果你不熟悉MSBuild批处理,那么我会build议购买我的书,或者你可以看看我在网上有关批量的资源(链接在下面的资源部分)。 下面你会find我创build的一个简单的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属性的值在这里没有使用。 我将为每个正在更新的文件放置一个包含属性的唯一值。 这只是让人们更容易理解这组值是什么,您可以稍后使用它批量更新。 XmlConfigUpdates项目有这两个元数据值:
- XPath – 这包含select要更新的元素所需的XPath
- NewValue – 这包含要更新的元素的新值在UpdateXml目标中,您可以看到我们正在使用XmlPoke任务,并将XPath作为%(XmlConfigUpdate.XPath)并将值作为%(XmlConfigUpdates .NewValue)。 由于我们在一个项目上使用%(…)语法,所以开始MSBuild批处理。 批处理是在一批“批量”值上执行多个操作的地方。 在这种情况下,有两个唯一的批次(XmlConfigUpdates中的每个值为1),所以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任务是多么容易。 现在我们来看看如何扩展这个例子来pipe理对同一个文件的更新以获得额外的环境。
如何pipe理多个不同结果的同一个文件的更新
由于我们已经创build了一个能够保留所有需要的XPath以及新值的项目,所以我们在pipe理多个环境方面有了更多的灵活性。 在这种情况下,我们有相同的文件,我们要写出来,但我们需要根据目标环境写出不同的值。 这样做很简单。 看看下面的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,它让我们知道目标环境是什么。 (注意:我只是编写了这个属性名称,使用任何你喜欢的名字)。 你也可以看到有两个ItemGroup元素包含不同的XmlConfigUpdate项目。 每个ItemGroup都有一个基于TargetEnvName值的条件,所以只能使用两个ItemGroup值中的一个。 现在我们有一个MSBuild文件,它具有两个环境的值。 在构build传递属性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>
您可以看到文件已更新为值元素中的不同path。
4使用第三方任务库
如果您不使用MSBuild 4,则需要使用第三方任务库(如MSBuild扩展包(资源中的链接))。
希望有所帮助。
资源
- MSBuild批处理: http : //sedotech.com/Resources#Batching
- SlowCheetah – XML转换扩展名: http ://visualstudiogallery.msdn.microsoft.com/69023d00-a4f9-4a34-a6cd-7e854ba318b5
- MSBuild扩展包(有一个任务来更新XML文件): http : //msbuildextensionpack.codeplex.com/
- 在线XPathtesting器: http : //www.whitebeam.org/library/guide/TechNotes/xpathtestbed.rhtm
我们使用configuration转换来更改不同构build环境(例如,dev,staging,production)的configuration值。 我认为configuration转换可能不会为你工作,但如果这是一种可能性,看看这个答案 ,显示如何应用.Netconfiguration转换为任何XML文件。
另一种方法是使用MSBuild社区任务项目中的FileUpdate构build任务。 此任务允许您使用正则expression式来查找和replace文件中的内容。 这是一个例子:
<FileUpdate Files="version.txt" Regex="(\d+)\.(\d+)\.(\d+)\.(\d+)" ReplacementText="$1.$2.$3.123" />
因为如果您决定使用第二个选项,那么您会将TeamCity系统属性传递到FileUpdate,请查看此问题以了解如何在MSBuild脚本中引用系统属性。