Thursday, February 19, 2009

Stilling Learning CI

Today some colleagues and I were discussing prospective deployment options. We have all worked on many projects, isnt it funny how often the deployment process, the most important part, is a complete after thought. After mentioning it this morning to the PM/BA/Non techies we realised there was no actual deployment procedure in place (this is my first real release at the current company). We decided to let them come up with a standardised plan for their end ("put last build into production" was an option we had to take away from them) while we sorted out our plan.

I am still very much learning abut builds and CI, so this is by no means the authoritative answer; This post basically described what we came up with.

Preface

One thing we are dong well is actually using ClickOnce, a technology that is perfectly suited for the large corporate environment we work in and deliver smart client app's to. We want to continue to do this but make sure it is done properly.

We have very loose processes out side of the people actually writing code. The non tech's are not au fait with agile, they are not too good at requirements, planning, resourcing... well you get it. So we need to insulate ourselves from any curve balls that get thrown by these guys. We also need to seriously cover our ass, because when the proverbial hits the fan around here it slide down the ranks very fast; we want to make sure the have no way of letting it get to us, unless of course we actually deserve it.

We don't have full control over deployments. There is a system team that will copy our applications up to the next environment (ie Test -> UAT), and it has to be the same application to get pushed up. This is fair enough, however it doesn't work well for ClickOnce, by default any one testing on a lower level would not get updated*, we actually need a separate application for each environment. We also have only once chance for deployments. if we mess up even one deployment it means this process is no longer automatically approved and every change must go thru a 2 week change management process.

The Plan

In the time I have been at my new contract I have managed to get a couple of pretty big wins. We are now completely TDD we have a build server, we do (close to) weekly deployments to a dev test environment and we are getting up to speed on scrum. What I really wanted next was "one click deployments".

The plan basically is:

  • We will decide a release date/time for moving from Dev to Test. We are currently doing 7 day sprints and trying (scrum is new) to deliver a working production-quality piece of software each week. ATM this is Wednesday 2pm (for a variety of reasons).
  • We run the Deploy Script**. The deploy script runs our standard build which performs unit and integration test, static analysis etc, then it modifies the config to the test, environment and publishes to the pre-deploy network location (then repeats for UAT then Prod) .
  • We now have all the releases of each version in a known pre-deployment folder. From here the test click once is copied to the real deployment folder.
  • The Testers test away and of course there are no bugs [ ;) ] and they approve release of version x.y.z.b. Because we have all the releases for each environment produced at the same time and are the exact same build (other than 3 small config files) the system lads can do their Test->UAT (or UAT->Prod)deployment based on the version number that has been approved.

This means

  • We have every release in pre-deployment for Test, UAT and Prod
  • The testers can let the system guys know the exact version number that has been approved. It is now up to the system guys to copy the correct versions up to the next environment.
  • We cant modify the files as it will break the manifest and render the app useless. This keep the system guys happy.
  • We are removed from the deployment process, which means we don't have to be at work at 9pm when the deployment takes place.
  • Multiple versions of the application can be held on the users workstation, each one assured it is the latest for it given environment. This keeps PM's, BA's,Testers & UAT testers very happy.

This process takes about a minute. A lot happens but it is totally repeatable and completely versioned. This certainly is a better/ faster/ more reliable option than the 3 hour deploys we did at my last place of work. To be honest I'm pretty happy with it. This should also work well with ASP.Net deployments however there would have to be a versioning folder "hand created"*** (I believe) to get the same effect.

So I haven't quite got my "one click deployments", but half a dozen clicks and some automated scripts that run in under 5 minutes (most of that is watching static analysis and  tests run) is a bloody good start. Plus it's a good time for me to sit back and have a coffee; I'm almost looking forward to deployments :)

 

For you nosey bastards; The deployment part of the script looks like:

