Never break a build again with the new Gated Check-in feature from Visual Studio Team System 2010

Breaking the current team build is not a good thing. This will cause many people to lose time while the build is being fixed. So how can you avoid this? One way is by only checking-in on isolated branches, which allow you first to test if everyone’s changes build together.

But, remember that Team System Source Control has that one-click branching feature, called shelving? Well, now if you check in VSTS can take your changeset and merge it on a shelve with the current version in source control. It will then launch a build (using Team Build) and if this is a successful build (depends on the build) will allow you to check-in.

Let’s have a look at this:

To try this you first have to have something in Source Control. For example this simple library project and class:

   1: namespace Lib
   2: {
   3:   public class Code
   4:   {
   5:     public int Test(int i, int j)
   6:     {
   7:       return i + j;
   8:     }
   9:   }
  10: }

I’ve also added two unit tests in a test project:

   1: [TestMethod()]
   2: public void Test_2And3_Returns5()
   3: {
   4:   Code target = new Code(); 
   5:   int i = 2;
   6:   int j = 3;
   7:   int expected = 5;
   9:   int actual;
  10:   actual = target.Test(i, j);
  11:   Assert.AreEqual(expected, actual);
  12: }
  14: [TestMethod()]
  15: public void Test2And4Returns10()
  16: {
  17:   Code target = new Code();
  18:   int i = 2; 
  19:   int j = 4; 
  20:   int expected = 6; 
  21:   int actual;
  22:   actual = target.Test(i, j);
  23:   Assert.AreEqual(expected, actual);
  24: }

Make sure all of this is currently checked in.

Now go to Team Explorer, right-click on Builds and choose “New Build Definition…” The Build Definition dialog should open. Call your build GatedCheckIn (although the name doesn’t matter, I’ll be using it later).


Now go the the Trigger tab; here is where the new Gated Check-in can be selected:


Next go to the Workspace tab, and select the folder containing your solution (both the library and test projects).


Next come Build Defaults, the usual (you might need to create a shared folder so you can map to it here; make sure the Build Service account can access it):


I’ve left everything in the Process tab to its default value, but you might want to double check the Automated Tests. Team Build will run all tests in all assemblies whose name contains test.


Also note the Fail Build On Test Failure setting; you can change it to true to ensure nobody can check-in with failing tests.


If you’re using the Layer Diagram, you can also validate it during the build.

And finally Retention Policy, where you can choose whatever you like.

Close the window and save your changes.

You might want to test your build definition by queuing it. It should work:


Ok, now let’s make a change to our code that would break the build by failing one on the tests:

   1: namespace Lib
   2: {
   3:   public class Code
   4:   {
   5:     public int Test(int i, int j)
   6:     {
   7:       return i - j;
   8:     }
   9:   }
  10: }

I’ve simply changed to + sign into a – sign.

Now open the Pending Changes window and click the Check In button. The Gated Check-in dialog should appear:


Note the Shelveset and Build definition. If you have more than one build definition, things change, we’ll look at this later.

Also note the Preserve my pending changes locally. This works exactly the same way as normal shelving, so if you leave it checked, your changes are left on your machine. If the build fails, everything is the same. If you uncheck it, your changes will be shelved, but then you will have to make sure you reconcile back with the server’s version after the check in succeeds.

Ok, Click the Build Changes button. A build should start. Open the Build Explorer if it doesn’t do so automatically:


The tests failed, so TFS decides that the check in failed:



Change the code back to use the + sign. Check In. This should now succeed.


Now this really become interesting if two developers check-in conflicting changes. We can emulate this using two workspaces. So I’ve setup another workspace mapped to the same project. To do this, go to Source Control Explorer and open the Workspace: drop-down list:


Select Workspaces…, this should open the Manage Workspaces dialog (you will have other names for any workspaces you might already have):


Now pick a name, and in the Working Folders select your project mapping, one for the server, one for on your disk. For example:


Then get all files locally (TFS will suggest this anyway). To switch between the workspaces you simply select it from the “Workspace:” dropdown list.

In one workspace rename the Test method in the Code class to Add. Use the Rename feature to ensure all references are renamed as well… Run all tests, they should succeed. Don’t check in yet.

Open your solution in the other workspace. Change the implementation of the Test method like this:

   1: public class Code
   2: {
   3:   public int Test(int i, int j)
   4:   {
   5:     if (j == 4) j = 8;
   6:     return i + j;
   7:   }
   8: }

And change the unit tests as well:

   1: [TestMethod()]
   2: public void Test2And4Returns10()
   3: {
   4:   Code target = new Code();
   5:   int i = 2; 
   6:   int j = 4; 
   7:   int expected = 10; 
   8:   int actual;
   9:   actual = target.Test(i, j);
  10:   Assert.AreEqual(expected, actual);
  11: }

Run all tests. They should succeed. Then check In. A build should start. While that is running check in your other changes (other workspace). Because your first check in is still running you shouldn’t see any conflict with your other changes. Actually, both builds should work, because the second build will merge the first build’s changes in the workspace, and they auto-Merge…

Let’s try to really break it now. In one workspace add another method to call the Add method (reconcile the workspace first, or do a recursive get-latest version):

   1: public class Code
   2: {
   3:   public int Add(int i, int j)
   4:   {
   5:     if (j == 4) j = 7;
   6:     return i + j;
   7:   }
   9:   public void OtherMethod()
  10:   {
  11:     int x = Add(3, 2);
  12:   }
  13: }

Then in the other workspace rename the Add method again. Check this workspace in, then the other workspace. Because the extra method doesn’t get the Add method renamed, the build will break:


Merge your code with the current check in code, and retry. This should now succeed.