Friday, August 17, 2012

My home workspace

Recently I took the time to clean up my home workspace, so I thought now would be a great time to show it off. Pardon the blurrycam, as photography is not my forte, and these were taken on my 3 year old phone. I may try to update them down the line.

Most of the desk comes from IKEA, including the tan lap desk, which was built from wood in the as-is section. My chair is an Ikea Markus, which is great for tall people. The monitor is a 27" asus @ 1920x1200. I use a Logitech G500 gaming mouse with weights set on the left side to balance out my hand. The keyboard is a cheap but usable Wintec scissor key keyboard, as I hate typing on anything besides scissor keys.

The speakers are from an old stereo with a broken amp, driven by another amp which is best summed up as a college project gone horribly wrong, and a fire hazard waiting to happen.  Yes, it uses a spliced-in ac adapter I found at goodwill and "adjusted". While making it, I also had the experience of browning out a whole dorm building, true story.

On the bookshelf is my media server, which holds 5 1.5 TB hard drives, which serves as the on-site backup (to complement our off-site backup service, SpiderOak) while also hosting a Plex media sever and pyTivo. To switch between the server and assortment of laptops, I use an iogear 4 port KVM, and although it's only VGA, it works great.

The swords/knives are a collection of mine. I have a knife from every continent except Australia and Antarctica (Why haven't those penguins invented tools yet!?!). Specifically, the country list includes Romania, Brazil, India, China, Japan, the C.A.R. (I think), and the good ol USA. Right now you can only see the katana, wakizashi, and tanto sets. Most of the other swords are somewhat scattered around the house.

Monday, August 6, 2012

Multi-Site Continuous Integration with TeamCity and MSDeploy: Parameters.xml

 (JD Hancock)
I love continuous integration. CI servers such as TeamCity let developers see their changes against an example deployment immediately, as well as have a example site for demonstrations. However, what's the best way to CI a project with multiple client builds? For example, a framework project that will be used by multiple clients, each with their own server, database, and asset images?

For that use case, CI makes even more sense. Visual Studio makes it easy to develop on a single build configuration at a time, but would require a host of deployments to test changes side by side. Luckily, TeamCity is great at doing a host of deployments.

The goal for this particular project was to get TeamCity to deploy a solution with four web applications to multiple sites, each of which had it's own set of app settings and specific web.config setup. To do this, we needed to have a way to change these settings, preferably after build. Luckily for us, Microsoft has this functionality built in, though a special file called parameters.xml.

Parameters.xml lets us replace any node in an XML file on deployment. This is similar to web.config transforms, but is customizable for every deployment, can be used for any XML file, and does not require a rebuild. To specify a parameter, follow the format below:

<?xml version="1.0" encoding="utf-8" ?>
<parameters>
<parameter name="ConnectionString" defaultValue="DefaultSite\ConnectionStrings.config">
<parameterEntry kind="XmlFile" scope="\\web.config\$" match="/configuration/connectionStrings/@configSource" />
</parameter>
...
</parameters>

Each parameter must have a name and default value. Inside the parameter node, the XML file is specified (scope), along with an XPath location to modify (match). In this example, we have already set up ConfigSource on the connectionStrings in web.config. The parameter will replace the value of ConfigSource with a client specific value.

Along with the parameters.xml file, each site we deploy to will need to have it's own XML file with the specific site settings.

<parameters>
<setParameter name="ConnectionString" value="Site1\ConnectionStrings.config"/>
...
</parameters>


Using MSDeploy is generally straightforward. To deploy these changes to a client site, we will first need to perform a msbuild with a target of "package", and the desired build configuration. We also set the location of the package to a specific location, which will let us find it later on:

msbuild "/path/to/project.csproj" /t:Package /p:Platform=AnyCPU;Configuration=Release;OutputPath=bin;CreatePackageOnPublish=True; PackageLocation=bin\Deploy\project.zip


And in teamcity:

Once the package has been created, we can deploy it to as many sites as we want without having to rebuild. For a quick example, here is a sample msdeploy batch file to use custom parameters when deploying to a local IIS website. The gory details are here, but I'll try to explain the code after giving you a peek :-)

"C:\Program Files\IIS\Microsoft Web Deploy V3\msdeploy.exe" -source:package=%1 -setParamFile:%2 -dest:auto -verb:sync -disableLink:AppPoolExtension -disableLink:ContentExtension -disableLink:CertificateExtension -setParam:name="IIS Web Application Name",value="%3"


This batch file takes three parameters: %1 is the zip file package specified in msbuild. %2 would be the client-specific XML file. %3 is the local web site we deploy to. MSDeploy will take the deployment package, inject the parameters into it, and then uploads it to the local IIS instance.

%3 is actually an automatic parameter added when packaging, so we could have added it to the client specific XML as follows:

<parameters>
<setParameter name="IIS Web Application Name" value="ClientSite1" />
<setParameter name="ConnectionString" value="Site1\ConnectionStrings.config" />
...
</parameters>


However, in this case, I wanted the ability to reuse the deployment package artifacts created by TeamCity. Since the client web sites might have different names then their TeamCity versions, I left it out of the XML. We can use the -setParam command line parameter to set any parameter that is not a constant for each client.

If we run this batch file for each site, we can easily deploy customized releases to multiple IIS applications from a single build:

deploy.bat \path\to\package.zip path\to\site1\variables.xml ClientSite1

deploy.bat \path\to\package.zip path\to\site2\variables.xml ClientSite2

deploy.bat \path\to\package.zip path\to\site3\variables.xml ClientSite3