Client application has different app.config per environment

I have a set of services with web.config transforms that Octopus easily transforms at the time of deploy. I also have a WiX .msi/.exe containing a client application that needs it’s app.config transformed depending on which environment it is going to. Prior to Octopus I had been handling this with SlowCheetah by running the build for the installer once for each environment.

I’m trying to get setup with Octopus but I’m not sure how to handle Octopus’ 1-build / many-environment philosophy for the WiX installer. The app.config is compiled into the installer so there is no option to modify it at the time of deployment. Are there any recommendations for how to setups my TeamCity builds and Octopus deploy so that I can do all the builds at once (as recommended by Octopus) and progress it through the environments?

Note the installer will be run by hundreds of non-savvy corporate users without supervision so I can’t really expect them to call the installer with command arguments or use a .zip file with a .bat file in it.

Thanks for your help,
Kevin

Hi Kevin,
This is certainly an interesting problem.
Just so we know that we are properly solving your problem, could you provide a little more information about your deployment scenario. How does Octopus Deploy fit into your deployment strategy considering the product being deployed is an installer? Do the customers run the installer on their machine, seperate from Octopus Deploy, or does the server trigger the installations? Essentially, what do your deployment, targets and projects actually consist of and what does the full automation strategy look like?

Based on how it appears that your process works i’d have to ask if you have any of the app.config files which reside outside the .msi post-build that Octopus would be able to get access to to perform variable replacement on? Is it correct to say that you have a build process which creates an installer which gets pushed to Octopus to deploy? Im not sure that Octopus Deploy can be of much help for configurations that have become embedded within the installer since its only really during its build phase that it would be able to be modified. We do know of users who happily use SlowCheetah for configuration transforms during the build process before it gets passed along to an Octopus server and we see that as an acceptable approach.
If you are able to include a .config with the installer inside the nuget package then perhaps Octopus can get involved and modify the config before the installer is run.

I hope my response answers some of your questions around this approach, but I’m sorry there isn’t a simple solution I can provide. Please let me know if my assumptions about how your deployments take place are incorrect and let me know if you would like any further clarification of how Octopus Deploy can be of help in this process.
Cheers,
Robert

We have an internal application that has both a WPF client and a WCF server component. We are using TeamCity to create a NuGet package for Octopus to deploy the server components and transform the web.config to point at the appropriate database, etc. This works perfectly.

Each release of the server components also has a corresponding client application that is packaged into a WiX installer that users download from a network share and run at their leisure. Currently, Visual Studio and SlowCheetah compile the client WPF application with the appropriately transformed config and then WiX packages it into an installer. Using TeamCity as both a builder and deployer, we push that installer to a network share where corporate users can download and run it.

It’s pretty clear that there is little Octopus could do to transform a config that is compiled into an installer. What I’m looking for is some guidance on is how to work around this situation.

My current thought is that when TeamCity is building it’s server NuGet package it could run a build of the WPF client/WiX project to create an installer for each environment. I’m not sure where to incorporate multiple installers into an Octopus deploy though. Maybe each is a separately named NuGet package for each environment and there is a step that would, based on Variables, deploy the QA WPF package to the QA network share on a QA deploy, and the same for Production, etc.

Ideally we wouldn’t have to make and store 3 or 4 or 5 20MB installers with each build, but I don’t see any way around it right now. I’ll be sure to post if I come up with any

Thank you,
Kevin

Hi Kevin,
Given that it looks like we can both agree that something like SlowCheetah during the build process to generate the packages is the way to go the next question is, as you mentioned, how can Octopus Deploy help with the deployments.

It looks like you are on the right path in terms of how the deployment process would work. What I would suggest is that when TeamCity builds each of the different environment’s packages, it build the package with a distinct name that can be referred to from within Octopus Deploy. So for example the staging name might be Acme-Stg.nupkg and UAT Acme-UAT.nupkg. Within the deployment step you can then refer to the package using variable replacement e.g. Acme-#{enviroPrefix}.nupkg. This variable enviroPrefix could then have a differently scoped environment variable that defines which package gets picked up and used. It sounds like you are thinking of a similar solution which is a good sign.

This does mean you will need to store multiple installers for each “version” however the other problem is that since a variable replacement will be used for determining the NuGet package, the retention policy will not remove the package. This is because there is no deterministic way for the server to determine if the package is/has been recently used in a deployment.
This isn’t a perfect solution but it does allow for your deployments to go through a more automated and manageable process.

Let me know your thoughts.
Robert

