rss
twitter
Find out what I'm doing, Follow Me :)

Export only modified files on SVN commit

13
 

At my work place we were setting up local development environment for all the developer. Our setup was pretty standard. Local development environment for all developer, Subversion (SVN) for version control and development/test server for QA. To make this setup painless for developers, one of the main requirement was to export the code to our development server when it was committed to SVN. To do something like that, SVN has what they call "hooks". These are essentially scripts that are run on certain repository events. The events are:

  • start-commit - This is run before commit transaction begins, can be used to do special permission checking.
  • pre-commit - This is run when the transaction is complete, but before commit. Often used to validate things such as a non zero length log message.
  • post-commit - This is run after the transaction has been committed and a revision number created. Can be used for sending emails, or backing up repository or in our case exporting the changed files.
  • pre-revprop-change - Runs before a revision property change. Can be used to check permissions.
  • post-revprop-change - Runs after a revision property change. Can be used to email or backup these changes.

More information on hooks here http://svnbook.red-bean.com/en/1.1/ch05s02.html

So, with this knowledge I thought it should be easy enough to just do an SVN export command in the post-commit hook, right? Well, I should know better. Nothing ever is quite that simple. But, hopefully after you read this post, it will be for you. I will go step by step on how to get it working and the issues I faced.

If you are someone like me who doesn't like to read long posts, go ahead and download the script and play with it. If it works for you, great! Otherwise keep reading :)

Step 1: Install SVN

I installed VisualSVN Server. It's much better than Tigris in terms of management and maintenance. It came bundled with Apache web server, so you are ready to use it over http or https without any additional setup/configuration. It supports windows authentication as well. From their on, I'm assuming you will be able to create a repository and project(s) and import it into your eclipse environment (with subclipse plugin).

Our repository structure looked like this:

myRepository
Coldfusion
branch
tags
trunk
Flex
branch
tags
trunk

Step 2: The "Hook"

As I mentioned, the hooks are simply scripts that are run on certain SVN event. These script files are to be placed in [repository]\hooks folder. You can find a sample script for all the events in this folder. In Windows, these are .bat files.

TIP: Hooks are by repository and not by projects/folder. So, you can only have one hook per event per repository.

 

So, let's create a simple script to test a post-commit hook.

Create a post-commit.bat file in the [repository]\hooks folder (if a file exists, rename it to .bak) and put the following code it it:

svnlook changed [repository path]

 

This will give use a listing of the changed resources in current revision. Let's run this bat file from the command line first to make sure it's working ok. If you get an error like "Expected FS format '2'; found format '3'" Chances are you have multiple version of svnlook on your system. In my case I was getting this error because I had an older version of Tigris SVN Server installed as well. I had to change the environment path to point to the Visual SVN Server bin directory. 

Any time you change an environment path, the change won't take effect until you start a new command window. In some cases you may also need to restart the machine for the change to take effect. 

 

If everything is good you should get a list of changes in the format:

A ColdFusion/
A ColdFusion/branch
A ColdFusion/trunk
...

Where "A" is the action [A=Add; D=Delete; U=Update etc.], and next is the changed resource name.

If you configured SVN server over https and didn't install a "real" certificate, you will be asked to accept the validity of the certificate.

Ok, so the script works from the command prompt. Let's test it from Eclipse. Make a change and commit. But hold on! How would we know if anything happened? Well, let add some logging in there. Let's change the script a little bit so we can log every thing that happens in the script. 

Rename this script to post-commit-run.bat. Create another script called post-commit.bat and write this line in there:

call "%~dp0post-commit-run.bat" %* > %1/hooks/post-commit.log 2>&1

 

This will dump the output to post-commit.log instead of screen. Now, do a commit and see if the log file got created with the svnlook output. If it did, congratulations! Your environment is setup and now you can proceed to real task at hand. Here are couple of issues I got stuck with at this point. 

  • We need to define the path for all our executables used in the script. In our case here we are using svnlook, so we need to specify the path for it. Since it's in our environment path we can set the path to that: SET PATH=%PATH%

