Monday, February 13, 2012

Continuous Delivery with psake and TeamCity - Config Templates

The series so far:

  1. Creating a Local Build With psake, Part 1: Compiling
  2. Creating a Local Build with psake, Part 2: Testing
  3. Continuous Delivery with psake and TeamCity - Reusing the Local Build to Create a CI Build
  4. Continuous Delivery with psake and TeamCity - Preparing for a pipeline
  5. Continuous Delivery with psake and TeamCity - Creating a pipeline with artifact dependencies
  6. Continuous Delivery with psake and TeamCity - Visualizing a pipeline
  7. Continuous Delivery with psake and TeamCity - Environment Configuration Management
  8. Continuous Delivery with psake and TeamCity - Time for Some Refactoring

When we are deploying we always do some config transforms so that the application is configured for the right environment. In Nant we got xmlpoke, in msbuild 4.0 we got XmlPoke and there's also web.config transforms in .Net 4.0 which has been transformed to a general config transformation tool. In psake there isn’t something like this available, however as James Kovacks himself says, it’s very easy to do with PowerShell. I’ve created a little PowerShell script called Replace-Tokens, which you can find on GitHub, that does the work. Given a file some.template:

hello #{world}

and a file some.data.ps1:

@{
    world = 'world'
}

When we run:

replace_tokens_with_file "some.data.ps1" "some.template" "output.txt"

The output is the file output.txt:

hello world

The main idea here is that we pass in a hash table who's keys will be located in the template file and swapped by the value. We can also pass in a hash table directly instead of a file. So referring to my former post,  if we were to change the app.config of the application when deploying to Test and Acceptance Test  we could use the script and do the transformation. Change the test.ps1 properties file to:

$database_server = "Server 2"

$app_config_data = @{
    "database_connection_string" = "Data Source=$database_server;Initial Catalog=App;Integrated security = true";
}

Then change the deploy task of deploy.ps1 to:

task deploy -depends set_build_number{
    Write-Output "deploying to $env => database is deployed to $database_server"
    $app_path = "$build_artifacts_dir\Debug\ContinuousDelivery.WpfApplication"
    $config_file = "ContinuousDelivery.WpfApplication.exe.config"
    replace_tokens $app_config_data "$configs_dir\$config_file.template" "$app_path\$config_file"
}

Note that we pass in the $app_config_data from the test environment properties file. Now we just need to create a template of the app.config called ContinuousDelivery.WpfApplication.exe.config.template and place in in folder called configs under the build folder:

<?xml version="1.0"?>
<configuration>
  <connectionStrings>
    <add name="database" connectionString="#{database_connection_string}"/>
  </connectionStrings>
</configuration>

This will overwrite the app.config based on the values found in the environment specific properties file when we deploy. Download the code from GitHub.

Tuesday, February 7, 2012

Continuous Delivery with psake and TeamCity - Time for Some Refactoring

Refactoring is not just something you do with code. It’s also something you do with the build scripts and build process. Today we will split up our build script in nested builds and do some changes to how we pass on the build number to the next build configuration in the pipeline.

My previous installments in this series:

  1. Creating a Local Build With psake, Part 1: Compiling
  2. Creating a Local Build with psake, Part 2: Testing
  3. Continuous Delivery with psake and TeamCity - Reusing the Local Build to Create a CI Build
  4. Continuous Delivery with psake and TeamCity - Preparing for a pipeline
  5. Continuous Delivery with psake and TeamCity - Creating a pipeline with artifact dependencies
  6. Continuous Delivery with psake and TeamCity - Visualizing a pipeline
  7. Continuous Delivery with psake and TeamCity - Environment Configuration Management

First up, I want to change how I pass in the task to run and the environment to the build script. It looked something like this for the CI configuration:

&  {$host.UI.RawUI.BufferSize = new-object System.Management.Automation.Host.Size(512,50); .\tools\psake\psake .\build\build.ps1 ci -parameters @{env='ci}}

See how ci gets passed in, both for the task and the env parameter. I want to change this so the build script gets these values from TeamCity through Configuration Parameters. By doing that it’s a little bit easier to see and change these values. Let’s define some configuration parameters. Go to Build Parameters and add this:

image

Change the PowerShell build step to:

&  {$host.UI.RawUI.BufferSize = new-object System.Management.Automation.Host.Size(512,50); .\tools\psake\psake .\build\build.ps1 %task% -parameters @{env='%environment%'}}

TeamCity let’s you access the parameters that you have defined by typing %parameter_name%. Do the same changes for the Test and Acceptance Test build configuration.

Next we’ll do something similar to pass in the build number from CI to Test and Acceptance Test. When you set up a dependent build you get access to all the build parameters passed and set by the build a build configuration is dependent on. So if C is dependent on B and B is dependent on A, C gets access to both A’s and B’s build parameters. So the build number of CI gets passed to Test and Acceptance Test as %dep.bt2.build.number% where bt2 is the build configuration id of CI. For Test and Acceptance Test add the following build parameter:

image

and change the PowerShell build step to:

&  {$host.UI.RawUI.BufferSize = new-object System.Management.Automation.Host.Size(512,50); .\tools\psake\psake .\build\build.ps1 %task% -parameters @{env='%environment%'; build_number='%build_number%'}}

The final thing I want to change how we call the task deploy and set_build_number. Both these tasks are relevant only to deployment so we’ll move them to a separate file called deploy.ps1:

$framework = '4.0'

include .\..\tools\psake\teamcity.ps1

task default -depends deploy

task deploy -depends set_build_number{
    Write-Output "deploying to $env => database is deployed to $database_server"
}

task set_build_number {
    TeamCity-SetBuildNumber $build_number
}

and then we’ll add a new deploy task in build.ps1 which calls the the deploy build script so that we get nested builds, a feature in psake. Here we also pass along all the parameters and properties so that the nested build can access the same variables:

task deploy {
    invoke-psake .\deploy.ps1 -properties $properties -parameters $parameters
}

Remember to always refactor your build scripts and build process. It’s just as important as code refactorings :)

Download the bits from GitHub.