我试图在持续集成环境中使用configuration transformations。
我需要一种方法来告诉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>
我从输出中知道正在构建正确的配置。现在我只需要学习如何触发配置转换。是否有一些hocus pocus,我可以添加到构建中的最终.proj以启动转换并吹走各个转换文件?
答案 0 :(得分:11)
我找到了另一种方法来实现这一目标,而不是创建自定义活动。您只需要修改正在构建的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 Arguments”字段中。
当TFS进行构建时,这将强制TFS 2010在web.config上进行转换。
更多详情可在Kevin Daly's Blog找到。
答案 1 :(得分: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复制到您需要的任何位置。
答案 2 :(得分:6)
默认情况下,对于命令行和TFS版本,已禁用添加到Visual Studio 2010中的网站项目的web.config转换功能。
有两种相对简单的解决方案:
选项1 :编辑构建定义并将以下内容添加到“MSBuild Arguments”字段中:
/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 Project构建过程被连接起来的地方。在 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 targets” - 如果您使用特定名称“ProjectName.wpp.targets”命名另一个MSBuild文件,则可以控制整个网站发布过程。我们使用它来删除-vsdoc javascript文档文件,因为复制了已发布的网站输出:
<ItemGroup>
<ExcludeFromPackageFiles Include="Scripts\**\*-vsdoc.js;Resources\Scripts\**\-vsdoc.js">
<FromTarget>Project</FromTarget>
</ExcludeFromPackageFiles>
</ItemGroup>
一切都说,你可能最好完全从你的构建中删除生产web.configs。我们将转换直接放在我们的生产部署机器上,并在部署应用程序时使用powershell进行转换。
答案 3 :(得分:5)
这是一个更简单的答案。 :)
http://social.msdn.microsoft.com/Forums/en-US/tfsbuild/thread/d5c6cc7b-fbb1-4299-a8af-ef602bad8898/
从链接(如果它被移动/ 404 /等):
以下是我解决这个问题的方法。钥匙 是编辑* .csproj文件 网站并添加以下内容 AfterBuild目标(一定要移动 结束评论上面)。这是为了 我们的网站在团队中建立项目 Foundation Server。
<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等等...来自 发表时一定要设置“Build 动作“在属性窗口中 每个配置转换 文件为“无”。只有主力 web.config应该有一个“Build “内容”的行动
编辑csproj文件的一种简单方法 是加载“PowerCommands” 对于Visual Studio 2010“或 “生产力电动工具”扩展 从Visual Studio 2010可用 Visual Studio库。一旦 加载所有你需要做的是对的 单击解决方案中的项目 并选择“卸载项目”。那么你 可以再次右键单击并选择 “编辑...”编辑csproj文件XML 直。然后做得恰到好处 再次单击并选择“重新加载” 项目”。
答案 4 :(得分:1)
您需要做的就是设置应在TFS构建定义中使用哪种配置。
在我的情况下,我已经为CI设置了一个配置,然后执行正确的web.config转换。确保您已添加“CI”转换文件,您应该很高兴。
答案 5 :(得分:1)
要在WorkFlow中执行此操作,您必须创建自定义活动。关于它的文章非常好here
对于此特定活动,您需要创建和活动项目(将其从 .Net 4客户端配置文件更改为 .Net 4 )并参考 Microsoft.Build来自GAC的.Framework 和 Microsoft.Build.Utilities.v4.0 ,然后来自%programfiles%\的 Microsoft.Web.Publishing.Tasks msbuild \ Microsoft \ VisualStudio \ v10.0 \ WebApplications (%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);
}
}
}
BuildEngineStub 的原因是 TransformXml 类使用它来进行记录。
唯一需要注意的是 TransformXml.Execute 函数会锁定源配置文件,直到构建过程完成。