You *must* add any paths for command line tools you plan to use since SVN does not include the Windows %PATH% environment variable for security reasons. http://svnbook.red-bean.com/en/1.1/ch05s02.html

  • Now here is a tricky one. I installed the VisualSVN over https without a "real" SSL key. By "real", I mean a SSL key purchased from a verified source, and not the default VisualSVN self signed certificate. In this case you are asked to accept the validity of the certificate when you access it the first time. When running the script from the command line we can inter-actively do this. Since the response is cached per user account, we need to make sure we set the response for the account under which the scripts when executed from eclipse. When running from Eclipse, the script is run under the "Network Service" account. Visual SVN service is installed and run under this account by default for increased security. I could not find a way to get this to work under this account. The only way I could get it to work is change Visual SVN to run under the local system account. Here is the information on how to do this http://www.tentaclesoftware.com/blog/archive/2009/04/28/37.aspx.

Now let's look at the complete code in the hook.

@ECHO OFF & setLocal EnableDelayedExpansion
SET REPOS=%1
SET REV=%2

SET repoURL=https://[host]:[port]/[path to repository]
SET webSiteRoot=[network path (e.g. \\something\) or your mapped drive path to website root ]
SET logDir=%REPOS%/hooks

SET PATH=%PATH%

 

First we are setting ECHO OFF to disable screen output of commands. EnableDelayedExpansion is required when using variables inside a loop. More on that later.

%1 and %2 are arguments that SVN passes to the script through the hook. %1 is the repository path (not URL) and %2 is the current revision number.

Then set some local variables to be used in the script. Nothing fancy here. One thing to be careful about in the batch programming is the variable assignment. There should be no space between the variable name and "=". I wasted lot of time to figure out that one!!

And then, you set path of where executables are located.

Make sure there is no space between variable name and "=".

 

Now the actual export part. In my case, I wanted to find out if there was a change made to the ColdFusion trunk and export it to the website root. So, let's look at the code to do that.

SET removeStr=Coldfusion/trunk/
FOR /F "tokens=1,2 eol=¬ delims= " %%A in ('svnlook changed %1 -r %2') DO (
ECHO %%B |FIND "Coldfusion/trunk" > null
IF ERRORLEVEL = 0 (
SET dirLocation=%%B
REM ECHO current location !dirLocation!
SET newLocation=!dirLocation:%removeStr%=!
IF %%A == D (
SET delLocation=!newLocation:/=\!
REM ECHO delete file %webSiteRoot%\!delLocation!
DEL %webSiteRoot%\!delLocation!
) ELSE (
REM ECHO exporting to !newLocation!
svn export --force -r %2 "%repoURL%/!dirLocation!" %webSiteRoot%/!newLocation!
    )
    )
)

 

First get a list of changes to the current revision through svnlook and loop through them using the 'FOR" loop. "tokens=1,2 delims= " will create 2 variables %%A and %%B representing action and resource name. Next we use "find" to see if the change was made to the ColdFusion trunk. "> null" is just used to suppress the output. If a match is found (ERRORLEVEL = 0), that means this is a change in the ColdFusion trunk. 

Now here is the tricky part. Let's say the change was made in folder [webroot]\mySite\somefolder\files. This is where I need to export all the changed files in the proper directory structure. The svnlook gives me the changed resource in the format "ColdFusion\trunk\mySite\somefolder\files". So, I need to get rid of the "ColdFusion\trunk" part to set my export location. That's what we do in line "SET newLocation=!dirLocation:%removeStr%=!"

"!" is used around the variable name for delayed evaluation (evaluation during each loop). Anything after the ":" is matched and replaced with the string after "=". 

Then we delete or export based on the action that was taken on the resource.  

That's all there is to it! Next, modify the script to not export certain commit. Some times, you want to commit so that your changes are in the repository but you don't want to export it to the server. I will leave it to you guys to expand the script to do that. 

Download Script.