<Target Name="DeployClickOnce">
<Message Text="**************BUILD_NUMBER = $(BUILD_NUMBER)*********************"/>
<Message Text="OutputFile='$(ApplicationPropFolder)\AssemblyInfo.cs'" />
<AssemblyInfo CodeLanguage="CS"
OutputFile="$(ApplicationPropFolder)\AssemblyInfo.cs"
AssemblyTitle="$(AssemblyTitle)"
AssemblyDescription="$(AssemblyDescription)"
AssemblyCompany="YOURCOMPANY"
AssemblyProduct="$(AssemblyProduct)"
AssemblyCopyright="Copyright © YOURCOMPANY"
ComVisible="false"
CLSCompliant="true"
Guid="$(WinUiProjectGuid)"
AssemblyVersion="$(BUILD_NUMBER)"
AssemblyFileVersion="$(BUILD_NUMBER)"
/>
<PropertyGroup>
<DevFolderLocation>$(RootClickOnceDeploymentLocation)\DEV\$(ProjectName)\</DevFolderLocation>
<TestFolderLocation>$(RootClickOnceDeploymentLocation)\TEST\$(ProjectName)\</TestFolderLocation>
<UatFolderLocation>$(RootClickOnceDeploymentLocation)\UAT\$(ProjectName)\</UatFolderLocation>
<ProdFolderLocation>$(RootClickOnceDeploymentLocation)\PROD\$(ProjectName)\</ProdFolderLocation>
</PropertyGroup>

<Error Condition="!Exists($(RootClickOnceDeploymentLocation))" Text="The Root deployment location ($(RootClickOnceDeploymentLocation)) does not exist, publish can not occur."/>

<MakeDir Directories="$(DevFolderLocation)" Condition="!Exists($(DevFolderLocation))"/>
<MakeDir Directories="$(TestFolderLocation)" Condition="!Exists($(TestFolderLocation))"/>
<MakeDir Directories="$(UatFolderLocation)" Condition="!Exists($(UatFolderLocation))"/>
<MakeDir Directories="$(ProdFolderLocation)" Condition="!Exists($(ProdFolderLocation))"/>

<MSBuild Projects="$(SolutionFile)"
Targets="Publish"
Properties="Configuration=Release;
PublishDir=$(DevFolderLocation);
PublishUrl=$(DevFolderLocation);
InstallUrl=$(DevFolderLocation);
UpdateUrl=$(DevFolderLocation);
ApplicationVersion=$(BUILD_NUMBER);
"/>
<MSBuild Projects="$(SolutionFile)"
Targets="Publish"
Properties="Configuration=Release;
PublishDir=$(TestFolderLocation);
PublishUrl=$(TestFolderLocation);
InstallUrl=$(TestFolderLocation);
UpdateUrl=$(TestFolderLocation);
ApplicationVersion=$(BUILD_NUMBER);
"/>
<MSBuild Projects="$(SolutionFile)"
Targets="Publish"
Properties="Configuration=Release;
PublishDir=$(UatFolderLocation);
PublishUrl=$(UatFolderLocation);
InstallUrl=$(UatFolderLocation);
UpdateUrl=$(UatFolderLocation);
ApplicationVersion=$(BUILD_NUMBER);
"/>
<MSBuild Projects="$(SolutionFile)"
Targets="Publish"
Properties="Configuration=Release;
PublishDir=$(ProdFolderLocation);
PublishUrl=$(ProdFolderLocation);
InstallUrl=$(ProdFolderLocation);
UpdateUrl=$(ProdFolderLocation);
ApplicationVersion=$(BUILD_NUMBER);
"/>
</Target>


 



NB:You will need the MS Build Community Task download to update the AssemblyInfo.cs. The M$ one is a bit flakey (apparently). I used the community task for other things anyway so I figured I would use what is there.



NB: the BUILD_NUMBER is being passes in as a parameter to the script.



As my MSBuild skills are not the sharpest, I have left the repeated code in. I spent a couple of minutes trying to figure out how to prevent repeating myself with MSBuild but to no avail. if you have any ideas (short of Rake) let me know.  I am also still figuring out ClickOnce so some of those URLs are probably not necessary, have a play yourself.... this is just my first run.



*because, for example, Dev version would be higher than Test so when clicking on the latest Test ClickOnce application it would deem the application does not need to be updated as the dev version number is higher than the Test version number (if they were being run on the same machine). The applications have to be different applications for each environment. We still need to confirm this is the case with today's changes. Yeah... a bit of a pain, but worth it I guess.



**we actually stop the build server for the project we are deploying, reset the build counter, increment the release version, then run the script then restart the server.



** MakeDir is not really hand created, but you get my drift ;)

No comments: