-#!/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 <Andrew@ClearSCM.com>
-
-=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 <bugshost>] [-login <login email>]
- [-jiraserver <server>]
- [-username <username>] [-password <password>]
- [-bugids bugid,bugid,... | -file <filename>]
- [-[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 <bugid>[,<bugid>,...] or -file <filename>'
- 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