|
| 1 | +#!/usr/bin/env perl |
| 2 | +# |
| 3 | +# |
| 4 | +use strict; |
| 5 | +use warnings; |
| 6 | +use Carp; |
| 7 | +use Config::Any; |
| 8 | +use Data::Dump; |
| 9 | +use File::Slurp; |
| 10 | +use FindBin; |
| 11 | +use Getopt::Long; |
| 12 | +use Git::Repository; |
| 13 | + |
| 14 | +use lib "$FindBin::Bin/../lib"; |
| 15 | +use WWW::Bugzilla; |
| 16 | + |
| 17 | +my $verbose; |
| 18 | +my $debug; |
| 19 | + |
| 20 | +# ------------------------------------------------------------------------ |
| 21 | +sub update_bugzilla { |
| 22 | + my $cfg = shift; |
| 23 | + my $info = shift; |
| 24 | + my $set = shift; |
| 25 | + |
| 26 | + my $bz = WWW::Bugzilla->new( |
| 27 | + server => $cfg->{bugzilla}{server}, |
| 28 | + email => $cfg->{bugzilla}{user}, |
| 29 | + password => $cfg->{bugzilla}{pass}, |
| 30 | + bug_number => $set->{bug} |
| 31 | + ) || croak "Cannot open bz - $!"; |
| 32 | + |
| 33 | + my $header = sprintf( "Git commit: %s/commitdiff/%s\n", $cfg->{gitweb}, $info->{rev} ); |
| 34 | + if ( scalar( @{ $info->{diff} } ) > 50 ) { |
| 35 | + |
| 36 | + # big diff - we skip the diff |
| 37 | + $bz->additional_comments( |
| 38 | + join( "\n", $header, @{ $info->{info} }, '', @{ $info->{log} }, '----', @{ $info->{diffstat} } ) ); |
| 39 | + } |
| 40 | + else { |
| 41 | + |
| 42 | + # otherwise we do the whole thing |
| 43 | + $bz->additional_comments( join( "\n", $header, @{ $info->{all} } ) ); |
| 44 | + } |
| 45 | + |
| 46 | + $bz->change_status("fixed") if ( $set->{action} eq 'fixes' ); |
| 47 | + $bz->change_status("closed") if ( $set->{action} eq 'closes' ); |
| 48 | + |
| 49 | + $bz->commit; |
| 50 | + |
| 51 | + printf( "[%d] %s %s [%s]\n", $set->{bug}, $info->{rev}, $info->{log}[0], $set->{action} ); |
| 52 | +} |
| 53 | + |
| 54 | +# ------------------------------------------------------------------------ |
| 55 | +sub find_bugzilla_references { |
| 56 | + my $info = shift; |
| 57 | + my $cfg = shift; |
| 58 | + |
| 59 | + my @results; |
| 60 | + my $action = ''; |
| 61 | + my $bugid; |
| 62 | + foreach my $line ( @{ $info->{log} } ) { |
| 63 | + $line = lc($line); |
| 64 | + if ( $line =~ /(closes|fixes|references):?\s*(?:bug(?:zilla)?)?\s*\#?(\d+)/ ) { |
| 65 | + $action = $1; |
| 66 | + $bugid = $2; |
| 67 | + } |
| 68 | + elsif ( $line =~ /\b(?:bug(?:zilla)?)\s*\#?(\d+)/ ) { |
| 69 | + $action = 'references'; |
| 70 | + $bugid = $1; |
| 71 | + } |
| 72 | + else { |
| 73 | + next; |
| 74 | + } |
| 75 | + |
| 76 | + # remap actions |
| 77 | + $action = 'closes' if ( $action =~ /^fix/ ); |
| 78 | + |
| 79 | + push( @results, { bug => $bugid, action => $action } ); |
| 80 | + ##printf( "%s\n\taction = %s bugid = %s\n", $info->{rev}, $action, $bugid ); |
| 81 | + } |
| 82 | + return @results; |
| 83 | +} |
| 84 | + |
| 85 | +# ------------------------------------------------------------------------ |
| 86 | + |
| 87 | +sub git_commit_info { |
| 88 | + my $git = shift; |
| 89 | + my $rev = shift; |
| 90 | + |
| 91 | + my @lines = $git->run( 'show', '-M', '-C', '--patch-with-stat', '--pretty=fuller', $rev ); |
| 92 | + |
| 93 | + my $info = { |
| 94 | + rev => $rev, |
| 95 | + info => [], |
| 96 | + log => [], |
| 97 | + diffstat => [], |
| 98 | + diff => [], |
| 99 | + all => [@lines], # deliberate copy |
| 100 | + }; |
| 101 | + |
| 102 | + while ( my $line = shift @lines ) { |
| 103 | + last if ( $line =~ /^$/ ); |
| 104 | + push( @{ $info->{info} }, $line ); |
| 105 | + } |
| 106 | + |
| 107 | + while ( my $line = shift @lines ) { |
| 108 | + last if ( $line =~ /^---\s*$/ ); |
| 109 | + push( @{ $info->{log} }, $line ); |
| 110 | + } |
| 111 | + |
| 112 | + while ( my $line = shift @lines ) { |
| 113 | + last if ( $line =~ /^$/ ); |
| 114 | + push( @{ $info->{diffstat} }, $line ); |
| 115 | + } |
| 116 | + |
| 117 | + # all the rest |
| 118 | + $info->{diff} = \@lines; |
| 119 | + |
| 120 | + return $info; |
| 121 | +} |
| 122 | + |
| 123 | +# ------------------------------------------------------------------------ |
| 124 | + |
| 125 | +sub walk_git_commits { |
| 126 | + my $git = shift; |
| 127 | + my $cfg = shift; |
| 128 | + |
| 129 | + my $lastrev = $git->run( 'rev-parse', $cfg->{lastref} ); |
| 130 | + my $headrev = $git->run( 'rev-parse', $cfg->{branch_head} ); |
| 131 | + |
| 132 | + return if ( $lastrev eq $headrev ); |
| 133 | + |
| 134 | + my @revs = $git->run( 'rev-list', '--topo-order', '--no-merges', ( $lastrev . '..' . $headrev ) ); |
| 135 | + |
| 136 | + foreach my $rev ( reverse(@revs) ) { |
| 137 | + my $info = git_commit_info( $git, $rev ); |
| 138 | + |
| 139 | + #ddx($info); |
| 140 | + #dd( $info->{info}, $info->{log}[0] ); |
| 141 | + my @sets = find_bugzilla_references( $info, $cfg ); |
| 142 | + foreach my $set (@sets) { |
| 143 | + update_bugzilla( $cfg, $info, $set ); |
| 144 | + } |
| 145 | + } |
| 146 | + return $headrev; |
| 147 | +} |
| 148 | + |
| 149 | +# ------------------------------------------------------------------------ |
| 150 | + |
| 151 | +# main |
| 152 | +{ |
| 153 | + my $config; |
| 154 | + |
| 155 | + GetOptions( |
| 156 | + 'config=s' => \$config, |
| 157 | + 'debug!' => \$debug, |
| 158 | + 'verbose!' => \$verbose, |
| 159 | + ) or die "Incorrect options"; |
| 160 | + die "No config file given\n" unless ( $config and -f $config ); |
| 161 | + my $cfg = ( values( %{ Config::Any->load_files( { files => [$config], use_ext => 1 } )->[0] } ) )[0]; |
| 162 | + |
| 163 | + die "No git_dir specified\n" unless ( $cfg->{git_dir} ); |
| 164 | + $cfg->{lasttag} ||= $cfg->{git_dir} . '/refs/tags/BugzillaDone'; |
| 165 | + $cfg->{branch_head} ||= 'HEAD'; |
| 166 | + |
| 167 | + $cfg->{lastref} = -f $cfg->{lasttag} ? read_file( $cfg->{lasttag} ) : 'HEAD'; |
| 168 | + chomp( $cfg->{lastref} ); |
| 169 | + |
| 170 | + my $git = Git::Repository->new( git_dir => $cfg->{git_dir} ) || die "No valid git repo\n"; |
| 171 | + |
| 172 | + my $newlast = walk_git_commits( $git, $cfg ); |
| 173 | + if ($newlast) { |
| 174 | + write_file( $cfg->{lasttag}, $newlast ); |
| 175 | + } |
| 176 | +} |
0 commit comments