Damian Hickey

Mostly software and .NET related. Mostly.

Getting TeamCity Build Agents and OpenWrap to work together

Just had some fun trying to get OpenWrap (1.0.0.53270093) working on TeamCity (6.0), and to be honest, I'm not entirely sure that I am doing it right. My build tool of choice is psake, and I am calling OpenWrap from within one of my build tasks.

Anyway, this is what I had to do and feedback is, of course, appreciated :)

1. Login to your build server with the same credentials your agents run under.

OpenWrap likes to install itself into the user specific application data folder, c:\Users\[User]\AppData\Local\openwrap, so you will need to login to your build server with the same credentials the TeamCity build agents run under. So they could access network resources, my agents run with domain credentials so this wasn't a problem.

2. Install OpenWrap with the 'i' option, "install the shell and make it available on the path?".

This will install OpenWrap in the folder mentioned above. The 'c' option, "use the current executable location and make it available on the path?" threw a DirectoryNotFoundException for me. Choosing 'n', "do nothing?" means that o.exe will continue to prompt you the installation questions.

This is where the fun begins. OpenWrap will add an entry to the user's (as opposed to the system's), PATH environment variable, creating the PATH variable if it didn't already exist. In my case, it didn't. Nothing wrong with this but there seems to a bug with TeamCity whereby, if the user's PATH environment variable is specified, the agent will use that and ignore the system one altogther. The expected behaviour would be to concatenate both.

So when restarting the agent, it's PATH enviroment variable went from this:

to this:

...and that broke a whole bunch of things!

3. Delete the User PATH environment variable.

Control Panel -> System -> Advanced Tab -> Environment Variables -> User variables for [username].

4. Modify buildAgent.properties

Open buildAgent.properties (typically located in C:\TeamCity\buildAgent\conf) in notepad and add the following line:

env.PATH=%env.PATH%;C\:\\Users\\[username]\\AppData\\Local\\openwrap

This is a workaround that adds the OpenWrap path to the system's PATH variable just for the use of the agent.

5. Restart Agent service

The agent's PATH variable should now be fixed:

And your build scripts should be able to call 'o' without problem.

 

Even though the bug is entirely lies with TeamCity, I do think OpenWrap could provide an better mechanism for such deployment scenarios. My system admin would prefer actually prefer MSI.. I don't know enough about OpenWrap's architecture perhaps an install for 'all users' would help?

If you spot anything wrong, or there is a better way, do let me know.

Patching OpenWrap descriptor with a branch name.

Firstly, why would would one want to do this? Well, it would depend on your team's setup. If you like to regularly create branches (for features, versions, spikes or otherwise), you may also like to have those branches built under CI. The problem is that it is not desirable to have the artifacts / packages of builds of these branches conflicting. Especially if you publish these artifacts to a shared location on a network drive.

Since two branches may have the same 'version', the best way (that I can come up with) to differentiate OpenWrap packages is based on the branch name, since that is usually guaranteed to be unique within a VCS.

At this point I am mostly experimenting, so I don't have this in active use, but this is how I am considering doing it. My build script tool of choice is PSake and the VCS is HG, but the concept should work for other setups.

Take an OpenWrap.desc file:

name: DH.Common
depends: Xunit
depends: ...

Add a field to the name and commit this:

name: DH.Common.%BranchName%
depends: Xunit
depends: ...

Add a build task to 'patch' the OpenWrap.desc file. This should be exected before the openwrap task:

task PatchOpenWrapDesc{
   $branchName = hg.exe branch
   Get-Content $rootDir\OpenWrap.desc | `
      Foreach-Object {$_ -replace "%BranchName%", "$branchName"} | `
      Set-Content $rootDir\OpenWrap.desc
}

This will result in a wrap called "DH.Common.branchName.wrap" and will be not clash with wraps created from other branches, by default.

If there is need to designate a branch as a 'release', removing the branch name patching is a dilberate step, which I prefer from an SOP point of view.

On ReSharper, object initializers, disposables and code analysis rule CA2000

Given a disposable class and a consumer:

public class DisposableClass : IDisposable
{
	public string Text { get; set; }

