четверг, 2 августа 2012 г.

CI and msdeploy: I want to parameterize my package

When we deploy to different environments, we’d like to have different configuration values to be set during deployment. Some of them (like connection strings) can be parameterized by msdeploy itself when it creates deployment package, but that’s only part of the story. Say we want some values in web.config to be changed depending on environment we’re deploying to. Good news: msdeploy has a mechanism called ‘deployment parameters’ – you can supply a file declaring these parameters and msdeploy will parameterize your package (this is not the same as web.config transformation – a good overview of difference can be found here). Bad news: there’s no UI in Visual Studio that allows you to either specify these parameters or specify a file where they are declared. In case you decide to build deployment package on your own (by invoking msdeploy directly of from an msbuild script) you will face another problem: you can either supply deployment parameters declaration in xml file or have msdeploy gather/discover them – but not both. So you can’t define some of the parameters in a file and let msdeploy do the rest of the job for you. This comes especially inconvenient when you have a database deployment [sql] scripts in your package – in order for msdeploy to pick them up [when invoked from command line] you have to do a lot of plumbing around declaration and command-line arguments. From the other side, when msdeploy gathers parameters on its own, it tends to parameterize things that you might not want to be parameterized (like IIS application name or application pool name).

This is how it can be solved:
1) When Visual Studio creates a deployment package for your application it looks for [YourWebProjectName].wpp.targets file and imports it into running msbuild script (well, it is actually Microsoft.Web.Publishing.targets which looks for and imports this file in case it exists). This little file lets you extend / influence package building pipeline.
2) EnablePackageProcessLoggingAndAssert msbuild parameter allows you to see extended log of what happens when a deployment package is build for your project – by examining this you can discover a set of msbuild targets that can give you a clue on what to extend/precede.
3) DisableAllVSGeneratedMSDeployParameter – allows you to prevent VS from parameterizing your package (so you don’t have duplicate / unnecessary parameters).
4) ParametersXMLFiles item type allows you to supply your own parameters declaration file (you have to put it inside ItemGroup element and reference a file with parameters declaration).
5) When defining you own target, you can hook on a ‘standard’ one (i.e. the one, defined in Microsoft.Web.Publishing.targets) by using either of the following attributes: BeforeTargets, AfterTargets – they contain a coma-separated list of targets you want to hook on.

HTH,
AlexS

среда, 1 августа 2012 г.

CI and msdeploy: I want deployment package to be build by my CI tool

Here’s a scenario I have in my current project: ASP.NET MVC application is being deployed to a number of environments (staging, test, uat, production). Web Deployment tool (aka msdeploy) is used for assembling deployment package and for the deployment itself. There’s a CI tool employed (which is TeamCity) with corresponding set of [ms]build scripts.

At first sight the problem seems to be fairly simple – if Visual Studio can build deployment package for us, so why can’t we? It ain’t so simple, because in order to invoke msdeploy [for building deployment package] you need to provide it a lot of arguments and (which is more important) have all the files to be packaged ready at some location (which is not your project directory). This is done by Visual Studio when you … create a deployment package (via project context menu –> Build Deployment Package). But the problem is that this is an explicit action, triggered via a dedicated UI element (i.e. it’s not part of the build process). Fortunately for us, deeps inside it actually relies on a set of MSBuild targets that do all the job (for the curious ones: they are defined in Microsoft.Web.Publishing.targets file which resides in Program Files (x86)\MSBuild\Microsoft\VisualStudio\v10.0\Web\ folder on your system drive). And there’s a ‘magic switch’ which can be passed to MSBuild so it actually build deployment package as part of a normal build process.  These parameters are:

DeployOnBuild=True
DeployTarget=Package

so you will have to invoke msbuild like this

msbuild MySolution.sln /p:Configuration=Release,DeployOnBuild=True,DeployTarget=Package

In case you want to always create a deployment package during build, you can define the same parameters in your project file (but this will require editing it outside Visual Studio).

HTH,
AlexS