Bug tracking and SVN hooks for Mantis

I came across an interesting article on Task Driven Development the other day. I came to it via the recent Linus on branching story on Slashdot.

One of the first things mentioned in that article is that you need an issue tracking system and it needs to be integrated with your source control system. After a moment's consideration, I said to myself, "That's a good idea." I've been thinking lately about getting back into doing some open-source stuff as well as some educational projects for myself, and something like that might be helpful. In the past, I usually didn't bother to track bugs and features in my personal projects, unless you count post-it notes and unorganized text files, because, well, they were personal projects and it just didn't seem important. However, now I'm used to living and dying by Jira and am fully aware of how crazy it is to think I'm just going to remember all the bugs I find and potential features I think up.

So, to that end, I installed a copy of Mantis on my site. I settled on Mantis, after a good 5 minutes of careful research, based largely on the facts that the article recommended it and that the requirements were low enough that I could run it on my cheap-ass shared hosting account. Fortunately, it seems nice enough so far - not radically different from Bugzilla or Jira, but OK.

Anyway, I came across a slight snag in setting up the Mantis integration with my Subversion repository. You see, Mantis supplies a script that can be called from Subversion's post-commit hook that will update any issues referenced in the commit message. However, the script is written on the assumption that Mantis and SVN live on the same box. In fact, that is not the case - SVN lives on my local Windows 7 box, while Mantis lives on my hosting provider's box, to which I do not have shell access (yes, I'm that cheap).

With a little Googling, I was able to turn up a few useful resources to help me get this set up. This article gave a nice overview of SVn hooks and Mantis integration, while this one provided a nice perspective for SVN on Windows. However, I had to cobble these together to make it work on my system.

My main problem with the documentation I had was my lack of SSH access to the box on which I'm running Mantis - all the articles I found assumed you had it. So the first order of business was a simple gateway PHP script to wrap the command-line Mantis script that would normally be run via SSH. Therefore I quickly banged out this do_checkin.php script and dropped it in a web-accessible directory.

<?php
# Very cheap security measure, simply so that just *anybody* can't post issues.
if ($_POST['secret'] !== 'somePasswordHere') {
   exit;
}

$cmd = 'php checkin.php <<< ' . escapeshellarg($_POST['message']);
system($cmd);

Next was the Subversion hook. I'm using SlikSVN, and it turns out that this will only execute hook scripts that are .exe or .bat files. I know this because I tried to write a Powershell hook script and it didn't work. But no worries - it's easy enough to write a wrapper batch post-commit.bat file such as this:

@ECHO OFF

SET REPOS=%1
SET REV=%2

CD %REPOS%\hooks
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe %REPOS%\hooks\post-commit.ps1 "%REPOS%" "%REV%"

For the Powershell script, it was easy enough to just port the BASH script posted in the previously linkd article. However, instead of calling the Mantis checkin.php script by SSH, I actually just used wget to send a POST to the do_checkin.php posted above.

[System.Reflection.Assembly]::LoadWithPartialName("System.Web") | Out-Null

$REPOS = $args[0]
$REV = $args[1]

"$REPOS $REV"

$svnlook = 'C:\Program Files\SlikSvn\bin\svnlook.exe'

$auth = & $svnlook author -r $REV $REPOS
$dt = & $svnlook date -r $REV $REPOS
$changed = & $svnlook changed -r $REV $REPOS
$changed = $changed -join "`n"
$log = & $svnlook log -r $REV $REPOS
$log = $log -join "`n"
$data = "Changeset [${REV}] by $auth, $dt
$log
$changed"

$postData = "secret=somePasswordHere&message="
$postData += [System.Web.HttpUtility]::UrlEncode($data)

C:\Cygwin\bin\wget.exe -O req.out --post-data=$postData http://yoursite.com/path/to/mantis/scripts/do_checkin.php
rm req.out

This seems to work fine in my testing. We'll see if that changes once I start adding in bugs and features for my projects and then writing the code to complete them. Hopefully that process will be somewhat easier for being more organized.

Ron Jeffries rocks

Last night I spent some time browsing Ron Jeffries web site. While I've seen a few of the items on Ron's site before, I never really stopped to look through - and boy was that a mistake! It turns out that Ron has a number of very interesting and entertaining articles on his site. I particularly recommend his classics section.

Included in the "classics" is an article entitled We Tried Baseball and It Didn't Work. Unless you're lucky enough to have only ever worked in high-quality organizations, which really, really had their act together, this article will probably ring true on some level. It also nicely sums up a number of Ron's other articles on the use and abuse of the term "Agile".

This kind of reminded me of when my current team first started to "do Scrum". For the first few months, the entire team agreed that it just wasn't working. Of course, we weren't exactly following the book in our application of Scrum: our "stand-up" meetings often lasted 30 to 60 minutes; we didn't always deliver "done-done" software at the end of a sprint; our sprints started at a month long, and when we failed to finish our tasks by then, we pushed the date. So "Scrum wasn't working" for us, if by Scrum you mean "some process that involves a daily meeting." But we had to do Scrum, or come up with some other "Agile" process, because if it's not Agile, then it's not good. I mean, how could all those industry pundits be wrong?

Fortunately, we've figured things out a bit better now. We're much closer to a real Agile process and it's working better for us. Of course, we're not really there yet, but we're making progress. And, really, that's what matters.

Sort of agile

While catching up on Jason Gorman's blog today, I came across this entry, which linked to the Half-Arsed Agile Manifesto. In a word, it's awesome.

That manifesto made me think back to when I started my current job. I was reading through the process documentation for our parent company (whose processes the spin-off I work for inherited). The documents referred to themselves as describing the "agile" process the company employed. But as I read through them, with the descriptions of requirements documents and formal hand-off procedures, I just kept thinking, "This sounds a lot more like waterfall than any agile process I've ever heard of." It's always nice to know you're not alone in having experiences like that.

Sadly, while my team's current Scrum-like process is considerably more agile, we still have some vestiges of that. For instance, the stated expectation that our initial estimates will be 90% accurate based on what is (very) generously described as "80% definition" by product management. So we're only mostly sure what we're building, but we're almost positive how long it will take. I'm still not sure how the math is supposed to work out on that. Fortunately, we don't take that expectation too seriously.

Miscellaneous Windows utilities

In honor of Raymond Chen, it's link clearance time! This one is devoted to Windows utilities I've discovered over the last few weeks.

First up is the Elevation PowerToys. This is just a bunch of scripts related to privilege elevation. In particular, I like the "elevate" command included with this. It's kind of like sudo for UAC - you use it to elevate to admin with a UAC prompt rather than having to use runas.

Next is Shell Link Extension. As the name suggests, this is just a shell extension for creating symlinks and hardlinks. It's a bit rough around the edges, but it's nice not to have to drop down to a cmd.exe prompt.

On a more eye-candy note, I recently discovered PuttyTray, an improved version of PuTTY. It includes systray integration and supports configurable window transparency. The eye-candy support is fairly limited compared to the options available in the UNIX world (how many configuration options did aterm have just related to transparency?), but overall it's not too bad.

Similarly, I also just discovered PowerShell Glass. Basically, it's just a utility to enable Aero glass in PowerShell terminals, rendering the window transparent. While it's a nice idea, it doesn't seem to work that well. Sure, the window turns transparent, but I can't read what's in it half the time. The text and background coloring isn't quite right and isn't configurable to any great degree, so depending on what's behind your terminal window, it can be very hard to read.

Edit: Apparently I spoke too soon on the PSGlass colors. I Googled it yesterday and noticed a comment that the transparency seems to work best with the dark blue Aero color scheme. So I tried changing my color scheme and, lo and behold, it worked. Turns out I had my desktop set to the "frost" scheme, which looks like it's probably the worst possible setting for PSGlass. The "twilight" (dark blue) setting does seem to be the most readable, but it's a little too blue for my taste. I ended up settling on "slate", which still gives you readable text when the window is active, but is a little less "in your face" color-wise.

Fixing MediaMonkey playlists in RockBox

Yesterday I tried creating a playlist in MediaMonkey and synchronizing it with my Sansa e280 running RockBox. I never really bothered with trying to sync playlists to my MP3 player before, because syncing, well, anything that's not an iPod using third-party software is a little iffy. Needless to say, my fears were well founded.

While MediaMonkey's syncing feature is nice, I ran into several problems. First, and most obvious, MediaMonkey didn't like the layout of my media directory. You see, I have a main "Music" folder which contains a bunch of artist and album sub-folders, but also a bunch of miscellaneous MP3s at the top-level. MediaMonkey didn't see those MP3s for some reason. After the sync, they just weren't there - I had to copy them over manually.

My other problem was a combination of the playlists themselves, how RockBox on the Sansa works, and where I'm putting my music on the Sansa. A couple of weeks ago, I "upgraded" my Sansa by buying a 16GB microSDHC card for it to complement the 8GB of internal flash storage. This resulted in a change of organization - I now keep all my actual music on the microSD card and put podcasts and other spoken material on the internal storage. Turns out that this messes with MediaMonkey's assumptions about playlists.

There are several issues here. The first is that when MediaMonkey syncs to the microSD card, it sees it as a separate device and creates the playlists accordingly. The result is that the playlists use absolute paths within the microSD card. However, the real absolute path for files on the microSD card starts with the device path, i.e. <microSD1>. So, basically, the paths in the playlist are wrong for RockBox.

The second issue is that the playlists get synced onto the microSD card. This isn't really a problem, just now what I want. I want the files synced to the main RockBox playlist folder on the Sansa's internal storage.

The third issue was with those assorted MP3s in my root "Music" directory. Even though I set the sync path to <Path:2> in MediaMonkey's sync configuration, which should mirror the directory structure on my hard drive, when I ran the sync, the assorted files on my playlist got copied into a "Music" folder on the microSD card. I'm not sure why. But as long as that's the case, I decided to "solve" that problem by just moving the rest of the assorted files there too. They weren't being synced anyway, so why not.

I decided to solve the playlist path and location problems with a simple Powershell script. You can download a copy here. It simply reads your playlists, adjusts the paths, and moves them to the main device storage. The code is below:

$source = "MUSICSD"
$device = "Sansa e280"
$listDir = "Playlists"

function findDriveByLabel {
   $drives = [System.Io.DriveInfo]::GetDrives()
   foreach ($drv in $drives) {
      if ($args[0] -eq $drv.VolumeLabel) {
         return $drv.RootDirectory
      }
   }
}

$sd = findDriveByLabel $source
$dev = findDriveByLabel $device
$sdDir = [System.Io.Path]::Combine($sd, $listDir)
$devDir = [System.Io.Path]::Combine($dev, $listDir)

$lists = ls $sdDir

foreach ($lst in $lists) {
   $outFile = [System.Io.Path]::Combine($devDir, $lst.Name)
   $outFile
   Get-Content $lst.FullName | ForEach-Object {
      "/<microSD1>" + $_.Replace("\", "/")
   } | Out-File -Encoding utf8 -Width 1000 $outFile
   rm $lst.FullName
}

$remLists = ls $sdDir
if ($remLists -eq $Null) {
   rmdir $sdDir
}

So far, this part is working fine. I'm still not 100% happy with the sync experience, though. The missing files is the part that really bugs me. I'll have to do a little experimenting at some point and see if I can make it work. Only problem is that it takes a while to write 16GB of data to the device....