Thanks for the help Rob. Here is my final solution. It uses a single package that does not need a variable name in it. This allows the retention policy to remain intact.

  1. Setup the WiX project in Visual Studio to put the QA installer into bin\QA, the production installer into bin\Production, etc. This allows VS and SlowCheetah to do a different transform for each installer and to retain each environment’s newly built installer.

  2. Use 7-Zip in TeamCity to zip up the bin folder of the WiX installer project. This gives a QA\Installer.exe, Production\Installer.exe, etc

  3. Add a Deploy.ps1 to the zip file (stored in the VS solution) that chooses the installer from the proper folder using Octopus’ $OctopusParameters[‘Octopus.Environment.Name’] parameter

  4. Upload the .zip to Octopus’ NuGet feed using Powershell and System.Net.WebClient becuase NuGet.exe won’t push a .zip file. This should be called out more clearly in the help docs.

  5. Create a ‘Deploy a Package’ step in Octopus that uses this package and runs on whatever server needs to hold the installer for public consumption

  6. The deployment runs the Deploy.ps1 in the .zip file automatically, pulling out the appropriate installer and deleting everything else. The script I ended up with is shown below. It seems to work, but I haven’t done all the edge testing yet.

# This script takes a folder of installers (QA, Prod, etc), moves one out of the folder structure to the root, and deletes everything else

$installerFile = "Installer.exe"
$installerToCopy = $OctopusParameters['Octopus.Environment.Name'] + "\" + $installerFile
$holdingFileName = "DontDelete.exe"

if(Test-Path $installerToCopy)
{
	# Pull out the appropriate installer and rename to a known name
	Copy-Item $installerToCopy -Destination "."
	Rename-Item $installerFile $holdingFileName
}
else
{
	Write-Host "No installer deployed. Installer does not exist at: " + $installerToCopy
}

# Recursively delete all files in the deployment folder other than the renamed file 
get-childItem * -exclude $holdingFileName -recurse | remove-item -recurse -force -confirm:$false

# Rename the previously renamed file
if(Test-Path $holdingFileName)
{
    Rename-Item $holdingFileName Installer.exe
}

Hi Kevin,
I’m glad to hear you managed to get something together that works. I hope though that you don’t run into problems with the multiple installers in the same package!
I am assuming you are using the 3.3-beta build of Octopus Deploy where we just provided support for .zip files? We have some documentation available outlining this new feature here and here.
Let me know if you run into any other problems.
Cheers,
Robert

Rob, I appreciate your help.

Allow me to clarify my issue with the documentation. I was trying to use TeamCity to create a zip file and then use the TeamCity NuGet Publish step to push that file to Octopus. That fails because NuGet.exe, which TeamCity uses to publish, refuses to upload a .zip file.

I eventually worked around this by noticing on the ‘Library’ tab of Octopus that it uses PowerShell and System.Net.WebClient to upload .zip files to Octopus’ NuGet feed. Nowhere in the Octopus help or on the ‘Library’ tab is it called out that this is a limitation of NuGet.exe. I think you should clearly call out on both the ‘Library’ tab and in the help docs that to upload a .zip file/package you cannot use NuGet.exe and should instead use PowerShell.

Here is the error I get out of TeamCity when using NuGet.exe to publish

[10:08:07]Step 8/9: NuGet Publish Installers to Octopus (NuGet Publish)
[10:08:07][Step 8/9] Attempt to publish NuGet package with wrong extension: .zip, expected: .nupkg
[10:08:07][Step 8/9] push: Publish package xxx.16.3.72392.zip
[10:08:07][push] NuGet command: C:\BuildAgent\plugins\nuget-agent\bin\JetBrains.TeamCity.NuGetRunner.exe C:\BuildAgent\tools\NuGet.CommandLine.DEFAULT\tools\NuGet.exe push C:\BuildAgent\work\65d4267844f6cfbb\xxx.16.3.72392.zip %%teamcity_nuget_api_key_1%% -Source http://xxx/nuget/packages?replace=true
[10:08:07][push] Starting: C:\BuildAgent\temp\agentTmp\custom_script700182781756680353.cmd
[10:08:07][push] in directory: C:\BuildAgent\work\65d4267844f6cfbb
[10:08:07][push] JetBrains TeamCity NuGet Runner 8.0.37459.9
[10:08:07][push] Registered additional extensions from paths: C:\BuildAgent\plugins\nuget-agent\bin\plugins-3.3
[10:08:07][push] Starting NuGet.exe 3.3.0.212 from C:\BuildAgent\tools\NuGet.CommandLine.DEFAULT\tools\NuGet.exe
[10:08:08][push] Package does not contain a manifest.
[10:08:08][push] Process exited with code 1
[10:08:08][Step 8/9] Step NuGet Publish Installers to Octopus (NuGet Publish) failed