IgorShare Thoughts and Ideas

Consulting and Training

Can TFS be useful if the sources are in Subversion (SVN)? Or how to run integration and nightly builds on TFS from SVN?

Posted by Igor Moochnick on 12/26/2010

This is the question that I’ve been asked multiple times during my Microsoft ALM tools presentations and trainings. So I’ve decided to post a series of articles that will show the path that we had to walk to teach the TFS to run our builds from SVN.

This will be helpful for you especially if you have a similar configuration – source code location in SVN but the development tools are still Visual Studio and MSBuild. The projects are combined into the solution files (.sln), etc… I hope you got the picture.

[Disclaimer: To continue you should be familiar with the TFS build workflow and the basics of the TFS administration]

After quite some search I was unable to find any activities that enable the communication with the SVN (except for a custom MSBuild Svn task) so I’ve decided to build my own…

There are a bunch of ways to connect to SVN. One of them is the SharpSvn. The same library that AnkhSvn plugin for Visual Studio is using. The usage of SharpSvn was pretty straightforward.  You can see in the code below how SvnController is implemented to allow either clean checkout or an update of a previously checked out code structure.

The most complicated part with SharpSvn was to figure out how to provide the credentials that should be passed from the build configuration. I’ll talk about how to pass the credentials from the build configuration in the next post but, for the moment, let’s concentrate on the SharpSvn. Notice that, in order to allow the https:// urls, you need to subscribe to SslServerTrustHandlers and accept the server certificate. This is a sample code that doesn’t do any certificate validations (which is totally fine if you’re working only inside the company security boundaries) but in real life you have to implement a real certificate validation.

As soon as the code was tested, I’ve checked in the custom activity along with all the SharpSvn assemblies into the CustomActivities folder under the BuildProcessTemplates. Don’t forget to update the Build Controller to check for custom activities in that folder as well.

The default build template (DefaultTemplate.xaml) was updated as follows:

image

This allows to check out the code from the Svn into the same location as the TFS. The code that will be checked out from TFS, if needed, may override or complete the Svn source tree.

Note: after the custom activity was added to the workflow (and each time you edit the workflow) remember to add the “;assembly=SvnActivityLib” like this:

xmlns:local="clr-namespace:SvnActivityLib;assembly=SvnActivityLib"
In this case the TFS build host will be able to correctly resolve the custom activity assembly.
namespace SvnActivityLib
{
    [BuildActivity(HostEnvironmentOption.All)]
    [BuildExtension(HostEnvironmentOption.All)]
    [Designer(typeof(SvnActivityDesigner))]
    public sealed class SvnActivity : CodeActivity
    {
        [RequiredArgument]
        public InArgument<string> SvnToolPath { get; set; }

        [RequiredArgument]
        public InArgument<string> SvnCommandArgs { get; set; }

        [RequiredArgument]
        public InArgument<string> DestinationPath { get; set; }

        [RequiredArgument]
        public InArgument<string> SvnPath { get; set; }

        [RequiredArgument]
        public InArgument<svncredentials> SvnCredentials { get; set; }

        protected override void Execute(CodeActivityContext context)
        {
            TrackMessage(context, "Starting SVN action");

            string destinationPath = context.GetValue(this.DestinationPath);
            string svnPath = context.GetValue(this.SvnPath);
            string svnToolPath = context.GetValue(this.SvnToolPath);
            string svnCommandArgs = context.GetValue(this.SvnCommandArgs);
            SvnCredentials svnCredentials = context.GetValue(this.SvnCredentials);
            new SvnControl.SvnController().UpdateAll(destinationPath,
                svnPath, svnCredentials.Username, svnCredentials.Password, true);

            TrackMessage(context, "End SVN action");
        }

        private static void TrackMessage(CodeActivityContext context, string message)
        {
            context.Track(new BuildInformationRecord<buildmessage>
            {
                Value = new BuildMessage()
                {
                    Importance = BuildMessageImportance.Normal,
                    Message = message,
                },
            });
        }

        public sealed class BuildMessage
        {
            public String Message { get; set; }
            public BuildMessageImportance Importance { get; set; }
        }
    }

    public class SvnController
    {
        public void UpdateAll(string destinationFolder, string svnPath, string username, string password, bool cleanCheckout)
        {
            using (SvnClient client = new SvnClient())
            {
                client.Authentication.Clear();

                client.Authentication.UserNamePasswordHandlers +=
                    delegate(object sndr, SvnUserNamePasswordEventArgs e)
                        {
                            e.UserName = username;
                            e.Password = password;
                        };
                client.Authentication.SslServerTrustHandlers +=
                    delegate(object sndr, SvnSslServerTrustEventArgs e)
                        {
                            e.AcceptedFailures = e.Failures;
                            e.Save = true;
                        };

                if (cleanCheckout)
                {
                    // Checkout the code to the specified directory
                    client.CheckOut(new Uri(svnPath), destinationFolder);
                }
                else
                {
                    // Update the specified working copy path to the head revision
                    SvnUpdateResult result;
                    client.Update(destinationFolder, out result);
                }
            }
        }
    }
}

If this didn’t work for you continue reading the sad part of the story – it didn’t work for me either. It works great if the process runs anywhere but in TFS build host (try the attached command line client or unit tests). I’ve tried a lot of things to make it work, but still, the TFS fails to resolve the SharpSvn correctly with the following error:

Could not load file or assembly ‘SharpSvn, Version=1.6015.1625.10417, Culture=neutral, PublicKeyToken=d729672594885a28’ or one of its dependencies. The system cannot find the file specified.

I’ve passed a lot of hurdles to even get to this point. The incompatibility of the .NET versions were solved with the following section in the TFSBuildServiceHost.exe.config file on the Buld Host

    <startup uselegacyv2runtimeactivationpolicy="true">
        <supportedruntime version="v4.0" />
    </startup>

I’ve added the probing path in the same file. I’ve registred the SharpSvn in the GAC and rewrote the activity to work with the assemblies from the GAC. I’ve manually force-changed the probing path for the activity AppDomain to probe in the place where all the parts of the SharpSvn were located. Etc… etc… etc…

Bottom line – I’m pretty sure that it is possible to solve this problem, but sometime in the future. The Svn integration for my project I’ve solved in a different way – see the following post …

Advertisements

9 Responses to “Can TFS be useful if the sources are in Subversion (SVN)? Or how to run integration and nightly builds on TFS from SVN?”

  1. […] Can TFS be useful if the sources are in Subversion (SVN)? Or how to run integration and nightly buil… […]

  2. Add the following section in the config file. It worked for me.

  3. Sorry, this section:

  4. startup useLegacyV2RuntimeActivationPolicy=”true”
    supportedRuntime version=”v4.0″ sku=”.NETFramework,Version=v4.0″/
    startup

  5. it seems like this forum doesn’t take xml inside comments 🙂

  6. Mike said

    Put SharpSVN in the GAC. I had it only in the BuildAssemblies folder and kept getting an error similar to yours. When I added it to the GAC I then got the .NET version issue that you resolved. Your post helped me solve that issue. Now I can checkout from SVN and build it on my TFS system. This is awesome now that I can let the devs use whatever source control they want. As long as I can pull their repo when I’m creating the workspace all is good!

    • Awesome! Thanks.

      • Mike said

        You’re welcome! Another fact I learned while working on this today. In Visual Studio (I confirmed this in 2012) you’ll need to have the references point to the 32bit version of SharpSVN. If you use the 64bit it will create really weird errors. In my build assemblies folder I left the 64bit version as my build machine is 64bit and did not like the 32bit dlls. Very odd, but it has seemingly sorted out my issues.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: