SVN, Hudson & MSBuild - Building code on post commit

SVN, Hudson and MSBuild - Revision control repository
SVN, Hudson and MSBuild - Continuous Integration

This is the third and last installment in the series I’m writing about SVN, Hudson and MSBuild.

Today I’m going to show you the last piece that actually makes the whole thing work. We could call this the plumbing. The piece lies within a specific SVN folder related to your project. It’s called hooks. The path to the hooks folder is this:

Project’s hooks folder before the set upFigure 1 - Project’s hooks folder before the setup

As you can see there are some template files ( .tmpl ). The one we’re going to use to inform Hudson that it’s time to build the code just committed to the repository is the file post.commit.tmpl. Make a copy of this file and change its extension to .bat since it’ll be used by SVN to execute some commands. The file should be named post-commit.bat.

Open the .bat file and add this code at the end:

SET REPOS=%1
SET REV=%2
SET CSCRIPT=C:\WINDOWS\system32\cscript.exe
SET VBSCRIPT=C:\svn\post-commit-hook-hudson.vbs
SET SVNLOOK=C:\Program Files\VisualSVN Server\bin\svnlook.exe
SET HUDSON=http://leniel-pc:8080/
"%CSCRIPT%" "%VBSCRIPT%" "%REPOS%" %REV% "%SVNLOOK%" %HUDSON%

Note above that we’re setting some vars and pointing to some specific files:

- CSCRIPT points to cscript.exe file that should be present in your Windows system32 folder.

- VBSCRIPT points to to the post-commit-hook-hudson.vbs file and its code is as follows:

repos   = WScript.Arguments.Item(0)
rev     = WScript.Arguments.Item(1)
svnlook = WScript.Arguments.Item(2)
hudson  = WScript.Arguments.Item(3)

Set shell = WScript.CreateObject("WScript.Shell")

Set uuidExec = shell.Exec(svnlook & " uuid " & repos)
Do Until uuidExec.StdOut.AtEndOfStream
  uuid = uuidExec.StdOut.ReadLine()
Loop
Wscript.Echo "uuid=" & uuid

Set changedExec = shell.Exec(svnlook & " changed --revision " & rev & " " & repos)
Do Until changedExec.StdOut.AtEndOfStream
  changed = changed + changedExec.StdOut.ReadLine() + Chr(10)
Loop
Wscript.Echo "changed=" & changed

url = hudson + "subversion/" + uuid + "/notifyCommit?rev=" + rev
Wscript.Echo url

Set http = CreateObject("Microsoft.XMLHTTP")
http.open "POST", url, False
http.setRequestHeader "Content-Type", "text/plain;charset=UTF-8"
http.send changed

- SVNLOOK points to svnlook.exe file that comes with VisualSVN Server (see part 1 of this series for more details about it).

- HUDSON points to your Hudson server address. Change it accordingly.

With it all configured we should be ready to get an automatic build when code is committed to the repository.

To test your environment, change any file already versioned and commit it. Open Hudson in your browser and watch a new build start automatically.

If you look in Hudson’s build Console Output you’ll see that the build was initiated by an SCM change.

That’s all!

This is how your SVN project hooks folder should look like now:

Project’s hooks folder after the set upFigure 2 - Project’s hooks folder after the setup

Can you spot another .bat file in the folder? It’s the pre-revprop-change.bat. I’ve been using it so that I can modify the commit’s log message/comment when I forget to mention something or to correct spelling. More info about this file can be seen in this StackOverflow question: What is a pre-revprop-change hook in SVN and how do I create it?

Tracking Last.fm profile visits with Google Analytics

I'm a big fan of analytics data; therefore I try to collect stats about anything – like visitors that check my web pages/profiles. It’s no different with my Last.fm profile.

Last.fm profile page uses BBCode markup that is a lightweight markup language used to format posts in many message boards and unfortunately it has no JavaScript support.
Google Analytics (GA) on the other hand makes use of JavaScript code when it comes to the tracking code one must include in their website. This causes a frustrating experience to anyone that tries to integrate the GA tracking code in places where JavaScript code isn’t accepted.

While searching for a solution some time ago I didn’t find a way to solve this impedance, but yesterday after putting a little bit more caring on my Google search query, I finally found a way of filling the gap so that both services could talk to each other: NoJSStats, short for No JavaScript Stats is the missing piece. It’s a free web service. Basically NoJSStats uses Google App Engine to track visitors using just an image or anything that allows external resource requests in the website you want to track.

Here’s what you need to do in order to get analytics data popping up at your Google Analytics account in respect to your Last.fm profile:

First, let’s configure GA side…

1 - Go to Google Analytics and click the Gear icon at the top right of any page and create a new Web Property:

 Creating a new Last.fm Google Analytics Web PropertyFigure 1 - Creating a new Last.fm Google Analytics Web Property

2 - Name the Web Property and in the Web Site URL field enter your Last.fm profile URL and click Create property:

Filling Last.fm Web Property with correct dataFigure 2 - Filling Last.fm Web Property with the correct data

3 - Take note of the Web Property ID assigned to your new Web Property:

Checking Web Property ID that’ll be used when configuring Last.fm profileFigure 3 - Checking Web Property ID that’ll be used when configuring Last.fm profile

Now let’s configure Last.fm side:

4 - Edit your Last.fm profile clicking in the Edit link located in the right side of your profile page:

Edit button used to get access to Last.fm profile dataFigure 4 - Edit button used to get access to Last.fm profile data

5 - Head to the “About You” field and add this line at the end:

[img]http://nojsstats.appspot.com/UA-1234567-89/www.last.fm/user/leniel[/img]

Do not forget to click the Save details button.

Two things related to that img URL deserve a note here:

You’re using the Last.fm GA Web Property ID, for example UA-1234567-89 taken from step 3 above.
You’re pointing to your Last.fm profile at the last part of the URL.

DO CHANGE these two parts accordingly.

Interesting enough to try is that if you copy & paste the URL from step 5 in your browser, you get this result:

http://www.google-analytics.com/__utm.gif?utmwv=1&utmn=37172618&utmsr=-&utmsc=-&utmul=-&utmje=0&utmfl=-&utmdt=-&utmhn=www.last.fm/user/leniel&utmr=&utmp=&utmac=UA-1234567-89&utmcc=__utma%3D167996071.415430387.1319322154.1319322154.1319322154.1%
3B%2B__utmb%3D167996071%3B%2B__utmc%3D167996071%3B%2B__utmz%
3D167996071.1319322154.2.2.utmccn%3D%28direct%29%7Cutmcsr%3D%28direct%
29%7Cutmcmd%3D%28none%29%3B%2B__utmv%3D167996071.189.24.47.122%3B

Awesome! NoJSStats does its magic and all GA data -- every single item is assembled and packed into the Request URL's query string (everything after the '?'). For more technical details, see this StackOverflow question: Why does Google Analytics use __utm.gif?

This is everything you need to do. Easy, no?

If you have problems
I have tested this workflow and it’s indeed working as expected. In case you can’t get it working:

If you check the Tracking Code status in GA it says: "Tracking Not Installed".

Last.fm Tracking Code statusFigure 5 - Last.fm Tracking Code status

It doesn't matter... really. That's because we aren't using the standard JavaScript code provided by GA. GA is waiting to detect the presence of that code. We are not using this approach and so it's OK if it reports "Tracking Not Installed".

Analytics data appear within a time frame. It's not real-time at the moment.

If you still do not see any data after a day or two, please leave a comment in this post and I’ll try to help.

Final notes
You should also see data on GA about your own visits to your profile! You can block this from being counted. Check this: How do I exclude my internal traffic from reports?

One of the advantages of using this approach instead of others like FLAG counter that I usually see all over Last.fm profiles is that you keep your data private and get way more stats from your visitors.

Last.fm is missing visitor Analytics features in its current incarnation. I sure hope it implements something interesting in this area.

For now, I hope you get a better insight of how your profile is performing in the internetz.