2009-12-08 37 views
16

我试图在持续集成环境中使用configuration transformations在TFS 2010或msbuild中触发配置转换

我需要一种方法来告诉TFS构建代理执行转换。我很希望它能在发现配置转换文件(web.qa-release.config,web.production-release.config等)后才起作用。但事实并非如此。

我有一个TFS构建定义,可以构建正确的配置(qa-release,production-release等),并且我有一些特定的.proj文件在这些定义中构建,并包含一些特定于环境的参数例如:

<PropertyGroup Condition=" '$(Configuration)'=='production-release' "> 
    <TargetHost Condition=" '$(TargetHost)'=='' ">qa.web</TargetHost> 
    ... 
</PropertyGroup> 
<PropertyGroup Condition=" '$(Configuration)'=='qa-release' "> 
    <TargetHost Condition=" '$(TargetHost)'=='' ">production.web</TargetHost> 
    ... 
</PropertyGroup> 

我从输出中知道正在构建正确的配置。现在我只需要学习如何触发配置转换。是否有一些可以添加到构建中的最终.proj中的hocus pocus,以启动变换并吹走各个变换文件?

回答

1

您应该只需要设置哪些配置应该在TFS构建定义中使用。

  1. 转到团队资源管理器>构建
  2. 编辑您的构建定义(或新建)
  3. 在“过程”的步骤有对“配置,以打造”设置。

在我的情况下,我已经为CI设置了专门的配置,然后执行正确的web.config转换。确保你已经添加了“CI”转换文件,你应该很好。

+0

另请注意,web.config转换仅在部署过程中调用,而不是在标准构建过程中调用。 – 2010-05-13 13:44:41

+4

我有一个发布配置(AnyCpu | Release),但构建中的Web.config不会更改。 – 2011-01-08 00:23:29

+1

这是错误的,那些“配置”是构建配置,而不是配置文件。 – Alex 2012-02-06 09:17:46

6

我终于设法得到这个工作。我使用TFS 2008,但也使用MSBuild 4.0,所以它应该为你工作。

首先,这个进口添加到TFSBuild.proj:

<Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" /> 

接下来,添加一个BeforeDropBuild目标:

<Target Name="BeforeDropBuild"> 
    <TransformXml Source="$(SolutionRoot)\MySite\Web.config" 
    Transform="$(SolutionRoot)\MySite\Web.QA.config" 
    Destination="$(OutDir)\_PublishedWebsites\MySite\Web.QA.config.transformed" /> 
</Target> 

然后,您可以复制Web.QA.config.transformed无论你需要它去。

+0

TFSBuild.proj没有人在TFS 2010中存在这个问题。他们使用新的基于工作流的构建系统。我还没有想出如何transofrm web.config,因为我需要做到这一点... – 2010-05-17 23:40:49

+1

我们刚刚升级到TFS 2010.进行升级的顾问设置了某种向后兼容的工作流程,使用我们现有的TFSBuild。 proj文件。当然我们会后悔的,但现在我们仍然在为Team Build 2010工作...... – 2010-09-16 20:43:50

+3

实际上,作为升级过程的一部分,自动安装了向后兼容的东西。 MS在这种情况下将其留在原地。 – NotMe 2011-01-04 18:40:45

1

要在WorkFlow中执行此操作,您必须创建一个自定义活动。有一个关于它here

相当不错的文章,你需要创建和活动项目这一特定活动(从.NET 4的客户端配置文件改变.NET 4中),并引用Microsoft.Build.FrameworkMicrosoft.Build.Utilities.v4.0从GAC然后Microsoft.Web.Publishing.Tasks%PROGRAMFILES%\的MSBuild \微软\ VisualStudio的\ V10.0 \ web应用(%PROGRAMFILES(X86)如果你在一个64位系统上)。

如果这样做了,你加入这两个类:

首先,有一个存根:

internal class BuildEngineStub : IBuildEngine 
{ 
    public bool BuildProjectFile(string projectFileName, string[] targetNames, System.Collections.IDictionary globalProperties, System.Collections.IDictionary targetOutputs) 
    { 
     throw new NotImplementedException(); 
    } 

