La transformación de los archivos con MSDeploy
Pregunta
¿Puedo utilizar el mecanismo de las transformaciones de configuración de MSDeploy para transformar otros archivos?
Solución
(otro enfoque)
El embalaje MSDeploy se invoca jsut durante una ejecución MSBUILD para su proyecto.
TransformXml es una tarea incluida de un .csproj o .vsproj construcción.
Sólo modificar su proceso de construcción de invocar esa tarea en cualquier archivo que necesita.
Por ejemplo, lo que hacemos es escribir un objetivo de encargo
<Target Name="TransformFile">
<TransformXml Source="$(DestinationPath)\$(Sourcefile)"
Transform="$(DestinationPath)\$(TransformFile)"
Destination="$(DestinationPath)\$(DestFile)" />
</Target>
A continuación, modificar su .csproj para ejecutar esta tarea antes del Publish se invoca.
<CallTarget Targets="TransformFile"
Condition="'$(CustomTransforms)'=='true'" />
Otros consejos
La respuesta de Taylor no funcionó para mí y que no dio más detalles. Así que fui espeleología en los Microsoft.Web.Publishing.targets archivo para encontrar una solución. La siguiente Target
MSBuild se puede añadir al archivo de proyecto para transformar todos los demás archivos de configuración en el directorio raíz de la aplicación. Disfrutar:)
<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>
Respuesta corta: Sí se puede. Pero es difícil".
Respuesta larga: Cuando hacemos uso de los sitios a los destinos tuvimos la web.test.config habitual, y web.prod.config. Esto funcionó bien hasta que introdujimos log4net.test.config y log4net.prod.config. MSBuild no pasará automáticamente a través y reemplazar todos estos. Sólo lo hará los web.config.
Si desea que la auténtica pétreas ir al último fragmento de código. Muestra las funciones para tomar una configuración y reemplazarlo con un reemplazo. Pero ... que tendrá más sentido si describo todo el proceso.
El proceso:
- MSBUILD hace un paquete de archivos zip del sitio.
- Nos escribió una aplicación .NET a medida que se llevará a ese archivo zip y hacer los reemplazos de configuración en cada uno de los archivos. Vuelva a guardar el archivo zip.
- Ejecutar la orden MSDeploy para desplegar el archivo empaquetado.
MSBUILD no reemplazará automáticamente todas las configuraciones adicionales. Lo que es interesante es MSBuild eliminará cualquier configuraciones "extra". Por lo que su log4net.test.config habrá desaparecido después de la acumulación. Así que lo primero que tiene que hacer es msdbuild diga que mantenga esos archivos adicionales en su lugar.
Tiene que modificar su archivo vbproj para incluir un nuevo ajuste:
<AutoParameterizationWebConfigConnectionStrings>False</AutoParameterizationWebConfigConnectionStrings>
Abra el archivo de vbproj para la aplicación web en su editor de texto favorito. Vaya a cada configuración de despliegue que desea que esto se aplica también (liberación, prod, depuración, etc.) y añadir que la configuración en ella. Aquí está un ejemplo de nuestra configuración de "liberación".
<?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>
Así que ahora msbduild realizará el proyecto, y mantener esos archivos extra en su lugar y no hacer los reemplazos. Ahora usted tiene que hacer manualmente.
escribió una aplicación .NET que estarán atentos a estos nuevos archivos zip. Escribí un código que hará girar a través de todo el paquete postal y encontrar configuraciones que coinciden con el configname {}. {Entorno} .config. Se extraerá ellos, reemplazarlos, y ponerlos de nuevo. Para hacer efectiva la sustitución utilizamos MSDeploy que los usos de la misma DLL. También uso Ionic.Zip hacer las cosas postal.
Así que añadir una referencia a:
Microsoft.Build.dll
Microsoft.Build.Engine.dll
Microsoft.Web.Publishing.Tasks (possibly, not sure if you need this or not)
Importar:
Imports System.IO
Imports System.Text.RegularExpressions
Imports Microsoft.Build.BuildEngine
Imports Microsoft.Build
Este es el código que hace girar a través del archivo 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
Por último es el más importante, pero donde realmente hacemos la sustitución de los 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
Como he dicho ... es difícil, pero se puede hacer.
Sólo para añadir a esta awnser, con el fin de modificar otros archivos que el web.config en una aplicación publicada con MSDeploy (webdeploy) se puede establecer el atributo scope
en el archivo parameters.xml en la raíz del proyecto:
<parameters>
<parameter name="MyAppSetting" defaultvalue="_defaultValue_">
<parameterentry match="/configuration/appSettings/add[@key='MyAppSetting']/@value" scope=".exe.config$" kind="XmlFile">
</parameterentry>
</parameter>
</parameters>
scope
es una expresión regular que será utilizado para encontrar archivos para aplicar el XPath match
a. No he experimentado con esto extensamente pero por lo que entiendo que simplemente reemplaza lo que cada vez los partidos XPath con el valor que se proporciona más adelante.
También hay otros valores que se pueden utilizar para kind
que tienen diferentes comportamientos de un XPath, consulte https://technet.microsoft.com/en-us/library/dd569084 (v = ws.10) .aspx para más detalles
Nota: esto se aplica a cuando se está utilizando un parameters.xml, no cuando se utiliza el web.config.Debug / ficheros Release