【问题标题】:TFS Build not transforming web.config as expectedTFS Build 未按预期转换 web.config
【发布时间】:2011-06-12 16:13:42
【问题描述】:

目标是让 TFS 构建和部署 2 种以上不同的配置,并让 web.config 转换文件在其输出中包含预期的内容。这在 ASP.NET MVC 项目中。

Web.Debug.Config - see on PasteBin.
Web.Release.Config - see on PasteBin

这 2 个转换后的配置文件的 Build Action 设置为 None。这已被修改,因为所有 3 个 web.*.config 文件都包含在部署中。

正确配置 TFS 以构建和部署这两种配置。它按预期部署到 2 个放置位置。构建定义中没有指定 MSBuild 参数。

问题:构建和部署的 2 个网站具有相同的 web.config 文件。基本上就好像转换后的文件不存在一样。

预期:指定的更改(xdt:Transform="Replace"xdt:Transform="Remove")将出现在 web.config 文件中。

如何配置项目或 TFS 以确保处理 web.config 转换并将其输出部署到正确的部署位置?我还能检查/修改什么?

  • 已确认转换良好 -- Vishal's Joshit's tutorial with the MSBuild on the command line 输出正确的转换!
  • 没有对 .csproj 进行任何后期构建或部署的修改。
  • 是否有任何xdt 属性被滥用或丢失?
  • 构建定义中没有指定 MSBuild 参数。
  • 是否正确设置了 web.config 构建操作?
  • 我们没有使用 Web 部署包或任何东西。只是希望在以后将这些输出 xcopy 到它们的各个网络服务器位置。

如果我遗漏了任何重要信息,请发表评论,我会提供更多相关信息!

