4

Within the Visual Studio solution that contains all our unit tests we have some text files. These text files are checked based on some results generated by our unit tests.

In order to load the files we have an app.config with:

 <appSettings>
    <add key="BaseTestDataPath" value="D:\MyPath\MySolution\" />
 </appSettings>

Within TeamCity on each build run I want to:

Change the BaseTestsDataPath to the specific work path of the agent eg.

C:\TeamCity\buildAgent\work\1ca1a73fe3dadf57\MySolution\

I know the physical layout within the agent work folder so what I need to know is:

  • How to change the app.config file prior to the Nunit run against the solution in my build steps for TeamCity
TheEdge
  • 9,291
  • 15
  • 67
  • 135

3 Answers3

14

There are a couple of approaches to this.

Just choose one of the following scripts, add it to your source control and setup a PowerShell build runner in your build configuration to run the script passing in the required parameters, before you run the NUnit step. If you choose option two then you'll also need to consider the transform dll.

AppSettingReplace.ps1

If you only want to change a single value you can achieve this with some simple PowerShell that will load up the config file into an xml document, iterate the app settings and change the one that matches.

# -----------------------------------------------
# Config Transform
# -----------------------------------------------
#
# Ver   Who                     When      What
# 1.0   Evolve Software Ltd     13-05-16  Initial Version

# Script Input Parameters
param (
    [ValidateNotNullOrEmpty()]
    [string] $ConfigurationFile = $(throw "-ConfigurationFile is mandatory, please provide a value."),
    [ValidateNotNullOrEmpty()]
    [string] $ApplicationSetting = $(throw "-ApplicationSetting is mandatory, please provide a value."),
    [ValidateNotNullOrEmpty()]
    [string] $ApplicationSettingValue = $(throw "-ApplicationSettingValue is mandatory, please provide a value.")
)

function Main() 
{
    $CurrentScriptVersion = "1.0"

    Write-Host "================== Config Transform - Version"$CurrentScriptVersion": START =================="

    # Log input variables passed in
    Log-Variables
    Write-Host

    try {
        $xml = [xml](get-content($ConfigurationFile))
        $conf = $xml.configuration
        $conf.appSettings.add | foreach { if ($_.key -eq $ApplicationSetting) { $_.value = $ApplicationSettingValue } }
        $xml.Save($ConfigurationFile)
    } 
    catch [System.Exception] {
        Write-Output $_
        Exit 1
    }

    Write-Host "================== Config Transform - Version"$CurrentScriptVersion": END =================="
}

function Log-Variables
{
    Write-Host "ConfigurationFile: " $ConfigurationFile
    Write-Host "ApplicationSetting: " $ApplicationSetting
    Write-Host "ApplicationSettingValue: " $ApplicationSettingValue
    Write-Host "Computername:" (gc env:computername)
}

Main

Usage

AppSettingReplace.ps1 "D:\MyPath\app.config" "BaseTestDataPath" "%teamcity.build.workingDir%"


XdtConfigTransform.ps1

The alternative approach to this is to provide full config transformation support using XDT - This does require Microsoft.Web.XmlTransform.dll to end up on the server somehow (which I normally put into source control).

The following script will transform one config file with another one.

# -----------------------------------------------
# Xdt Config Transform
# -----------------------------------------------
#
# Ver   Who                     When      What
# 1.0   Evolve Software Ltd     14-05-16  Initial Version

# Script Input Parameters
param (
    [ValidateNotNullOrEmpty()]
    [string] $ConfigurationFile = $(throw "-ConfigurationFile is mandatory, please provide a value."),
    [ValidateNotNullOrEmpty()]
    [string] $TransformFile = $(throw "-TransformFile is mandatory, please provide a value."),
    [ValidateNotNullOrEmpty()]
    [string] $LibraryPath = $(throw "-LibraryPath is mandatory, please provide a value.")
)

function Main() 
{
    $CurrentScriptVersion = "1.0"

    Write-Host "================== Xdt Config Transform - Version"$CurrentScriptVersion": START =================="

    # Log input variables passed in
    Log-Variables
    Write-Host

    if (!$ConfigurationFile -or !(Test-Path -path $ConfigurationFile -PathType Leaf)) {
        throw "File not found. $ConfigurationFile";
        Exit 1
    }
    if (!$TransformFile -or !(Test-Path -path $TransformFile -PathType Leaf)) {
        throw "File not found. $TransformFile";
        Exit 1
    }

    try {

        Add-Type -LiteralPath "$LibraryPath\Microsoft.Web.XmlTransform.dll"
        $xml = New-Object Microsoft.Web.XmlTransform.XmlTransformableDocument;
        $xml.PreserveWhitespace = $true
        $xml.Load($ConfigurationFile);

        $xmlTransform = New-Object Microsoft.Web.XmlTransform.XmlTransformation($TransformFile);
        if ($xmlTransform.Apply($xml) -eq $false)
        {
            throw "Transformation failed."
        }
        $xml.Save($ConfigurationFile)
    } 
    catch [System.Exception] {
        Write-Output $_
        Exit 1
    }

    Write-Host "================== Xdt Config Transform - Version"$CurrentScriptVersion": END =================="
}

function Log-Variables
{
    Write-Host "ConfigurationFile: " $ConfigurationFile
    Write-Host "TransformFile: " $TransformFile
    Write-Host "LibraryPath: " $LibraryPath
    Write-Host "Computername:" (gc env:computername)
}

Main

Usage

XdtConfigTransform.ps1 "D:\MyPath\app.config" "D:\MyPath\app.transform.config" "%teamcity.build.workingDir%\Library"

Note: The last parameter is the path to the directory that contains Microsoft.Web.XmlTransform.dll

Github Repository - teamcity-config-transform

Hope this helps

Matt
  • 3,684
  • 1
  • 17
  • 19
  • Thanks this is exactly what I am looking for, you need more +1's for this answer ;) – Rippo Sep 21 '16 at 17:35
  • Is it possible to find all app settings keys and replace it values with TeamCity Configuration Parameter that match key name? Find Replace value with %$1% – user5629690 Jun 12 '22 at 05:06
4

You can use File Content Replacer build feature to performe regular expression replacements in text files before a build. After the build, it restores the file content to the original state.

Alina Mishina
  • 3,320
  • 2
  • 22
  • 31
1

Optionally you can use nuget package id="SlowCheetah". That adds transformation for app.config. On build it transforms so no need for extra scripts or dlls.

Leszek P
  • 1,807
  • 17
  • 24
  • While you could add this NuGet package it is ultimately only necessary for TC so having it in the solution itself is not necessary. So it still means messing around with a specific TC implementation. This does not seem to be any different than running separate scripts? – TheEdge Mar 23 '17 at 20:31
  • It helps with testing on pc. I have app.config.Debug, app.config.UAT, app.config.Release, etc. and with build solution it got transformed so can test diff servers, databases, etc out of the box without any extra scripts. – Leszek P Mar 24 '17 at 11:18