.net TFS 2010或msbuild中的触发器配置转换

.net TFS 2010或msbuild中的触发器配置转换,.net,tfs,msbuild,continuous-integration,config,.net,Tfs,Msbuild,Continuous Integration,Config,我试图在持续集成环境中使用 我需要一种方法来告诉TFS构建代理执行转换。我有点希望它在发现配置转换文件(web.qa-release.config、web.production-release.config等)后能够工作。但事实并非如此 我有一个TFS构建定义,用于构建正确的配置(qa版本、生产版本等),我有一些特定的.proj文件,这些文件在这些定义中构建,并且包含一些特定于环境的参数,例如: <PropertyGroup Condition=" '$(Configuration)'==

我试图在持续集成环境中使用

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

我有一个TFS构建定义,用于构建正确的配置(qa版本、生产版本等),我有一些特定的.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>

qa.web
...
生产网站
...

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

您需要做的就是设置TFS构建定义中应使用的配置

  • 转到团队资源管理器>构建
  • 编辑生成定义(或创建新的)
  • 在“流程”步骤下,有一个“要构建的配置”设置

  • 在我的例子中,我专门为CI设置了一个配置,然后执行正确的web.config转换。确保您已经添加了“CI”转换文件,您应该可以开始了。

    我终于成功地实现了这一点。我使用的是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复制到需要的任何位置。

    要在工作流中执行此操作,您必须创建自定义活动。这方面有一篇很好的文章

    对于此特定的活动,您需要创建活动项目(将其从.Net 4客户端配置文件更改为.Net 4)并从GAC中引用Microsoft.Build.Framework和Microsoft.Build.Utilities.v4.0,然后从%programfiles%\msbuild\Microsoft\VisualStudio\v10.0\WebApplications中引用Microsoft.Web.Publishing.Tasks

    完成后,将添加以下两个类:

    首先,有一个存根:

    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(); }
        }
    }
    
    然后是活动类it本身:

    [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);
            }
        }
    }
    
    [BuildActivity(HostenEnvironmentOption.Agent)]
    公共密封类WebConfigTransform:CodeActivity
    {
    私有常量字符串WEB_CONFIG=“WEB.CONFIG”;
    私有常量字符串WEB_CONFIG_TRANSFORM_FORMAT=“WEB.{0}.CONFIG”;
    私有IBuildEngine(u buildEngine{get{return new BuildEngineStub();}}
    [必需参数]
    公共InArgument转换名称{get;set;}
    [必需参数]
    公共InArgument源文件夹{get;set;}
    [必需参数]
    公共InArgument目标文件夹{get;set;}
    受保护的覆盖无效执行(CodeActivityContext上下文)
    {
    var transformationName=context.GetValue(this.transformationName);
    var sourceFolder=context.GetValue(this.sourceFolder);
    var destinationFolder=context.GetValue(this.destinationFolder);
    var source=Path.Combine(sourceFolder,WEB\u CONFIG);
    var destination=Path.Combine(destinationFolder,WEB_CONFIG);
    var destinationbackup=string.Format(“{0}.bak”,destination);
    var transform=Path.Combine(sourceFolder,string.Format(WEB\u CONFIG\u transform\u Format,transformationName));
    如果(!File.Exists(source))
    抛出新ArgumentException(“SourceFolder中不存在Web.config文件”);
    如果(!File.Exists(transform))
    抛出新ArgumentException(“SourceFolder中不存在Web.config转换”);
    if(File.Exists(destination))
    {
    文件副本(目的地、目的地备份);
    文件删除(目的地);
    }
    var transformation=new TransformXml();
    transformation.Source=新任务项(Source);
    transformation.Destination=新任务项(Destination);
    transformation.Transform=新任务项(Transform);
    transformation.BuildEngine=\u BuildEngine;
    if(transformation.Execute())
    {
    文件删除(destinationbackup);
    }
    其他的
    {
    文件副本(目标备份,目标);
    文件删除(destinationbackup);
    }
    }
    }
    
    之所以使用buildenginesub,是因为TransformXml类使用它来进行日志记录


    唯一需要注意的是,TransformXml.Execute函数会锁定源配置文件,直到构建过程完成。

    我找到了另一种方法来完成此操作,而不是创建自定义活动。您只需要修改正在构建的web应用程序的visual studio项目文件

    添加以下内容(可在项目文件末尾找到“AfterBuild”目标的占位符):

    
    
    然后,只需将
    /p:IsAutoBuild=“True”
    添加到生成定义的“高级”部分中的“MSBuild Arguments”字段中

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


    有关更多详细信息,请访问。

    这里有一个更简单的答案:

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

    下面是我如何解决这个问题的。钥匙 要在上编辑*.csproj文件 网站,并将以下内容添加到 后构建目标(确保移动 结束上面的评论)。这是给你的 我们的网站建设项目团队 基金会服务器
    <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等。。。从存在 请务必设置“Bu”
    <Target Name="AfterBuild">
        <TransformXml Condition="Exists('$(OutDir)\_PublishedWebsites\$(TargetName)')"
                      Source="Web.config"
                      Transform="$(ProjectConfigTransformFileName)"
                      Destination="$(OutDir)\_PublishedWebsites\$(TargetName)\Web.config" />
    </Target>
    
    /p:UseWPP_CopyWebApplication=true /p:PipelineDependsOnBuild=false
    
    <?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" />
    
    <Import Project="ProjectName.custom.targets"/>
    <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
    <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" />
    
    <ItemGroup>
      <ExcludeFromPackageFiles Include="Scripts\**\*-vsdoc.js;Resources\Scripts\**\-vsdoc.js">
        <FromTarget>Project</FromTarget>
      </ExcludeFromPackageFiles>
    </ItemGroup>