    public int ColumnNumberOfTaskNode 
    { 
     get { throw new NotImplementedException(); } 
    } 

    public bool ContinueOnError 
    { 
     get { throw new NotImplementedException(); } 
    } 

    public int LineNumberOfTaskNode 
    { 
     get { throw new NotImplementedException(); } 
    } 

    public void LogCustomEvent(CustomBuildEventArgs e) 
    { 
    } 

    public void LogErrorEvent(BuildErrorEventArgs e) 
    { 
    } 

    public void LogMessageEvent(BuildMessageEventArgs e) 
    { 
    } 

    public void LogWarningEvent(BuildWarningEventArgs e) 
    { 
    } 

    public string ProjectFileOfTaskNode 
    { 
     get { throw new NotImplementedException(); } 
    } 
} 

然后那里有活动类是自我:

[BuildActivity(HostEnvironmentOption.Agent)] 
public sealed class WebConfigTransform : CodeActivity 
{ 
    private const string WEB_CONFIG = "Web.config"; 
    private const string WEB_CONFIG_TRANSFORM_FORMAT = "Web.{0}.config"; 

    private IBuildEngine _buildEngine { get { return new BuildEngineStub(); } } 

    [RequiredArgument] 
    public InArgument<string> TransformationName { get; set; } 
    [RequiredArgument] 
    public InArgument<string> SourceFolder { get; set; } 
    [RequiredArgument] 
    public InArgument<string> DestinationFolder { get; set; } 

    protected override void Execute(CodeActivityContext context) 
    { 
     var transformationName = context.GetValue(this.TransformationName); 
     var sourceFolder = context.GetValue(this.SourceFolder); 
     var destinationFolder = context.GetValue(this.DestinationFolder); 

     var source = Path.Combine(sourceFolder, WEB_CONFIG); 
     var destination = Path.Combine(destinationFolder, WEB_CONFIG); 
     var destinationbackup = string.Format("{0}.bak", destination); 
     var transform = Path.Combine(sourceFolder, string.Format(WEB_CONFIG_TRANSFORM_FORMAT, transformationName)); 

     if(!File.Exists(source)) 
      throw new ArgumentException("Web.config file doesn't exist in SourceFolder"); 
     if (!File.Exists(transform)) 
      throw new ArgumentException("Web.config transformation doesn't exist in SourceFolder"); 
     if (File.Exists(destination)) 
     { 
      File.Copy(destination, destinationbackup); 
      File.Delete(destination); 
     } 

     var transformation = new TransformXml(); 
     transformation.Source = new TaskItem(source); 
     transformation.Destination = new TaskItem(destination); 
     transformation.Transform = new TaskItem(transform); 
     transformation.BuildEngine = _buildEngine; 

     if (transformation.Execute()) 
     { 
      File.Delete(destinationbackup); 
     } 
     else 
     { 
      File.Copy(destinationbackup, destination); 
      File.Delete(destinationbackup); 
     } 
    } 
} 

的原因BuildEngineStubTransformXml类使用它来做日志记录。

你需要小心的唯一的事情是,TransformXml.Execute功能锁定源配置文件,直到生成过程完成。

+1

这太复杂了,无法完成已经构建的内容..您可能想要查看接受的答案 – NotMe 2011-01-04 18:41:24

10

我发现了另一种方法来完成此操作,而不是创建自定义活动。您只需修改正在构建的Web应用程序的Visual Studio项目文件。

添加以下(一个为“AfterBuild”目标占位符都可以对项目文件的末尾找到):

<Target Name="AfterBuild" Condition="$(IsAutoBuild)=='True'"> 
    <ItemGroup> 
     <DeleteAfterBuild Include="$(WebProjectOutputDir)\Web.*.config" /> 
    </ItemGroup> 
    <TransformXml Source="Web.config" Transform="$(ProjectConfigTransformFileName)" Destination="$(WebProjectOutputDir)\Web.config"/> 
    <Delete Files="@(DeleteAfterBuild)" /> 
</Target> 

然后你只需要添加/p:IsAutoBuild="True"对发现的“的MSBuild参数”字段在构建定义的“高级”部分。

当TFS执行构建时,这将迫使TFS 2010在web.config上进行转换。

更多详细资料可在Kevin Daly's Blog找到。

5

以下是对您更简单的答案。 :)

http://social.msdn.microsoft.com/Forums/en-US/tfsbuild/thread/d5c6cc7b-fbb1-4299-a8af-ef602bad8898/

从链接(如果它被移动/ 404 /等):

这是我如何解决这一点。关键 是编辑 网站上的* .csproj的文件,并添加以下到 AfterBuild目标(一定要移动它上面的 结束注释)。这是 我们的网站建立项目团队 基金会服务器。

<Target Name="AfterBuild"> 
    <TransformXml Condition="Exists('$(OutDir)\_PublishedWebsites\$(TargetName)')" 
        Source="Web.config" 
        Transform="$(ProjectConfigTransformFileName)" 
        Destination="$(OutDir)\_PublishedWebsites\$(TargetName)\Web.config" /> 
</Target> 

为了保持web.debug.config, web.release.config等..被 公布一定要设置“构建 行动”,在属性窗口 每个配置转换 文件为“无”。只有主 web.config中应该有一个“构建 行动”,“内容”的

一个简单的方法来编辑的csproj文件 是为加载了“PowerCommands 为Visual Studio 2010”或 “生产力电源工具“扩展 到Visual Studio 2010可从 Visual Studio库。一旦加载了 所有你需要做的就是对 点击解决方案中的项目 并选择“卸载项目”。然后你可以再次右击并选择 “编辑...”直接编辑csproj文件XML 。然后,当完成正确的 再次点击并选择“重新加载 项目”。

+0

除了TFS之外,这还具有与其他构建引擎一起工作的额外优势 - 例如,我正在使用CC.NET与上切。 – 2012-03-05 23:05:45

6

对于命令行和TFS构建,添加到Visual Studio 2010网站项目中的web.config转换功能默认是禁用的。

有两个相对简单的解决方案:

选项1:编辑构建定义,并添加以下的“的MSBuild参数”字段:

/p:UseWPP_CopyWebApplication=true /p:PipelineDependsOnBuild=false 

UseWPP_CopyWebApplication将导致新的Web发布管道(WPP)将被激活用于构建。 WPP执行web.config转换,也可用于阻止诸如.PDB文件之类的内容被复制到bin文件夹。

选项2:MSBuild和WPP都是完全可扩展的。在与项目相同的目录中创建一个新的XML文件,并使用“.targets”扩展名 - 例如ProjectName.custom.targets。将以下MSBuild代码放入目标文件中:

<?xml version="1.0" encoding="utf-8"?> 
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> 
    <PropertyGroup> 
    <UseWPP_CopyWebApplication>True</UseWPP_CopyWebApplication> 
    <PipelineDependsOnBuild>False</PipelineDependsOnBuild> 
    </PropertyGroup> 
</Project> 

右键单击您的网站并选择“卸载项目”。右键单击卸载的项目并选择编辑。滚动到项目文件的底部并查找以下行:

<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> 
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" /> 

这些行是C#和Web项目构建过程连接起来的地方。插入一个进口到您的自定义生成扩展(目标文件)的进口CSHARP立即

<Import Project="ProjectName.custom.targets"/> 
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> 
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" /> 

这就是它 - 你是好去。 MSBuild定制方法需要更多的工作来设置,但好处是您可以使用新的目标文件“挂钩”到构建过程中,并更好地控制在服务器上的构建方式。例如,你可以挂钩任务来执行CSS和JS压缩。

我还建议看看“wpp目标” - 如果你用特定名称“ProjectName.wpp.targets”命名另一个MSBuild文件,你可以控制整个网站的发布过程。我们用它来去除的JavaScript文档文件的发布网站输出复制-vsdoc:

<ItemGroup> 
    <ExcludeFromPackageFiles Include="Scripts\**\*-vsdoc.js;Resources\Scripts\**\-vsdoc.js"> 
    <FromTarget>Project</FromTarget> 
    </ExcludeFromPackageFiles> 
</ItemGroup> 

说的一切,你可能会更好过离开从你的构建完全生产web.configs。我们将转换直接放置到生产部署机器上,并在部署应用程序时使用powershell进行转换。