【问题讨论】:

    标签: visual-studio-2010 msbuild web-config tfsbuild


    【解决方案1】:

    以前我一直在做与其他答案类似的事情。但是,我刚刚发现似乎是解决此问题的更好方法。只需将“/p:UseWPP_CopyWebApplication=true /p:PipelineDependsOnBuild=false”添加到您的 MSBuild 参数中。我刚刚在我的一个 TFS 版本上进行了尝试,它工作得很好。

    我在这里找到了这个很棒的提示:http://www.andygeldman.com/index.php/2011/10/web-and-app-config-transformations-with-tfs-build

    【讨论】:

    • 更简单的解决方案!即使在设置 TFS 构建以构建多个配置时也能完美运行(调试和发布都转换得很好)
    • 正是我需要的!感谢您的提示。
    【解决方案2】:

    Drop 实际上并没有进行任何转换。您需要将 /p:DeployOnBuild=True 添加到 MSBuild 参数中。

    这将创建一个包,然后可用于通过命令行或使用 IIS 导入应用程序向导安装网站。

    如果您追求的是直接发布多个配置,那就是另一回事了,这就是我偶然发现这篇文章的原因。

    【讨论】:

      【解决方案3】:

      TFS Team Build 2010 不会自动转换您的 Web.configs。您需要在构建过程模板中添加自定义工作流活动来完成此操作。

      Edwald Hofman 有一篇很好的博客解释了如何修改 TFS 2010 构建过程模板,所以我不会在这里深入讨论。

      http://www.ewaldhofman.nl/post/2010/04/29/Customize-Team-Build-2010-e28093-Part-4-Create-your-own-activity.aspx

      在您弄清楚如何将自定义活动添加到您的构建过程模板后,将以下活动添加到您的工作流程中,我在“将文件拖放到放置位置”之后添加了该活动。它利用 Microsoft.Web.Publishing.Tasks 程序集(位于:C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v10.0\Web)来执行转换:

      /// <summary>
      /// Transforms configuration files using TransformXml
      /// </summary>
      [BuildActivity(HostEnvironmentOption.All)]
      public sealed class WebConfigTransform : CodeActivity
      {
          #region Public Properties
      
          /// <summary>
          /// The binaries folder
          /// </summary>
          [RequiredArgument]
          public InArgument<string> BinariesLocation { get; set; }
      
          #endregion
      
          #region Overrides of CodeActivity
      
          /// <summary>
          /// When implemented in a derived class, performs the execution of the activity.
          /// </summary>
          /// <param name="context">The execution context under which the activity executes.</param>
          protected override void Execute(CodeActivityContext context)
          {
              var binariesFolder = context.GetValue(BinariesLocation);
      
              foreach (var sourceFolder in Directory.GetDirectories(Path.Combine(binariesFolder, "_PublishedWebsites")))
              {
                  var sourceFile = Path.Combine(sourceFolder, "Web.config");
                  if (File.Exists(sourceFile))
                  {
                      var filesToTransform = Directory.GetFiles(sourceFolder, "Web.*.config");
      
      
                      foreach (var fileToTransform in filesToTransform)
                      {
      
                          var tempSourceFile = Path.GetTempFileName();
                          var tempTransformFile = Path.GetTempFileName();
      
                          File.Copy(sourceFile, tempSourceFile, true);
                          File.Copy(fileToTransform, tempTransformFile, true);
      
                          var transformation = new TransformXml
                          {
                              BuildEngine = new BuildEngineStub(),
                              Source = tempSourceFile,
                              Transform = tempTransformFile,
                              Destination = fileToTransform
                          };
      
                          transformation.Execute();
                      }
                  }
              }
          }
      
          #endregion
      }
      

      您需要将工作流程中的放置位置传递给它。当您将其添加到工作流时,右键单击活动,然后转到属性,并将“DropLocation”(VB 表达式)粘贴到属性“BinaryLocation”中

      注意:您需要创建一个实现 IBuildEngine 接口的 BuildEngineStub 类才能使用 MSBuild 任务。这是我用的

      public class BuildEngineStub : IBuildEngine
      {
              #region IBuildEngine Members
      
              public bool BuildProjectFile(string projectFileName, string[] targetNames,
                                           IDictionary globalProperties,
                                           IDictionary targetOutputs)
              {
                  throw new NotImplementedException();
              }
      
              public int ColumnNumberOfTaskNode
              {
                  get { return 0; }
              }
      
              public bool ContinueOnError
              {
                  get { return false; }
              }
      
              public int LineNumberOfTaskNode
              {
                  get { return 0; }
              }
      
              public string ProjectFileOfTaskNode
              {
                  get { return ""; }
              }
      
              public void LogCustomEvent(CustomBuildEventArgs e)
              {
                  Console.WriteLine("Custom: {0}", e.Message);
              }
      
              public void LogErrorEvent(BuildErrorEventArgs e)
              {
                  Console.WriteLine("Error: {0}", e.Message);
              }
      
              public void LogMessageEvent(BuildMessageEventArgs e)
              {
                  Console.WriteLine("Message: {0}", e.Message);
              }
      
              public void LogWarningEvent(BuildWarningEventArgs e)
              {
                  Console.WriteLine("Warning: {0}", e.Message);
              }
      
              #endregion
          }
      

      【讨论】:

      • 其他选项要好得多,而且设置起来也容易得多
      • 不同意。我宁愿对我的构建过程模板进行一次更改(供所有解决方案使用),也不愿编辑我创建的每个 .proj 文件或构建定义。
      • Hrm,好点,从没想过...诚然,我是通过 TFS 和构建模板构建的新手,但这一切似乎都非常令人沮丧。似乎我们不得不竭尽全力开发/维护脚本来实现 msbuild 中已经存在的功能。
      • 当我们第一次从 TFS 2008 切换到 2010 时,我太沮丧了。但是一旦你掌握了修改构建过程模板的方法,我想你会喜欢这种增强的。
      • 我正在尝试实现它,但我没有在上下文中获得 WriteBuildMessage 方法,这是我缺少的某个地方的扩展方法吗?
      【解决方案4】:

      尽量不要设置Build Platform——基本删除ItemToBuild中的“Any CPU”,选择MSBuild platform为“Auto”

      【讨论】:

        【解决方案5】:

        这是我一直在使用的。当前的 TransformXml 任务有一个错误,它使文件保持打开状态。阅读更多here

        您可以调用此任务并为您正在使用的每个配置进行部署。

        <Target Name="TransformWebConfig">
        
            <PropertyGroup>
                <_tempSourceFile>$([System.IO.Path]::GetTempFileName())</_tempSourceFile>
                <_tempTransformFile>$([System.IO.Path]::GetTempFileName())</_tempTransformFile>
            </PropertyGroup>
        
            <Copy SourceFiles="$(_websiteDirectory)\Web.config" DestinationFiles="$(_tempSourceFile)"/>
            <Copy SourceFiles="$(_websiteDirectory)\Web.$(_transformConfiguration).config" DestinationFiles="$(_tempTransformFile)"/>
        
            <MSBuild.Community.Tasks.Attrib Files="$(_websiteDirectory)\Web.config" ReadOnly="false" />
        
            <TransformXml Source="$(_tempSourceFile)"
                          Transform="$(_tempTransformFile)"
                          Destination="$(_websiteDirectory)\Web.config"
                          StackTrace="false" />
        </Target>
        

        【讨论】:

        • 感谢您。在这个阶段我有点困惑;这个 XML 的 sn-p 放在哪里?对 TFS 构建定义的任何其他修改以适应?
        • 您对 MSBuild 的熟悉程度如何?您可以将此目标放在您的构建定义中,并可能从“BeforeDropBuild”目标中调用它。
        • 是的。虽然我认为当我发布我的答案时,情况并非如此。谁知道呢,我会重新发布 TFS 2010 的正确答案。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-04-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-07-23
        • 2016-11-28
        • 2021-09-19
        相关资源
        最近更新 更多