After the SharpSvn fiasco (described in the previous post), I’ve decided temporarily put this dependency loading issue aside and, instead of running native Svn client, to use one of the command-line Svn interfaces. I’ve tried to keep the activity interface generic enough so you can configure it to use any of the available command-line source control clients (not only Svn but Cvs, Git, Mercurial, etc…)
As a first step the following 4 common and mostly changed parameters were identified:
- Source location (usually a Url)
- Destination for check-out
- Username
- Password
This list defined all the input parameters for the Svn custom activity (plus extra one that carries the the command line template):
[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; }The rest of the activity takes care of building a command line from a provided template by replacing the placeholders with the provided parameter values and executing the external process:
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); string svnCommand = Regex.Replace(svnCommandArgs, "{uri}", svnPath); svnCommand = Regex.Replace(svnCommand, "{destination}", destinationPath); svnCommand = Regex.Replace(svnCommand, "{username}", svnCredentials.Username); TrackMessage(context, "svn command: " + svnCommand); // Never reveal the password! svnCommand = Regex.Replace(svnCommand, "{password}", svnCredentials.Password); if (File.Exists(svnToolPath)) { var process = Process.Start(svnToolPath, svnCommand); if (process != null) { process.WaitForExit(); process.Close(); } } TrackMessage(context, "End SVN action"); } } }To provide a nice UI element with the Svn logo, a custom designer was added (note the “Designer” attribude on top of the SvnActivity class). Here is the designer’s XAML:
<sap:ActivityDesigner x:Class="SvnActivityLib.SvnActivityDesigner" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:sap="clr-namespace:System.Activities.Presentation;assembly=System.Activities.Presentation" xmlns:sapv="clr-namespace:System.Activities.Presentation.View;assembly=System.Activities.Presentation" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" Collapsible="False" ExpandState="False" UseLayoutRounding="False" d:DesignHeight="22" d:DesignWidth="200" mc:Ignorable="d"> <sap:ActivityDesigner.Icon> <DrawingBrush> <DrawingBrush.Drawing> <ImageDrawing> <ImageDrawing.Rect> <Rect Location="0,0" Size="16,16" ></Rect> </ImageDrawing.Rect> <ImageDrawing.ImageSource> <BitmapImage UriSource="Resources\Subversion.png" ></BitmapImage> </ImageDrawing.ImageSource> </ImageDrawing> </DrawingBrush.Drawing> </DrawingBrush> </sap:ActivityDesigner.Icon> </sap:ActivityDesigner>
The Build Template, hosting this activity, will requre the following addition to it’s Arguments list:
as well as a couple of extra entries in the metadata:
After the activity was build and checked in to the CustomActivities location (see the previous post) I was able to configure new values in the build definition:
Notice the “Svn arguments” parameter that provides a command line template.
In the build definition metadata I’ve configured only 2 parameters to appear during the build submission (SvnPath and SvnCredentials) so they can be configured in both places (during the editing and the submission), but the rest of the parameters are configured to appear only during the build editing step. This allows any developer to provide his own “patch” source location and his own credentials during the build submission. If your security model doesn’t allow this – make sure to modify the metadata section accordingly.
This is it for now – I’ll talk about the whole credentials mystery in the next post.