使用 msdeploy 转换文件

发布于 2024-10-13 14:59:55 字数 36 浏览 6 评论 0原文

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

Can I use the config transforms mechanism of MSDeploy to transform other files?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

冷情 2024-10-20 14:59:55

(另一种方法)

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

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

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

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

<Target Name="TransformFile">

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

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

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

(another approach)

The msdeploy packaging is jsut invoked during an MSbuild run for your project.

TransformXml is an included task of a .csproj or .vsproj build.

Just modify your build process to invoke that task on whatever file you need.

For example, what we do is write a custom target

<Target Name="TransformFile">

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

Then modify your .csproj to run this BEFORE the Publish task is invoked.

<CallTarget Targets="TransformFile" 
   Condition="'$(CustomTransforms)'=='true'" />
橘亓 2024-10-20 14:59:55

泰勒的回答对我不起作用,他没有提供更多细节。因此,我深入研究 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>

The answer by Taylor didn't work for me and he didn't provide further details. So I went spelunking into the Microsoft.Web.Publishing.targets file to find a solution. The following MSBuild Target can be added to project file to transform all other config files in the root application directory. Enjoy :)

<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>
余生共白头 2024-10-20 14:59:55

简短回答:是的,可以。但这是“困难”的。

长答案:
当我们将站点部署到目的地时,我们有通常的 web.test.config 和 web.prod.config。在我们引入 log4net.test.config 和 log4net.prod.config 之前,这一切都很好。 MSBuild 不会自动遍历并替换所有这些。它只会执行 web.config 的操作。

如果您想了解具体情况,请查看最后一个代码片段。它显示了采用一个配置并将其替换为替代配置的功能。但是……如果我描述一下整个过程就会更有意义。

流程:

  1. Msbuild 制作网站的 zip 文件包。
  2. 我们编写了一个自定义 .net 应用程序,它将获取该 zip 文件并对每个文件进行配置替换。重新保存 zip 文件。
  3. 执行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 来进行压缩。

因此,请添加对以下内容的引用:

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

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

Short answer: Yes you can. But it's "difficult".

Long answer:
When we deploy sites to destinations we had the usual web.test.config, and web.prod.config. This worked fine until we introduced log4net.test.config and log4net.prod.config. MSBuild will not automatically go through and replace all of these. It will only do the web.config ones.

If you want the nitty gritty go to the last code snippet. It shows the functions to take one config and replace it with a replacement. But... it will make more sense if I describe the whole process.

The process:

  1. Msbuild makes a zip file package of the site.
  2. We wrote a custom .net app that will take that zip file and do the config replacements on each one of the files. Resave the zip file.
  3. Execute the msdeploy command to deploy the packaged file.

MSbuild will not automatically replace all of the extra configs. What's interesting is MSBuild will remove any "extra" configs. So your log4net.test.config will be gone after it's build. So the first thing you have to do is tell msdbuild to keep those extra files in place.

You have to modify your vbProj file to include a new setting:

<AutoParameterizationWebConfigConnectionStrings>False</AutoParameterizationWebConfigConnectionStrings>

Open your vbProj file for the web application into your favorite text editor. Navigate to each deploy configuration you want this to apply too (release, prod, debug, etc.) and add that config into it. Here is an example of our "release" config.

<?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>

So now msbduild will build the project and keep those extra files in place and not do the replacements. Now you have to manually do them.

We wrote a .net app that will watch for these new zip files. I wrote some code that will spin through the whole zip package and find any configs that match the {configname}.{env}.config. It will extract them, replace them, and put them back. To do the actual replacement we use the same DLL's that MSDeploy uses. I also use Ionic.Zip to do the zip stuff.

So add reference to:

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

Import:

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

Here is the code that spins through the zip file

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

Lastly but the most important is where we actually do the replacement of the 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

Like I said... it's difficult but it can be done.

懵少女 2024-10-20 14:59:55

只是为了添加到此 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 文件时

Just to add to this awnser, in order to modify other files than the web.config in an application published with msdeploy (webdeploy) you can set the scope attribute in the parameters.xml file in the root of the project:

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

scope is a regex that will be used to find files to apply the match xpath to. I havent experimented with this extensively but as far as I understand it simply replaces what ever the xpath matches with the value that is provided later.

There are also other values that can be used for kind that will have different behaviors than an xpath, see https://technet.microsoft.com/en-us/library/dd569084(v=ws.10).aspx for details

note: this applies to when you're using a parameters.xml, not when using the web.config.Debug/Release files

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文