I’m using TeamCity, MSBuild, and SVN together to do some pretty cool stuff around continuous integration and automated builds.  It’s been working well, untouched, for well over a year!  But all of a sudden my deployment process failed. Checking the logs with TeamCity, I found this:

[11:18:06]:   Error validating server certificate for ‘https://[xxx].com:443′:
[11:18:06]:    – The certificate is not issued by a trusted authority. Use the
[11:18:06]:      fingerprint to validate the certificate manually!
[11:18:06]:   Certificate information:
[11:18:06]:    – Hostname: [xxx]
[11:18:06]:    – Valid: from Mon, 29 Mar 2010 17:44:04 GMT until Thu, 26 Mar 2020 17:44:04 GMT
[11:18:06]:    – Issuer: [xxx]
[11:18:06]:    – Fingerprint: [xxx]
[11:18:06]:   (R)eject, accept (t)emporarily or accept (p)ermanently? svn: OPTIONS of ‘https://[xxx]': Server certificate verification failed: issuer is not trusted (https://[xxx].com)

The best part about this was that, for a change, this error message was very clear.  Although I don’t know why the certificate failed.  Did it change or expire?  Did one of the network guys make a change to something?  I didn’t really care.  I just wanted to get my build working again.

Here is the line of MSBuild Script that caused the issue: (I’m using the SVN stuff from MSBuild Community Tasks)

<SvnClient Command="ls $(SvnRepository)/tags/$(BuildNumber) Username="$(SvnUserName)" password="$(SvnPassword)" ContinueOnError="true">
  <Output TaskParameter="ExitCode" PropertyName="Value"/>
</SvnClient>

Since the Command of SvnClient accepts any SVN command (in other words, anything that I can type at a command prompt), I searched the SVN Documentation.  It wasn’t hard to find that I just needed to update to this:

<SvnClient Command="ls $(SvnRepository)/tags/$(BuildNumber) –non-interactive –trust-server-cert" Username="$(SvnUserName)" password="$(SvnPassword)" ContinueOnError="true">
  <Output TaskParameter="ExitCode" PropertyName="Value"/>
</SvnClient>

This worked great but my build still failed.  That is because I have other SVN actions in my script and they don’t seem to know that I accepted the certificate. However, my other tasks are not using the very generic and flexible SvnClient Command, they are using tasks such as SvnCopy or SvnCommit, etc.  For instance:

<SvnCopy SourcePath="$(SvnRepository)/$(SvnProjectLocation)/"
    DestinationPath="$(SvnRepository)/tags/$(BuildNumber)"
    Message="Auto-tagging Revision: $(BuildNumber)"
    Username="$(SvnUserName)" password="$(SvnPassword)" />

So how could I tell SVN to accept the certificate.  I checked the MSBuild Community Tasks documentation, took a guess and got lucky on the first try…sweet.  I added this:

    <SvnCopy SourcePath="$(SvnRepository)/$(SvnProjectLocation)/"
        DestinationPath="$(SvnRepository)/tags/$(BuildNumber)"
        Message="Auto-tagging Revision: $(BuildNumber)"
        Username="$(SvnUserName)" password="$(SvnPassword)"
        Arguments="–non-interactive –trust-server-cert" />

I added the Arguments to all of my other SVN Tasks in my script and it worked perfectly!

I’ve been working on continuous integration and automated builds/deployment lately.  I put together a MSBuild script that does a whole bunch of typical stuff including getting code from my repository, compiling, and running tests.  I also want to create a Tag in my Subversion repository so I know which code was deployed.  That wasn’t too hard to do either.  The tricky part was that I want my build to fail if I try to make a Tag that already exists!

I fumbled around with this for a while trying a wide variety of ideas.  In the end, it turned out to be pretty simple.  There may be better ways to accomplish this, but here is how I am doing it…

I’m using the SvnClient task (it’s part of the MSBuild.Community.Tasks download) and executing a command:  “ls $(SvnRepository)/tags/$(SvnTagName)”.  Of course, my MSBuild script already has the variables $(SvnRepository) which is the path to my repository and $(SvnTagName) which is the name of the Tag I hope to create.  You may have to tweak this stuff depending on the layout of your SVN repository.  The command “ls” will actually list the contents of my Tag.  Since I know my tag just has a few folders in it (it isn’t a recursive task), it isn’t too harmful if it succeeds.  If the Tag does not exist in my repository, the command actually fails.  Oddly, I actually want this task to fail!  Yes, in my case, I am hoping the task does not find the Tag with the specified name.  When the task is executed, it returns an ExitCode which is 0 for success or 1 for failure.  Next I just check the returned code and if it is successful I generate an error.  Sure this feels a bit odd.  I don’t love using this kind of logic, hoping for an error to occur. I’d much rather have some subversion command I could execute to see if the Tag exists but I couldn’t find anything like that.  Here is the actual xml from my Build file to check for the Tag:

<Target Name="CheckSvn">
  <Message Text="*** Checking to see if the tag already exists"/>
  <!-- this task will look up the tag supplied.  
    If it exists it will return a code of 0 
    Technically this is a success but for this instance, 
    it is a failure because we don't want to find it! -->
  <SvnClient Command="ls $(SvnRepository)/tags/$(SvnTagName)" 
             Username="xxx" password="xxx" ContinueOnError="true">
    <Output TaskParameter="ExitCode" PropertyName="CommandResult"/>
  </SvnClient>
  <Message Text="*** ExitCode = $(CommandResult)"/>
  <Error Condition="$(CommandResult)=='0'" Text="This Tag already exists!"/>
  <Message Text="*** Finished Checking to see if the tag already exists"/>
</Target>

Working with MSBuild is becoming a love/hate relationship for me.  I really love the power of what you can do to automate tasks.  But it can be a real pain to write, test, and debug these scripts.  I hope my sharing this code will make your life a little easier.