X-Git-Url: https://defaria.com/gitweb/?a=blobdiff_plain;f=audience%2FJIRA%2Fjiradep.pl;fp=audience%2FJIRA%2Fjiradep.pl;h=a8ce4c47e45fbf93790fd8ca345244044fd963a9;hb=dbbd4da4b211ec44626801cbf41476980d211751;hp=0000000000000000000000000000000000000000;hpb=b7aaa136bd4ad909325b0e5da59daa607233ae27;p=clearscm.git diff --git a/audience/JIRA/jiradep.pl b/audience/JIRA/jiradep.pl new file mode 100644 index 0000000..a8ce4c4 --- /dev/null +++ b/audience/JIRA/jiradep.pl @@ -0,0 +1,372 @@ +#!/usr/bin/perl +use strict; +use warnings; + +=pod + +=head1 NAME jiradep.pl + +Update Bugzilla dependencies (Dependencies/Blockers/Duplicates and Related), +transfering those relationships over to any matching JIRA issues. + +=head1 VERSION + +=over + +=item Author + +Andrew DeFaria + +=item Revision + +$Revision: #1 $ + +=item Created + +Thu Mar 20 10:11:53 PDT 2014 + +=item Modified + +$Date: 2014/05/23 $ + +=back + +=head1 SYNOPSIS + + $ jiradep.pl [-bugzillaserver ] [-login ] + [-jiraserver ] + [-username ] [-password ] + [-bugids bugid,bugid,... | -file ] + [-[no]exec] [-linkbugzilla] [-relinkbugzilla] + [-verbose] [-help] [-usage] + + Where: + + -v|erbose: Display progress output + -he|lp: Display full help + -usa|ge: Display usage + -[no]e|xec: Whether or not to update Bugilla. -noexec says only + tell me what you would have updated. + -use|rname: Username to log into JIRA with (Default: jira-admin) + -p|assword: Password to log into JIRA with (Default: jira-admin's + password) + -bugzillaserver: Machine where Bugzilla lives (Default: bugs-dev) + -jiraserver: Machine where Jira lives (Default: jira-dev) + -bugi|ds: Comma separated list of BugIDs to process + -f|ile: File of BugIDs, one per line + -linkbugzilla: If specified and we find that we cannot translate + a Bugzilla Bud ID to a JIRA Issue then create a + remote link for the Bugzilla Bug. (Default: + do not create Bugzilla remote links). + -relinkbugzilla: Scan current Remote Bugzilla links and if there + exists a corresponding JIRA issue, remove the + Remote Bugzilla link and make it a JIRA Issue + link. + -jiradbhost: Host name of the machine where the MySQL jiradb + database is located (Default: cm-db-ldev01) + +=head1 DESCRIPTION + +This script will process all BugIDs translating them into JIRA Issues, if +applicable. It will then determine the relationships of this BugID in Bugzilla - +what it blocks, what it depends on, if it's a duplicate of another bug or if +it has any related links. Those too will be translated to JIRA issues, again, +if applicable. Then the JIRA issue will be updates to reflect these +relationships. + +Note that it's not known at this time what to do for situations where BugIDs +cannot be translated into JIRA issues if such Bugzilla bugs have not yet been +migrated to JIRA. There's a though to simply make a Bugzilla Link but we will +need to keep that in mind and when we import the next project to JIRA these +old, no longer used Bugzilla Links should be converted to their corresponding +JIRA issue. Perhaps this script can do that too. + +=cut + +use FindBin; +use lib "$FindBin::Bin/lib"; + +$| = 1; + +use DBI; +use Display; +use Logger; +use TimeUtils; +use Utils; +use JIRAUtils; +use BugzillaUtils; + +use Getopt::Long; +use Pod::Usage; + +our %opts = ( + exec => 0, + bugzillaserver => $ENV{BUGZILLASERVER} || 'bugs-dev', + jiraserver => $ENV{JIRASERVER} || 'jira-dev', + jiradbhost => $ENV{JIRA_DB_HOST} || 'cm-db-ldev01', + username => 'jira-admin', + password => 'jira-admin', + usage => sub { pod2usage }, + help => sub { pod2usage (-verbose => 2)}, + verbose => sub { set_verbose }, + quiet => 0, + usage => sub { pod2usage }, + help => sub { pod2usage (-verbose => 2)}, +); + +our ($log, %total); + +my %relationshipMap = ( + Blocks => 'Dependencies Linked', + Duplicate => 'Duplicates Linked', + Related => 'Related Linked', +); + +sub callLink ($$$$) { + my ($from, $type, $to, $counter) = @_; + + my $bugzillaType; + + if ($from =~ /^\d+/) { + if ($type eq 'Blocks') { + $bugzillaType = 'is blocked by (Bugzilla)'; + } elsif ($type eq 'Duplicate') { + $bugzillaType = 'duplicate (Bugzilla)'; + } elsif ($type eq 'Related') { + $bugzillaType = 'related (Bugzilla)'; + } # if + } elsif ($to =~ /^\d+/) { + if ($type eq 'Blocks') { + $bugzillaType = 'blocks (Bugzilla)'; + } elsif ($type eq 'Duplicate') { + $bugzillaType = 'duplicate (Bugzilla)'; + } elsif ($type eq 'Related') { + $bugzillaType = 'related (Bugzilla)'; + } # if + } # if + + $total{$counter}++; + + if ($from =~ /^\d+/ && $to =~ /^\d+/) { + $total{'Skipped Bugzilla Links'}++; + + return "Refusing to link because both from ($from) and to ($to) links are still a Bugzilla link"; + } elsif ($from =~ /^\d+/) { + if ($opts{linkbugzilla}) { + my $result = addRemoteLink $from, $bugzillaType, $to; + + $total{'Bugzilla Links'}++ unless $result; + + if ($result eq '') { + return "Created remote $type link between Issue $to and Bug $from"; + } else { + return $result; + } # if + } else { + $total{'Skipped Bugzilla Links'}++; + + return "Refusing to link because from link ($from) is still a Bugzilla link"; + } # if + } elsif ($to =~ /^\d+/) { + if ($opts{linkbugzilla}) { + my $result = addRemoteLink $to, $bugzillaType, $from; + + $total{'Bugzilla Links'}++ unless $result; + + if (!defined $result) { + print "huh?"; + } + if ($result eq '') { + return "Created remote $type link between Issue $from and Bug $to"; + } else { + return $result; + } # if + } else { + $total{'Skipped Bugzilla Links'}++; + + return "Refusing to link because to link ($to) is still a Bugzilla link"; + } # if + } # if + + my $result = linkIssues $from, $type, $to; + + $log->msg ($result); + + if ($result =~ /^Unable/) { + $total{'Link Failures'}++; + } elsif ($result =~ /^Link made/) { + $total{'Links made'}++; + } elsif ($result =~ /^Would have linked/) { + $total{'Links would be made'}++; + } # if + + return; +} # callLink + +sub relinkBugzilla (@) { + my (@bugids) = @_; + + my %mapRelationships = ( + 'blocks (Bugzilla)' => 'Blocks', + 'is blocked by (Bugzilla)' => 'Blocks', + 'duplicates (Bugzilla)' => 'Duplicates', + 'is duplicated by (Bugzilla)' => 'Duplicates', + # old versions... + 'Bugzilla blocks' => 'Blocks', + 'Bugzilla is blocked by' => 'Blocks', + 'Bugzilla duplicates' => 'Duplicates', + 'Bugzilla is duplicated by' => 'Duplicates', + ); + + @bugids = getRemoteLinks unless @bugids; + + for my $bugid (@bugids) { + $total{'Remote Links Scanned'}++; + + my $links = findRemoteLinkByBugID $bugid; + + my $jirafrom = findIssue ($bugid); + + next if $jirafrom !~ /^[A-Z]{1,5}-\d+$/; + + for (@$links) { + my %link = %$_; + + # Found a link to JIRA. Remove remotelink and make an issuelink + if ($mapRelationships{$link{relationship}}) { + my ($fromIssue, $toIssue); + + if ($link{relationship} =~ / by/) { + $fromIssue = $jirafrom; + $toIssue = $link{issue}; + } else { + $fromIssue = $link{issue}; + $toIssue = $jirafrom; + } # if + + my $status = promoteBug2JIRAIssue $bugid, $fromIssue, $toIssue, + $mapRelationships{$link{relationship}}; + + $log->err ($status) if $status =~ /Unable to link/; + } else { + $log->err ("Unable to handle relationships of type $link{relationship}"); + } # if + } # for + } # for + + return; +} # relinkBugzilla + +sub main () { + my $startTime = time; + + GetOptions ( + \%opts, + 'verbose', + 'usage', + 'help', + 'exec!', + 'quiet', + 'username=s', + 'password=s', + 'bugids=s@', + 'file=s', + 'jiraserver=s', + 'bugzillaserver=s', + 'linkbugzilla', + 'relinkbugzilla', + 'jiradbhost=s', + ) or pod2usage; + + $log = Logger->new; + + if ($opts{file}) { + open my $file, '<', $opts{file} + or $log->err ("Unable to open $opts{file} - $!", 1); + + $opts{bugids} = [<$file>]; + + chomp @{$opts{bugids}}; + } else { + my @bugids; + + push @bugids, (split /,/, join (',', $_)) for (@{$opts{bugids}}); + + $opts{bugids} = [@bugids]; + } # if + + pod2usage 'Must specify -bugids [,,...] or -file ' + unless ($opts{bugids} > 0 or $opts{relinkbugzilla}); + + openBugzilla $opts{bugzillaserver} + or $log->err ("Unable to connect to $opts{bugzillaserver}", 1); + + Connect2JIRA ($opts{username}, $opts{password}, $opts{jiraserver}) + or $log->err ("Unable to connect to $opts{jiraserver}", 1); + + if ($opts{relinkbugzilla}) { + unless (@{$opts{bugids}}) { + relinkBugzilla; + } else { + relinkBugzilla $_ for @{$opts{bugids}} + } # unless + + Stats (\%total, $log); + + exit $log->errors; + } # if + + my %relationships; + + # The 'Blocks' IssueLinkType has two types of relationships in it - both + # blocks and dependson. Since JIRA has only one type - Blocks - we take + # the $dependson and flip the from and to. + my $blocks = getBlockers @{$opts{bugids}}; + my $dependson = getDependencies @{$opts{bugids}}; + + # Now merge them - we did it backwards! + for my $fromLink (keys %$dependson) { + for my $toLink (@{$dependson->{$fromLink}}) { + push @{$relationships{Blocks}{$toLink}}, $fromLink; + } # for + } # for + + #%{$relationships{Blocks}} = %$dependson; + + for my $fromLink (keys %$blocks) { + # Check to see if we already have the reverse of this link + for my $toLink (@{$blocks->{$fromLink}}) { + unless (grep {$toLink eq $_} keys %{$relationships{Blocks}}) { + push @{$relationships{Blocks}{$fromLink}}, $toLink; + } # unless + } # for + } # for + + $relationships{Duplicate} = getDuplicates @{$opts{bugids}}; + $relationships{Relates} = getRelated @{$opts{bugids}}; + + # Process relationships (social programming... ;-) + $log->msg ("Processing relationships"); + + for my $type (keys %relationshipMap) { + for my $from (keys %{$relationships{$type}}) { + for my $to (@{$relationships{$type}{$from}}) { + $total{'Relationships processed'}++; + + my $result = callLink $from, $type, $to, $relationshipMap{$type}; + + $log->msg ($result) if $result; + } # for + } # for + } # if + + display_duration $startTime, $log; + + Stats (\%total, $log) unless $opts{quiet}; + + return; +} # main + +main; + +exit; \ No newline at end of file