Tuesday, June 23, 2009

ClickOnce, Team Systems, and Versioning

You'd think everyone has only one deployment. In my last post, I covered how to set up a Team Build Type for a ClickOnce Application in Team Systems that allows you to modify the app.config file for multiple deployments. However, I had set up the versioning so that Major, minor, and build were stored in the Build Type project file, and the revision came out of the changeset.

Unfortunately, what I discovered was that I'd have to modify the project files for all deployements when the version for Major, minor, or build changed. Since I already had 20 deployments, with the number expected to reach nearly 100, this was unacceptable. I needed a way to store version information in a single file accessible from the build.

The best solution for me is to store it in the AssemblyInfo.cs file of the ClickOnce project. This data is easily modifiable from Visual Studio in the Project's properties, plus ensured the ClickOnce manifest version matched the assembly.

Again Googling, everything was backward. Plenty of links on how to set up a dynamic AssemblyInfo.cs file, or to store version data in a modifyable file as part of the project--no, no, no! This is not what I want! When I run 20 builds, I want all 20 to have the same version number, without having to modify 20 files to do so. Generating the AssemblyInfo.cs file was completely backward and would not ensure consistent version numbers. Using a shared version file was the next best thing, but I'd have to remember to modify it manually. I couldn't have the build change it as there would be a different build number for each of the 20 deployments.

Storing it in the AssemblyInfo.cs file was best as I could easily remember to always manually modify it. It would be even better if I could somehow replace the revision with the changeset number (which the project file already gets anyhow), but I can live without it.

--I can't believe how painful it is to find the information you need--

I thought of XmlRead from MSBuild.Community.Tasks (from http://msbuildtasks.tigris.org/), but AssemblyInfo.cs is in C#, not in XML. There was also a RegexMatch task that could be a possibility, but working with Regex is always nasty. Kind of makes me feel like I'm programming in Malbolge whenever I see it.

Okay, a RegEx tester I had came up with this pattern for extracting Major, minor, and build from AssemblyInfo.cs:(\[assembly\: AssemblyVersion\(.[0-9]\.[0-9]\.[0-9])?[0-9]\.[0-9]\.[0-9]

Unfortunately, after much painful work and finally looking at the source code from msbuildtasks.tigris.org, I discovered that the RegexMatch didn't return the capture match from the Regex expression, but the entire line that the Regex expression found a match on. Useless!

So I finally gave in and created my own custom task--which apparently I should have done in the first place and saved myself the headache of trusting others.

I won't go into the code for the custom task--I'll save something like that for another post. But basically it returned all captures found on a string with a Regex expression.

I used the ReadLinesFromFile task from the Microsoft.Sdc.Tasks to import the AssemblyInfo.cs file into an ItemGroup, which I converted to a PropertyGroup with a CreateProperty task, then passed this property to my custom task.

So, I had the following added:

<ReadLinesFromFile File="$(ConfigDir)/Properties/AssemblyInfo.cs">
<
Output TaskParameter="Lines"
ItemName="ItemsFromFile" />
</ReadLinesFromFile>



<!-- custom task below -->
<RegexCapture Input="@(ItemsFromFile)" Expression="(\[assembly\: AssemblyVersion\(.[0-9]\.[0-9]\.[0-9])?[0-9]\.[0-9]\.[0-9]">
<
Output ItemName ="VersionData" TaskParameter="Output" />
</
RegexCapture>

After a painful mess dealing with the confusion difference between "@" and "$", I finally had success http://msdn.microsoft.com/en-us/library/0k6kkbsd.aspx is a good MSBuild reference. http://msdn.microsoft.com/en-us/library/bb546106.aspx shows the special characters reference.

I used CreateProperty to convert the ItemList from the RegexCapture to a property, then referred to this property on the Regex that modified the version on the Landing Page, and on the ApplicationVersion of the MSBuild.

The revision was already in the project, using TfsVersion from MSBuild.Community Tasks from http://msbuildtasks.tigris.org/. I just concatenated this on the version information I grabbed from the AssemblyInfo file, thus ensuring each build would use an incrementing revision.

My next post: put this and my previous post together in one simplifed post, with details of what everything means.


No comments:

Post a Comment