# HG changeset patch # User Sean Russell # Date 1264565559 18000 # Tue Jan 26 23:12:39 2010 -0500 # Node ID d268075640c638c629dc7344735cc9d1536005a0 # Parent 56cc8b5971e9a1637be964ec0888b39b433ae875 Adds support for saving Trac ID, for more accurate syncing diff --git a/trac-sync.rb b/trac-sync.rb --- a/trac-sync.rb +++ b/trac-sync.rb @@ -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 + [< issue }] + + Trac ID: + <%= issue.trac_id %> + +EOS + end + + add_to_view :issue_details do |issue, config| + next unless issue.trac_id + [< issue, :config => config }] +

Trac Synchronization

+ + + + + + + + + +
Trac ID:<%= issue.trac_id %>
Trac URL:<%= config.trac_sync_url %>
+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 @@ 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 @@ :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 @@ end end + def change_status status, issue if issue.status != status old_status = issue.status @@ -119,6 +190,7 @@ nil end + def group_by_time(changelog) rv = {} changelog.each do |log| @@ -129,6 +201,7 @@ 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 @@ 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