Search code examples
xsltvisual-studio-2015tfstfs-2015vs-web-site-project

Manual config transform as TFS build step


I have a solution file that contains a web site project (among other project types). It uses a publish profile for deployment and I'm trying to move the entire thing to TFS (2015) to automate the build and deploy process. As stated here I cannot manage the build configuration since it is a web site project and consequently cannot use the Web.config Transformation feature.

I'd like to execute some sort of transform, perhaps as a build step. I can manually create and maintain the web.release.config file but don't know how to manually transform it. Does an XLST file exist to transform it outside of Visual Studio (e.g. a cmd build step to call an XSLT processor)?

Addendum: converting to a web project would most definitely fix the issue but not a solution for me as that would require involvement from the remote contractors contributing to our code base - a TFS build-level solution is the only thing I'm looking for.


Solution

  • Since my actual transforms were relatively simple I developed this XSL transform:

    <?xml version="1.0" encoding="utf-8"?>
    <xsl:stylesheet version="1.0"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:msxsl="urn:schemas-microsoft-com:xslt"
        exclude-result-prefixes="msxsl">
      <xsl:output method="xml" indent="yes"/>
      <xsl:variable name="xformPath">web.Prod.config</xsl:variable>
      <xsl:variable name="xform" select="document($xformPath)"></xsl:variable>
    
      <xsl:template match="@* | node()">
        <xsl:copy>
          <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
      </xsl:template>
    
      <xsl:template name="output-transform">
        <xsl:param name="xformNode" />
        <xsl:variable name="key" select="@key" />
        <xsl:choose>
          <xsl:when test="$xformNode">
            <xsl:copy-of select="$xformNode" />
          </xsl:when>
          <xsl:otherwise>
            <xsl:copy>
              <xsl:apply-templates select="@* | node()"/>
            </xsl:copy>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:template>
    
      <xsl:template match="/configuration/appSettings/add">
        <xsl:variable name="key" select="@key" />
        <xsl:call-template name="output-transform">
          <xsl:with-param name="xformNode" select="$xform/configuration/appSettings/add[@key=$key]" />
        </xsl:call-template>
      </xsl:template>
    
      <xsl:template match="/configuration/connectionStrings/add">
        <xsl:variable name="name" select="@name" />
        <xsl:call-template name="output-transform">
          <xsl:with-param name="xformNode" select="$xform/configuration/connectionStrings/add[@name=$name]" />
        </xsl:call-template>
      </xsl:template>
    
      <xsl:template match="/configuration/system.web/customErrors">
        <xsl:call-template name="output-transform">
          <xsl:with-param name="xformNode" select="$xform/configuration/system.web/customErrors" />
        </xsl:call-template>
      </xsl:template>
    </xsl:stylesheet>
    

    That has some obvious shortcomings,

    • Items to be replaced must be manually defined
    • Replaces with the entire element, not only the specified attributes
    • Does not maintain children elements (e.g. I tried to change system.web/compilation@debug to false but I lost all my system.web/compilation/assemblies entries)

    I plan to add this in a Command Line step, between my Visual Studio Build step and Copy Files step, and call either msxsl.exe or Saxon's HE transform engine.