使用 msdeploy 转换文件

Posted

技术标签:

【中文标题】使用 msdeploy 转换文件【英文标题】:Transforming files with msdeploy 【发布时间】:2011-06-12 14:49:48 【问题描述】:

我可以使用 MSDeploy 的配置转换机制来转换其他文件吗?

【问题讨论】:

【参考方案1】:

只需添加到此 awnser,为了修改使用 msdeploy (webdeploy) 发布的应用程序中的 web.config 以外的其他文件,您可以在项目根目录的 parameters.xml 文件中设置 scope 属性:

<parameters>
  <parameter name="MyAppSetting" defaultvalue="_defaultValue_">
    <parameterentry match="/configuration/appSettings/add[@key='MyAppSetting']/@value" scope=".exe.config$" kind="XmlFile">
    </parameterentry>
  </parameter>
</parameters>

scope 是一个正则表达式,用于查找要应用match xpath 的文件。我没有对此进行过广泛的试验,但据我所知,它只是将 xpath 匹配的内容替换为稍后提供的值。

还有其他值可用于kind,它们的行为与 xpath 不同,有关详细信息,请参阅https://technet.microsoft.com/en-us/library/dd569084(v=ws.10).aspx

注意:这适用于您使用 parameters.xml 时,而不适用于使用 web.config.Debug/Release 文件时

【讨论】:

【参考方案2】:

泰勒的回答对我不起作用,他没有提供更多细节。因此,我深入研究 Microsoft.Web.Publishing.targets 文件以寻找解决方案。可以将以下 MSBuild Target 添加到项目文件中,以转换根应用程序目录中的所有其他配置文件。享受:)

<Target Name="TransformOtherConfigs" AfterTargets="CollectWebConfigsToTransform">
<ItemGroup>
    <WebConfigsToTransform Include="@(FilesForPackagingFromProject)"
                           Condition="'%(FilesForPackagingFromProject.Extension)'=='.config'"
                           Exclude="*.$(Configuration).config;$(ProjectConfigFileName)">
    <TransformFile>%(RelativeDir)%(Filename).$(Configuration).config</TransformFile>
    <TransformOriginalFile>$(TransformWebConfigIntermediateLocation)\original\%(DestinationRelativePath)</TransformOriginalFile>
    <TransformOutputFile>$(TransformWebConfigIntermediateLocation)\transformed\%(DestinationRelativePath)</TransformOutputFile>
    <TransformScope>$([System.IO.Path]::GetFullPath($(_PackageTempDir)\%(DestinationRelativePath)))</TransformScope>
  </WebConfigsToTransform>
  <WebConfigsToTransformOuputs Include="@(WebConfigsToTransform->'%(TransformOutputFile)')" />
</ItemGroup>
</Target>

【讨论】:

【参考方案3】:

(另一种方法)

msdeploy 打包是在您的项目的 MSbuild 运行期间调用的。

TransformXml 是 .csproj 或 .vsproj 构建中包含的任务。

只需修改您的构建过程以在您需要的任何文件上调用该任务。

例如,我们所做的是编写一个自定义目标

<Target Name="TransformFile">

    <TransformXml Source="$(DestinationPath)\$(Sourcefile)" 
       Transform="$(DestinationPath)\$(TransformFile)" 
       Destination="$(DestinationPath)\$(DestFile)" />
    </Target>

然后修改您的 .csproj 以在调用 Publish 任务之前运行它。

<CallTarget Targets="TransformFile" 
   Condition="'$(CustomTransforms)'=='true'" />

【讨论】:

您能举例说明如何设置 DestinationPath、Sourcefile、TransformFile 和 DestFile?我似乎找不到写入 MSBuild 属性来动态设置这些属性。 如果你只是将目标名称设置为“AfterBuild”,那么它就会运行,并且不需要“CallTarget”(至少在某些 VS2012 项目中)。【参考方案4】:

简答:可以。但这很“困难”。

长答案: 当我们将站点部署到目的地时,我们有通常的 web.test.config 和 web.prod.config。在我们引入 log4net.test.config 和 log4net.prod.config 之前,这一直很好。 MSBuild 不会自动完成并替换所有这些。它只会做 web.config 那些。

如果您想了解细节,请转到最后一个代码 sn-p。它显示了获取一个配置并用替换替换它的功能。但是……如果我描述整个过程会更有意义。

过程:

    Msbuild 制作网站的 zip 文件包。 我们编写了一个自定义的 .net 应用程序,它将获取该 zip 文件并对每个文件进行配置替换。重新保存 zip 文件。 执行 msdeploy 命令部署打包文件。

