我正在尝试为多个环境的代码部署创建构建脚本。代码如下:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<PropertyGroup>
<TargetEnv>Production</TargetEnv>
</PropertyGroup>
<ItemGroup Condition="'$(TargetEnv)' == 'Integration'">
<Server Include="int1">
<ip>172.0.0.1</ip>
</Server>
</ItemGroup>
<ItemGroup Condition="'$(TargetEnv)' == 'Production'">
<Server Include="prod1">
<ip>172.0.2.1</ip>
</Server>
<Server Include="prod2">
<ip>172.0.2.2</ip>
</Server>
</ItemGroup>
<Target Name="Deploy">
<CallTarget Targets="DeployIntegration" />
<CallTarget Targets="DeployServers" />
</Target>
<Target Name="DeployIntegration" Condition="'$(TargetEnv)' == 'Integration'" Outputs="%(Server.Identity)">
<Message Text="= specific int server thing need access to variable %(Server.Identity) =" Importance="high" />
</Target>
<Target Name="DeployServers" Condition="'$(TargetEnv)' != 'Integration'" Outputs="%(Server.Identity)">
<Message Text="= specific prod thing here need access to variable %(Server.Identity) =" Importance="high" />
</Target>
<Target Name="RemoveServerFromLoadBalancer" AfterTargets="DeployServers" Condition="'$(TargetEnv)' != 'Integration'">
<Message Text="= removing %(Server.Identity) from load balancer =" Importance="high" />
</Target>
<Target Name="IgnoreRemoveServerFromLoadBalancer" AfterTargets="DeployServers" Condition="'$(TargetEnv)' == 'Integration'">
<Message Text="= ignore removing %(Server.Identity) from load balancer =" Importance="high" />
</Target>
<Target Name="CopyFilesAndCreateFolderLinks" AfterTargets="RemoveServerFromLoadBalancer;IgnoreRemoveServerFromLoadBalancer">
<Message Text=" = creating and copying files %(Server.Identity) =" Importance="high" />
</Target>
<Target Name="SetWebFarmServerName" AfterTargets="UpdateWebConfig" Condition="'$(TargetEnv)' != 'Integration'">
<Message Text=" = app setting CMSWebFarmServerName set to %(Server.Identity) =" Importance="high" />
</Target>
<Target Name="DisableWebFarmForIntegration" AfterTargets="UpdateWebConfig" Condition="'$(TargetEnv)' == 'Integration'">
<Message Text=" = Disabled webfarm setting for Integration - %(Server.Identity) =" Importance="high" />
</Target>
<Target Name="AddBackToLoadBalancer" AfterTargets="DisableWebFarmForIntegration" Condition="'$(TargetEnv)' != 'Integration'">
<Message Text=" = Putting server %(Server.Identity) back on load balancer =" Importance="high" />
</Target>
</Project>
此代码位于xml(保存在11.0文件夹中)文件中,我使用msbuild命令运行它:
C:\Program Files (x86)\Microsoft Visual Studio 11.0>msbuild buildtest.xml /t:Deploy
当我运行生产的构建任务时,此代码返回此内容:
DeployServers:
= specific prod thing here need access to variable prod1 =
DeployServers:
= specific prod thing here need access to variable prod2 =
RemoveServerFromLoadBalancer:
= removing prod1 from load balancer =
= removing prod2 from load balancer =
CopyFilesAndCreateFolderLinks:
= creating and copying files prod1 =
= creating and copying files prod2 =
我基本上想确保,如果我正在进行集成,它不会运行特定目标,例如负载均衡器相关任务,因为只有一台机器。我在想,返回的值应如下所示:
DeployServers:
= specific prod thing here need access to variable prod1 =
RemoveServerFromLoadBalancer:
= removing prod1 from load balancer =
CopyFilesAndCreateFolderLinks:
= creating and copying files prod1 =
DeployServers:
= specific prod thing here need access to variable prod2 =
RemoveServerFromLoadBalancer:
= removing prod2 from load balancer =
CopyFilesAndCreateFolderLinks:
= creating and copying files prod2 =
对于这篇长篇文章感到抱歉,这个msbuild的东西有点棘手。感谢您的投入。
答案 0 :(得分:1)
批处理MSBuild在这里执行是正确的,请考虑一下:你问Message
文本为%(Server.Identity)
所以它会为它知道的服务器做多少,而且没有理由它会等待中间的目标。因此,为了得到你想要的东西,你必须让它每个服务器执行一次所有必需的任务。此外,您的一般结构有点过于复杂。目标上的条件是无法控制的:只是你重复相同条件x次的事实已经是一个错误的标志,因为它简单地说违反了DIY原则。如果你添加另一个TargetEnv呢?然后是另一个?是的你认为:不会很好:]第二个可能的未来陷阱是使用AfterTargets
:当你只有一对时很好,但过了一段时间你继续添加目标,你就不知道了订单是什么,你基本上必须通过整个文件来掌握正在发生的事情。另外,如果您为每个TargetEnv添加更多常用目标,该怎么办?或者,如果您添加另一个TargetEnv。再次不会很好,因为你必须在多个地方修复它。
现在,因为你把这两种并发症混合在一起,并且在它之上进行批处理,事情变得非常不清楚。回到开始并考虑你真正需要的东西:如果TargetEnv是A,你想要做X和Y和Z,如果TargetEnv是你想做的Q和Q和Z.就是这样。您可以将其视为两个单独的责任:根据条件选择某些内容,并维护每个条件的操作列表。所以让我们以msbuild的方式表达它。
这是条件部分,现在在新的Deploy目标中。其余目标将移动到另一个文件。 Deploy将在名为deploy.targets的另一个msbuild文件中调用与当前文件位于同一目录中的目标(其中一个取决于条件)。由于批处理现在处于更高级别,因此它将自动以您希望的方式执行:每个服务器一次。请注意,所选服务器作为属性传递给另一个文件。还有其他方法可以做到这一点,但就像代码一样,拥有几个较小的文件而不是一个大的do-it-all文件是很好的。
<Target Name="Deploy">
<PropertyGroup>
<TargetsFile>$(MsBuildThisFileDirectory)deploy.targets</TargetsFile>
<TargetToCall Condition="$(TargetEnv)=='Production'">DeployServers</TargetToCall>
<TargetToCall Condition="$(TargetEnv)=='Integration'">DeployIntegration</TargetToCall>
</PropertyGroup>
<MSBuild Projects="$(TargetsFile)" Targets="$(TargetToCall)" Properties="Server=%(Server.Identity)" />
</Target>
这是包含所有目标的新文件,以及两个“主”目标,它们现在准确指定了他们想要调用的其他目标,不再需要条件,不再需要AfterTargets。
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<PropertyGroup>
<CommonTargets>CopyFilesAndCreateFolderLinks</CommonTargets>
</PropertyGroup>
<Target Name="DeployIntegration">
<Message Text="= specific int server thing need access to variable $(Server) =" Importance="high" />
<CallTarget Targets="IgnoreRemoveServerFromLoadBalancer;$(CommonTargets)"/>
</Target>
<Target Name="DeployServers">
<Message Text="= specific prod thing here need access to variable $(Server) =" Importance="high" />
<CallTarget Targets="RemoveServerFromLoadBalancer;AnotherTargetJustForDeploy;$(CommonTargets)"/>
</Target>
<Target Name="RemoveServerFromLoadBalancer">
<Message Text="= removing $(Server) from load balancer =" Importance="high" />
</Target>
<Target Name="AnotherTargetJustForDeploy">
<Message Text="= AnotherTargetJustForDeploy for $(Server) =" Importance="high" />
</Target>
<Target Name="IgnoreRemoveServerFromLoadBalancer">
<Message Text="= ignore removing $(Server) from load balancer =" Importance="high" />
</Target>
<Target Name="CopyFilesAndCreateFolderLinks">
<Message Text=" = creating and copying files $(Server) =" Importance="high" />
</Target>
</Project>