	public void Dispose()
	{}
}

public class Consumer
{
	public Consumer()
	{
		var disposableClass = new DisposableClass();
		disposableClass.Text = "S";
	}
}

ReSharper's quick fix will suggest to use object initializer:

Following this suggestion gives:

public class Consumer
{
	public Consumer()
	{
		var disposableClass = new DisposableClass { Text = "S" };
	}
}

But there is is a problem with this. If you run code analysis you will get the the following warning:

CA2000 : Microsoft.Reliability : In method 'Consumer.Consumer()', call System.IDisposable.Dispose on object '<>g__initLocal0' before all references to it are out of scope.

The problem is that under the hood, the compiler is creating a temporary local of DisposableClass, <>g_intLocal0, initializing it, then assigning it to variable disposableClass.

It's this local that it not getting disposed that causes the code analysis warning.

Perhaps the compiler could do a better job here but ReSharper could definitely do a better job:

  • Only suggest the object initializer quick fix for classes that are not disposable.
  • Include a quick fix to remove object initializers from disposable objects.

The issue has been reported, but infortunately JetBrains have marked it as "Won't Fix".

Edit 22/09/12: Looks like this will be fixed in Resharper 7.1.

Notify Property Weaver and Code Contracts

Notify Property Weaver is an AOP tool to convert automatic properties into properties that fire property changed events. I use code contracts, and since that also rewrites the outputted assemblies, I was wondering if there would be any conflict or issue.

Good news, my brief test (code below) tells me all is fine, though some more extensive usage would be required.

Couple of items I'd like to see in future versions of Notify Property Weaver:

  1. Dirty tracking. Which properties have changed. This would require the ability to 'load' a property with an initial value, without firing property changed event.
  2. Post-validation rules and IDataErrorInfo support.

Update: Simon Cropp, author of Notify Property Weaver, suggests a solution to validation and IDataErrorInfo using mixins.

namespace NotifyPropertyWeaverAndCodeContracts
{
	using System;
	using System.ComponentModel;
	using System.Diagnostics.Contracts;
	using NotifyPropertyWeaver;
	using Xunit;

	[ContractClass(typeof(FooContracts))]
	public interface IFoo
	{
		string Bar { get; set; }
	}

	[ContractClassFor(typeof(IFoo))]
	public class FooContracts : IFoo
	{
		public string Bar
		{
			get
			{
				return default(string);
			}
			set
			{
				Contract.Requires<ArgumentException>(!string.IsNullOrWhiteSpace(value));
			}
		}
	}

	public class Foo : IFoo, INotifyPropertyChanged
	{
		[NotifyProperty]
		public string Bar { get; set; }

		public event PropertyChangedEventHandler PropertyChanged;
	}

	public class FooTests
	{
		private readonly Foo _foo;

		public FooTests()
		{
			_foo = new Foo();
		}

		[Fact]
		public void When_set_Bar_to_null_Then_should_throw()
		{
			Assert.Throws<ArgumentException>(() => _foo.Bar = null);
		}

		[Fact]
		public void When_set_Bar_to_empty_Then_should_throw()
		{
			Assert.Throws<ArgumentException>(() => _foo.Bar = string.Empty);
		}

		[Fact]
		public void When_set_Bar_to_a_value_Then_should_raise_property_changed()
		{
			bool eventRaised = false;
			_foo.PropertyChanged += (sender, args) => eventRaised = args.PropertyName == "Bar";
			_foo.Bar = "a";
			Assert.True(eventRaised);
		}
	}
}

PowerShell script to restore a project's packages and tools

Requires nuget.exe to be in the solution root and commited to source control.

# Tools
.\NuGet.exe i DotCover -s \\myserver\Dev\NuGetPackages -o Tools
.\NuGet.exe i StyleCopCmd -s \\myserver\Dev\NuGetPackages -o Tools

# Dependencies
$packageConfigs = Get-ChildItem . -Recurse | where{$_.Name -eq "packages.config"}
foreach($packageConfig in $packageConfigs){
  Write-Host "Restoring" $packageConfig.FullName
  .\nuget.exe i $packageConfig.FullName -o Source\Packages
}