MSbuild 不会自动替换所有额外的配置。有趣的是 MSBuild 将删除任何“额外”配置。因此,您的 log4net.test.config 将在构建后消失。因此,您要做的第一件事就是告诉 msdbuild 保留这些额外文件。

您必须修改 vbProj 文件以包含新设置:

<AutoParameterizationWebConfigConnectionStrings>False</AutoParameterizationWebConfigConnectionStrings>

在您喜欢的文本编辑器中打开您的 web 应用程序的 vbProj 文件。导航到您希望它也应用的每个部署配置(发布、生产、调试等)并将该配置添加到其中。这是我们的“发布”配置的示例。

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  ...
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <DebugType>pdbonly</DebugType>
    <DefineDebug>false</DefineDebug>
    <DefineTrace>true</DefineTrace>
    <Optimize>true</Optimize>
    <OutputPath>bin\</OutputPath>
    <DocumentationFile>Documentation.xml</DocumentationFile>
    <NoWarn>42016,41999,42017,42018,42019,42032,42036,42020,42021,42022,42353,42354,42355</NoWarn>
    <CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
    <DeployIisAppPath>IISAppPath</DeployIisAppPath>
    <AutoParameterizationWebConfigConnectionStrings>False</AutoParameterizationWebConfigConnectionStrings>
  </PropertyGroup>
  ...
</Project>

所以现在 msbduild 将构建项目并将这些额外文件保留在适当的位置而不进行替换。现在您必须手动执行它们。

我们编写了一个 .net 应用程序来监视这些新的 zip 文件。我编写了一些代码,它将遍历整个 zip 包并找到与 configname.env.config 匹配的任何配置。它将提取它们,替换它们,然后将它们放回去。为了进行实际替换,我们使用 MSDeploy 使用的相同 DLL。我也使用 Ionic.Zip 来做 zip 的东西。

所以添加参考:

Microsoft.Build.dll
Microsoft.Build.Engine.dll
Microsoft.Web.Publishing.Tasks (possibly, not sure if you need this or not)

进口:

Imports System.IO
Imports System.Text.RegularExpressions
Imports Microsoft.Build.BuildEngine
Imports Microsoft.Build 

这是通过 zip 文件旋转的代码

specificpackage = "mypackagedsite.zip"
configenvironment = "DEV" 'stupid i had to pass this in, but it's the environment in web.dev.config

Directory.CreateDirectory(tempdir)

Dim fi As New FileInfo(specificpackage)

'copy zip file to temp dir   
Dim tempzip As String = tempdir & fi.Name

File.Copy(specificpackage, tempzip)

''extract configs to merge from file into temp dir
'regex for the web.config 
'regex for the web.env.config
'(?<site>\w+)\.(?<env>\w+)\.config$

Dim strMainConfigRegex As String = "/(?<configtype>\w+)\.config$"
Dim strsubconfigregex As String = "(?<site>\w+)\.(?<env>\w+)\.config$"
Dim strsubconfigregex2 As String = "(?<site>\w+)\.(?<env>\w+)\.config2$"

Dim MainConfigRegex As New Regex(strMainConfigRegex, RegexOptions.Compiled Or RegexOptions.IgnoreCase)
Dim SubConfigRegex As New Regex(strsubconfigregex, RegexOptions.Compiled Or RegexOptions.IgnoreCase)
Dim SubConfigRegex2 As New Regex(strsubconfigregex2, RegexOptions.Compiled Or RegexOptions.IgnoreCase)

