2

我正在尝试为我们的代码部署创建一个构建脚本到多个环境。代码如下:

<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 的东西有点棘手。感谢您的意见。

4

1 回答 1

1

MSBuild 在此处执行的批处理是正确的,请考虑一下:您要求它Message提供文本%(Server.Identity),因此它将为它所知道的尽可能多的服务器执行此操作,并且没有理由在两者之间等待其他目标。所以要得到你想要的,你必须让它在每台服务器上执行一次所有必需的任务。此外,您的一般结构有点太复杂了。目标上的条件是无法控制的:只是你重复相同条件 x 次这一事实已经表明有问题,简单地说,它违反了 DIY 原则。另外,如果您添加另一个 TargetEnv 会怎样?然后另一个?是的,你想到了:不会很好:] 未来的第二个可能的陷阱是使用AfterTargets: 只有一对很好,但是一段时间后你不断添加目标并且你不知道顺序是什么,你基本上必须浏览整个文件才能掌握发生了什么。此外,如果您为每个 TargetEnv 添加更多通用目标会怎样?或者,如果您添加另一个 TargetEnv。同样不会很好,因为您必须在多个地方修复它。

现在,因为您混合了这两种复杂性并在其上添加了批处理,所以事情变得非常不清楚。回到起点,想想你真正需要什么:如果 TargetEnv 是 A,你想做 X、Y 和 Z,如果 TargetEnv 是 B,你想做 Q 和 Z。就是这样。您可以将其视为两个单独的职责:根据条件选择某些内容,以及维护每个条件的操作列表。因此,让我们以 msbuild 的方式表达这一点。

这是条件部分,现在位于新的 Deploy 目标中。其余的目标被移动到另一个文件。Deploy 将调用另一个名为 deploy.targets 的 msbuild 文件中的目标(取决于条件),该文件与当前文件位于同一目录中。因为批处理现在处于更高级别,它将自动按照您想要的方式执行:每台服务器一次。请注意,所选服务器作为属性传递给另一个文件。还有其他方法可以做到这一点,但就像使用代码一样,最好有几个较小的文件而不是一个大文件。

<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>
于 2013-12-08T19:54:25.173 回答