Setting up Subversion Integration

Lighthouse allows for basic integration with your source control tool. Although a more specialized SCM tool may be desirable, it can be useful to send changeset notices to your Lighthouse dashboard. Lighthouse’s source control support is general enough that other tools should work, but this example will focus on subversion.

Authentication

This example beacon will use a token for authentication. Go to the My Profile link in the top right of the page. There you can create a new API token for your account. You can specify the project if you want, but make sure full access is selected.

Keywords in your changeset revision logs.

Once your subversion beacon is setup, you can now add keywords to your log messages that will update tickets. To simply refer to a ticket:

Prototype new feature [#15]

The #15 will add this log message as a comment to ticket #15. You can also change various properties:

New feature is implemented and tested [#15 tagged:committed responsible:rick milestone:"Launch" state:resolved]
  • tagged – Adds the tag(s) to the ticket, but does not replace them.
  • responsible – Sets the user responsible for the ticket. Use responsible:none to clear it.
  • milestone – Sets the milestone for the ticket. Use milestone:none to clear the milestone.
  • state – Sets the ticket state. Valid values are: new, open, hold, resolved, and invalid.

Setting up the script

As far as beacons go, this 73-line script is about as simple as they get. This script should work as a post-commit hook. It assumes your svn server has ruby with the standard library installed, and access to the svnlook and curl commands.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#!/usr/bin/ruby
require 'yaml'
require 'cgi'
require 'net/http'
require 'uri'

# configure multiple project settings below
SVNLOOK    = `which svnlook`.strip
LOG_FILE   = '/tmp/svn-hooks.log'

OPTIONS = {
  # default token
  :token   => 'CHANGEME',
  # tokens for other members of the team
  # name is matched against svn user name
  :users   => { 'bob' => "bob's token", 'fred' => "fred's token" },
  # full url, please
  :account => 'http://activereload.lighthouseapp.com',
  :project => 2, # REPLACE
  :prefix  => /^trunk/ # OPTIONAL
}

def gather_and_post(repo_path, revision, options)
  if options[:prefix]
    commit_dirs_changed = `#{SVNLOOK} dirs-changed #{repo_path} -r #{revision}`
    return unless commit_dirs_changed.split(/\n/)[0] =~ options[:prefix]
  end

  commit_author  = `#{SVNLOOK} author #{repo_path} -r #{revision}`.chop
  commit_log     = `#{SVNLOOK} log #{repo_path} -r #{revision}`
  commit_date    = `#{SVNLOOK} date #{repo_path} -r #{revision}`
  commit_changed = `#{SVNLOOK} changed #{repo_path} -r #{revision}`

  commit_changes = commit_changed.split("\n").inject([]) do |memo, line| 
    if line.strip =~ /(\w)\s+(.*)/
      memo << [$1, $2]
    end
  end.to_yaml

  changeset_xml = <<-END_XML
  <changeset>
    <title>#{CGI.escapeHTML("%s committed changeset [%d]" % [commit_author, revision])}</title>
    <body>#{CGI.escapeHTML(commit_log)}</body>
    <changes>#{CGI.escapeHTML(commit_changes)}</changes>
    <revision>#{CGI.escapeHTML(revision.to_s)}</revision>
    <changed-at type="datetime">#{CGI.escapeHTML(commit_date.split('(').first.strip)}</changed-at>
  </changeset>
END_XML

  token = options[:users][commit_author] || options[:token]
  url = URI.parse('%s/projects/%d/changesets.xml' % [options[:account], options[:project]])
  
  req = Net::HTTP::Post.new(url.path) 
  req.basic_auth token, 'x' # to ensure authentication
  req.body = changeset_xml.strip
  req.set_content_type('application/xml')
  
  res = Net::HTTP.new(url.host, url.port).start {|http| http.request(req) }
  case res
    when Net::HTTPSuccess, Net::HTTPRedirection
      ## all good, we submitted...
    else
      res.error!
  end
end

begin
  # feel free to add multiple calls below
  gather_and_post ARGV[0], ARGV[1], OPTIONS
rescue
  %x{echo "repo:#{ARGV[0]} rev: #{ARGV[1]}" > #{LOG_FILE}}
  %x{echo "Error: #{$!.to_s.gsub('`',"'")} trace:#{caller}" >> #{LOG_FILE}}
end

(permalink to beacon script)

First, make sure the basic paths at the top to your commands are correct. Then, make sure the #gather_and_post call has the correct arguments. The :token, :account, and :project options are self-explanatory. The :prefix option should be a regex that is matched against the paths in the commit. If it’s provided and matches the commit, the script proceeds with the ping.

You can also run this script manually to test. Assuming this is in /svn/my-repo/hooks/post-commit, you’d run it with: ruby /svn/my-repo/hooks/post-commit /svn/my-repo 1000 (fill in your own repository path and revision number).

The important SVN bits of this approach were yanked from: webtypes > Subversion post commit hook using basecamp API. Also, thanks goes to James Cox for taking the initiative in writing a better version with the Net/HTTP library.

Tags: , , , , , ,

« How do I send emails to Lighthouse? | Help | Using Beacons and API Tokens »

Visit us in the forums

If you can't find what your looking for here, visit us in the forums.