Dim filetoadd As New Dictionary(Of String, String)
Dim filestoremove As New List(Of ZipEntry)
Using zip As ZipFile = ZipFile.Read(tempzip)
    For Each entry As ZipEntry In From a In zip.Entries Where a.IsDirectory = False
        For Each myMatch As Match In MainConfigRegex.Matches(entry.FileName)
            If myMatch.Success Then
                'found main config. 
                're-loop through, find any that are in the same dir as this, and also match the config name
                Dim currentdir As String = Path.GetDirectoryName(entry.FileName)
                Dim conifgmatchname As String = myMatch.Groups.Item("configtype").Value

                For Each subentry In From b In zip.Entries Where b.IsDirectory = False _
                                     And UCase(Path.GetDirectoryName(b.FileName)) = UCase(currentdir) _
                                     And (UCase(Path.GetFileName(b.FileName)) = UCase(conifgmatchname & "." & configenvironment & ".config") Or
                                          UCase(Path.GetFileName(b.FileName)) = UCase(conifgmatchname & "." & configenvironment & ".config2"))

                    entry.Extract(tempdir)
                    subentry.Extract(tempdir)

                    'Go ahead and do the transormation on these configs
                    Dim newtransform As New doTransform
                    newtransform.tempdir = tempdir
                    newtransform.filename = entry.FileName
                    newtransform.subfilename = subentry.FileName
                    Dim t1 As New Threading.Tasks.Task(AddressOf newtransform.doTransform)
                    t1.Start()
                    t1.Wait()
                    GC.Collect()
                    'sleep here because the build engine takes a while. 
                    Threading.Thread.Sleep(2000)
                    GC.Collect()

                    File.Delete(tempdir & entry.FileName)
                    File.Move(tempdir & Path.GetDirectoryName(entry.FileName) & "/transformed.config", tempdir & entry.FileName)
                    'put them back into the zip file
                    filetoadd.Add(tempdir & entry.FileName, Path.GetDirectoryName(entry.FileName))
                    filestoremove.Add(entry)
                Next
            End If
        Next
    Next

    'loop through, remove all the "extra configs"
    For Each entry As ZipEntry In From a In zip.Entries Where a.IsDirectory = False

        Dim removed As Boolean = False

        For Each myMatch As Match In SubConfigRegex.Matches(entry.FileName)
            If myMatch.Success Then
                filestoremove.Add(entry)
                removed = True
            End If
        Next
        If removed = False Then
            For Each myMatch As Match In SubConfigRegex2.Matches(entry.FileName)
                If myMatch.Success Then
                    filestoremove.Add(entry)
                End If
            Next
        End If
    Next

    'delete them
    For Each File In filestoremove
        zip.RemoveEntry(File)
    Next

    For Each f In filetoadd
        zip.AddFile(f.Key, f.Value)
    Next
    zip.Save()
End Using

最后但最重要的是我们实际替换 web.configs 的位置。

Public Class doTransform
    Property tempdir As String
    Property filename As String
    Property subfilename As String
    Public Function doTransform()
        'do the config swap using msbuild
        Dim be As New Engine
        Dim BuildProject As New BuildEngine.Project(be)
        BuildProject.AddNewUsingTaskFromAssemblyFile("TransformXml", "$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.Tasks.dll")
        BuildProject.Targets.AddNewTarget("null")
        BuildProject.AddNewPropertyGroup(True)
        DirectCast(BuildProject.PropertyGroups(0), Microsoft.Build.BuildEngine.BuildPropertyGroup).AddNewProperty("GenerateResourceNeverLockTypeAssemblies", "true")

        Dim bt As BuildTask
        bt = BuildProject.Targets("null").AddNewTask("TransformXml")

        bt.SetParameterValue("Source", tempdir & filename)
        bt.SetParameterValue("Transform", tempdir & subfilename)
        bt.SetParameterValue("Destination", tempdir & Path.GetDirectoryName(filename) & "/transformed.config")
        'bt.Execute()
        BuildProject.Build()
        be.Shutdown()

    End Function

End Class

就像我说的......这很困难,但可以做到。

【讨论】:

优秀/全面的答案,但您可以使用 MsBuild 完成所有这些工作。看我的回复。如果“额外”文件有问题,您只需修改包含输入/输出文件的 ItemGroups,这些输入/输出文件会转到 Publish/Package 调用 是的,好主意。在我的场景中唯一的问题是我们有 15 个站点使用不同的配置等做同样的事情。所以我构建它只是为了在部署时“做”。不过你的要简单得多。 完全忘记了主要原因......它给了我一个包含每个环境配置的包。所以我可以将相同的包部署到 dev、test、qa、stage、prod 等。 是的,我们做同样的事情(一个包)仍然使用 msbuild 概念。我们转换所有配置并将它们放入包中,然后推送。然后我们以自动方式使用 msdeploy 的 exclude 操作符在部署时推送正确的配置。它的扩展性非常好,我们目前在多个数据中心和天蓝色的机架上使用约 65 个应用程序。一开始很痛苦,但经过多年的成长,我很高兴我们是“jsut Msbuild” 您能否详细说明您是如何做到的?我很想看到更多细节。谢谢!!

以上是关于使用 msdeploy 转换文件的主要内容,如果未能解决你的问题,请参考以下文章

使用msdeploy安装ng build创建的dist文件夹

msdeploy仅跳过web.config root

在Visual Studio 2012中使用MSBuild PublishProfile时,MSDeploy跳过规则

防止 msdeploy 在 TFS 构建后同步未更改的文件?

Team Build:使用 MSDeploy 在本地发布

MSDeploy 没有在目的地更新正确的路径