Преобразование файлов с помощью msdeploy

Могу ли я использовать механизм преобразования конфигурации MSDeploy для преобразования других файлов?


person Greg B    schedule 20.01.2011    source источник


Ответы (4)


(другой подход)

Пакет 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'" />
person Taylor Bird    schedule 08.02.2011
comment
Можете ли вы привести примеры того, как вы устанавливаете DestinationPath, Sourcefile, TransformFile и DestFile? Кажется, я не могу найти свойства записи MSBuild, чтобы установить их динамически. - person chief7; 18.03.2011
comment
Если вы просто установите целевое имя AfterBuild, тогда он просто запустится, и вам не нужен CallTarget (по крайней мере, в некоторых проектах VS2012). - person nateirvin; 14.12.2012

Ответ Тейлора мне не помог, и он не предоставил дополнительных подробностей. Поэтому я начал копаться в файле 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>
person chief7    schedule 21.03.2011

Короткий ответ: Да, можете. Но это "сложно".

Длинный ответ: когда мы развертываем сайты в местах назначения, у нас есть обычные файлы 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>

Откройте файл 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. Он извлечет их, заменит и вернет обратно. Для фактической замены мы используем те же библиотеки DLL, что и MSDeploy. Я также использую 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

Как я уже сказал... это сложно, но это можно сделать.

person Paul Lemke    schedule 03.02.2011
comment
Отличный / исчерпывающий ответ, но вы можете сделать все это с помощью MsBuild. Смотрите мой ответ. Если дополнительные файлы являются проблемой, вам просто нужно изменить группы элементов, которые содержат входные/выходные файлы, которые переходят к вызовам публикации/упаковки. - person Taylor Bird; 08.02.2011
comment
да, хорошая идея. Единственная проблема в моем сценарии заключается в том, что у нас есть 15 сайтов, которые делают одно и то же с разными конфигурациями и т. д. Поэтому я создал это, чтобы просто делать это во время развертывания. Хотя у вас все гораздо проще. - person Paul Lemke; 09.02.2011
comment
Совершенно забыл об основной причине этого... Он дает мне пакет с каждой конфигурацией среды. Таким образом, я могу развернуть один и тот же пакет в dev, test, qa, stage, prod и т. д. - person Paul Lemke; 09.02.2011
comment
Да, мы делаем то же самое (один пакет), все еще используя концепцию msbuild. Мы трансформируем все конфиги и кидаем их в пакет, и пушим это. Затем мы используем оператор exclude msdeploy в автоматическом режиме, чтобы передать правильную конфигурацию во время развертывания. Он очень хорошо масштабируется, в настоящее время мы используем около 65 приложений в стойке за стойкой в ​​нескольких центрах обработки данных и в Azure. В начале это было больно, но после многих лет роста я очень рад, что мы просто Msbuild - person Taylor Bird; 09.02.2011
comment
Не могли бы вы опубликовать более подробную информацию о том, как вы это сделали? Я хотел бы увидеть немного больше деталей. Спасибо!! - person Paul Lemke; 09.02.2011

Просто чтобы добавить к этому awnser, чтобы изменить другие файлы, кроме web.config в приложении, опубликованном с помощью msdeploy (webdeploy), вы можете установить атрибут scope в файле parameters.xml в корне проекта:

<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

person aL3891    schedule 20.10.2016