@@ 6,20 6,68 @@
# ditz sync: synchronize issues with Trac
#
# Usage:
-# 1. add a line "- trac-sync" to the .ditz-plugins file in the project root.
+# 1. Add a line '- trac-sync' to the .ditz-plugins file in the project root.
+# 2. Call 'ditz trac'
require 'rubygems'
require 'trac4r'
require 'ditz'
module Ditz
+
class Issue
+ field :trac_id, :ask => false
+
def log_at time, what, who, comment
add_log_event([time, who, what, comment || ""])
self
end
end
+
+ class ScreenView
+ add_to_view :issue_summary do |issue, config|
+ " Trac ID: #{issue.trac_id || 'none'}\n"
+ end
+ add_to_view :issue_details do |issue, config|
+ "Trac ID: #{issue.trac_id || 'none'}\n"+
+ "Trac URL: #{config.trac_sync_url || 'none'}\n"
+ end
+ end
+
+
+ class HtmlView
+ add_to_view :issue_summary do |issue, config|
+ next unless issue.trac_id
+ [<<EOS, { :issue => issue }]
+<tr>
+ <td class='attrname'>Trac ID:</td>
+ <td class='attrval'><%= issue.trac_id %></td>
+</tr>
+EOS
+ end
+
+ add_to_view :issue_details do |issue, config|
+ next unless issue.trac_id
+ [<<EOS, { :issue => issue, :config => config }]
+<h2>Trac Synchronization</h2>
+<table>
+ <tr>
+ <td class='attrname'>Trac ID:</td>
+ <td class='attrval'><%= issue.trac_id %></td>
+ </tr>
+ <tr>
+ <td class='attrname'>Trac URL:</td>
+ <td class='attrval'><%= config.trac_sync_url %></td>
+ </tr>
+</table>
+EOS
+ end
+ end
+
+
+
+ # A utility class for handling Trac tickets and milestones
class TracUtil
# If new types or dispositions are added to either ditz or trac, they'll need
# to be mapped here
@@ 52,31 100,52 @@ module Ditz
DSTATUS_TSTATUS[ :unstarted ] = "assigned"
DSTATUS_TSTATUS[ :paused ] = "assigned"
+
def initialize( project, config, trac )
@project, @config, @trac = project, config, trac
end
+
def equal?( ticket, issue )
ticket.summary == issue.title
end
+
+ # Find matches between tickets and issues. First matches by
+ # ticket.trac_id, and if that fails, by title
def pair( tickets )
rv = []
issues = @project.issues.clone
for ticket in tickets
- issue = issues.find { |i| equal?( ticket, i ) }
- issues.delete(issue) if issue
+ issue = issues.find { |i| ticket.id == i.trac_id }
+ issue = issues.find { |i| equal?( ticket, i ) } unless issue
+ if issue
+ if !issue.trac_id.nil? and (issue.trac_id != ticket.id)
+ puts "ERROR! Found a match by title, but the IDs don't match!!"
+ puts " issue has #{issue.trac_id}, ticket has #{ticket.id}"
+ # TODO do something useful here
+ end
+ puts "Found ticket/issue match #{ issue.trac_id ? "by id" : "by name"}"
+ issues.delete(issue)
+ issue.trac_id = ticket.id if issue.trac_id.nil?
+ end
rv << [ticket,issue]
end
+ # Now put the left-over, unmatched issues in with a nil ticket
+ # TODO check here that none of these issues have trac_ids.
for issue in issues
rv << [nil,issue]
end
rv
end
+
+ # Syncs ditz issues -> Trac
def create_tickets( issues )
end
+
+ # Syncs Trac tickets -> issues
def create_issues( tickets )
tickets.each do |t,i| # i will always be nil
# trac4r doesn't yet support resolution
@@ 101,7 170,8 @@ module Ditz
:component => t.component,
:status => TSTATUS_DSTATUS[ t.status ],
:disposition => resolution,
- :references => []
+ :references => [],
+ :trac_id => t.id
}, [@config, @project])
@project.add_issue( issue )
@@ 110,6 180,7 @@ module Ditz
end
end
+
def change_status status, issue
if issue.status != status
old_status = issue.status
@@ 119,6 190,7 @@ module Ditz
nil
end
+
def group_by_time(changelog)
rv = {}
changelog.each do |log|
@@ 129,6 201,7 @@ module Ditz
rv.sort
end
+
def update_issues( pairs, trac )
pairs.each do |ticket, issue|
puts "Working on #{ticket.id}/#{issue.id[0,4]}"
@@ 186,15 259,18 @@ module Ditz
end
end
+
class Config
field :trac_sync_url, :prompt => "URL of Trac project (without the login/xmlrpc)"
field :trac_sync_user, :prompt => "The Trac user ID that has XMLRPC permissions"
field :trac_sync_pass, :prompt => "The Trac password for the account"
end
+
+
class Operator
- operation :sync, "Sync with a Trac repository"
- def sync( project, config )
+ operation :trac, "Sync with a Trac repository"
+ def trac( project, config )
unless config.trac_sync_url
STDERR.puts( "Please run 'ditz reconfigure' and set the Trac URL" )
return