Ant Tasks for Git

Ant has tasks for CVS and Subversion, but none that I could find for Git. I threw together these simple Ant macros to get started:

<macrodef name = "git">
    <attribute name = "command" />
    <attribute name = "dir" default = "" />
    <element name = "args" optional = "true" />
        <echo message = "git @{command}" />
        <exec executable = "git" dir = "@{dir}">
            <arg value = "@{command}" />

<macrodef name = "git-clone-pull">
    <attribute name = "repository" />
    <attribute name = "dest" />
        <git command = "clone">
                <arg value = "@{repository}" />
                <arg value = "@{dest}" />
        <git command = "pull" dir = "@{dest}" />

The first one, “git” just runs git with whatever command you provide to it (clone, pull, etc) along with any arguments you pass to it. Clone:

<git command = "clone">
        <arg value = "git://" />
        <arg value = "ojunit" />

And pull:

<git command = "pull" dir = "repository_path" />

(Other git command will likely work, these are just the ones I needed)

The second one, “git-clone-pull” uses the first one to clone a repository then pull from it. This effectively clones the repository if it hasn’t already been cloned, otherwise it pulls. However, since ant is fairly limited in what sorts of conditional execution you can perform, it just does both (clone will fail if it’s already been cloned, and pull will always be executed, even immediately after a the initial clone). Obviously not ideal, but it works, and I couldn’t figure out a better way without writing actual code.

<git-clone-pull repository="git://" dest="ojunit" />

There is plenty of room for improvement, but I suspect a proper Ant task written in Java is the right way to go.

"Mark Old As Read" for NetNewsWire

I recently heard about an RSS reader (can’t remember which) that had a feature to mark all messages older than a certain threshold as “read”. I thought this was an incredibly useful feature, since I often forget to check my feeds for days at a time, and end up with hundreds of unread items that I don’t have time to read.

Luckily my current RSS reader, NetNewsWire, has AppleScript built in, so I whipped up this script that prompts for the number of days you want to keep as unread, and marks the rest as read.

tell application "NetNewsWire"
    display dialog "How many days old to mark read?" default answer "7"
    set numDays to text returned of result
    set threshold to (current date) – (numDays * days)
    set isRead of (headlines of subscriptions where (isRead is equal to false and date published < threshold)) to true
end tell

A Better BugMeNot Bookmarklet

BugMeNot is a great little service for bypassing the registration process for websites that really shouldn’t require it (ahem, The bookmarklet brings up BugMeNot for the current website you’re viewing, and gives you login/password pairs which you can then copy and paste.

But wouldn’t it be better if it automagically filled in the username and password for you? I thought so, so I wrote a few lines of code in the form of a bookmarklet and a JSONP web service to do this.

BugMeNot doesn’t provide an API so I had to do a little screen scraping with Hpricot. They also try to obfuscate the usernames and passwords by shifting the characters by some offset calculated from a “key” then Base64 encoding the string, and prepending 4 characters. Luckily their obfuscation was no match for a single line of Ruby:

def bmn_decode(input, offset)
  # decode base64, strip first 4 chars, convert chars to ints, substract offset, convert back ints back to chars
  input.unpack("m*")[0][4..-1].unpack("C*").map{|c| c – offset }.pack("C*")

The bookmarklet makes the request via an injected <script> tag. When it’s callback gets called it finds the most likely input elements for the username and password and fills them in with the result.

The Rails app consists of a single action that makes a request to for the specified site, extracts and decodes the usernames and passwords, and picks the one with the highest rating. It then returns the result as JSON wrapped in a function callback (i.e. JSONP)

I’m not going to post the location of the live JSONP web service since BugMeNot limits the number of requests you can make, but the code is available on GitHub.