Add --web and --tools to support other repo's.
[tools.git] / review-tools / gitaddrev
index 64976d184498dbae208051956ebc4a54a83dd103..cf041d4bd78710520c4c15dab3c4bb4a1a49aa0b 100755 (executable)
@@ -1,19 +1,17 @@
 #!/usr/bin/perl
 
+use strict;
+use warnings;
+
 use File::Basename;
 use FindBin;
 
 use OpenSSL::Query::REST;
 use Module::Load::Conditional qw(can_load);
 
-can_load(modules => { OpenSSL::Query::DB => undef });
+can_load(modules => { 'OpenSSL::Query::DB' => undef });
 
 my $rmrev = 0;
-my @reviewers;
-my @nocla_reviewers;
-my @unknown_reviewers;
-my $skip_reviewer;
-my $omccount = 0;
 my @commits;
 my $skip = 0;
 my $matchstr;
@@ -22,10 +20,44 @@ my $found = 0;
 my $num = 0;
 my $refuse = 0;
 my $prnum = 0;
-my $trivial = 0;
+my $verbose = 0;
+my $WHAT = 'openssl';
 
 my $query = OpenSSL::Query->new();
 
+my @reviewers;
+my @nocla_reviewers;
+my @unknown_reviewers;
+my $skip_reviewer;
+my $omccount = 0;
+sub try_add_reviewer {
+    my $id = lc(shift);
+    my $rc = undef;
+    my $id2 = $id =~ /^\@(.*)$/ ? { github => $1 } : $id;
+    my $rev = $query->find_person_tag($id2, 'rev');
+    if ($rev) {
+       my $cla = $query->has_cla($rev);
+       if ($cla) {
+           unless (grep {$_ eq $rev} @reviewers) {
+               $omccount++ if $query->is_member_of($id2, 'omc');
+               push @reviewers, $rev;
+           }
+           $rc = $rev;
+       } else {
+           push @nocla_reviewers, $id
+               unless grep {$_ eq $id} @nocla_reviewers;
+       }
+    } else {
+       push @unknown_reviewers, $id
+           unless grep {$_ eq $id} @unknown_reviewers;
+       unless ($id =~ m|^.+\@.*$| && $query->has_cla($id)) {
+           push @nocla_reviewers, $id
+               unless grep {$_ eq $id} @nocla_reviewers;
+       }
+    }
+    return $rc;
+}
+
 foreach (@ARGV) {
     if (/^--list$/) {
        my %list = ();
@@ -34,13 +66,17 @@ foreach (@ARGV) {
            my $rev = $query->find_person_tag($email_id, 'rev');
            my $omc = $query->is_member_of($email_id, 'omc');
            next unless $query->has_cla($rev);
-           my @ids = sort grep { $_ =~ m|^[a-z]+$| } map {
-               if (ref($_) eq "HASH") {
-                   values %$_;
-               } else {
-                   $_;
-               }
-           } @$_;
+           next unless $query->is_member_of($email_id, 'commit') || $omc;
+           my @ids =
+               sort grep { $_ =~ /^[a-z]+$/ || $_ =~ /^\@(?:\w|\w-\w)+$/ }
+               map {
+                   if (ref($_) eq "HASH") {
+                       my %h = %$_;
+                       map { $_ eq "github" ? '@'.$h{$_} : $h{$_} } keys %h;
+                   } else {
+                       $_;
+                   }
+               } @$_;
            foreach (@ids) {
                $list{$_} = { tag => $rev, omc => $omc };
            }
@@ -52,22 +88,7 @@ foreach (@ARGV) {
        }
        exit 0;
     } elsif (/^--reviewer=(.+)$/) {
-       my $rev = $query->find_person_tag($1, 'rev');
-       if ($rev) {
-           my $cla = $query->has_cla($rev);
-           if ($cla) {
-               unless (grep {$_ eq $rev} @reviewers) {
-                   $omccount++ if $query->is_member_of($1, 'omc');
-                   push @reviewers, $rev;
-               }
-           } else {
-               push @nocla_reviewers, $1
-                   unless grep {$_ eq $1} @nocla_reviewers;
-           }
-       } else {
-           push @unknown_reviewers, $1
-               unless grep {$_ eq $1} @unknown_reviewers;
-       }
+       try_add_reviewer($1);
     } elsif (/^--prnum=(.+)$/) {
         $prnum = $1;
     } elsif (/^--commit=(.+)$/) {
@@ -76,39 +97,64 @@ foreach (@ARGV) {
     } elsif (/^--rmreviewers$/) {
         $rmrev = 1;
     } elsif (/^--myemail=(.+\@.+)$/) {
-       my $rev = $query->find_person_tag($1, 'rev');
-       if ($rev) {
-           my $cla = $query->has_cla($rev);
-           if ($cla) {
-               # If author doesn't match us add us as reviewer
-               if ($ENV{GIT_AUTHOR_EMAIL} ne $1) {
-                   unless (grep {$_ eq $rev} @reviewers) {
-                       $omccount++ if $query->is_member_of($1, 'omc');
-                       push @reviewers, $rev;
-                   }
-               } else {
-                   # Can't review our own commits, setup this one for removal
-                   $skip_reviewer = $rev;
-                   $omccount-- if $query->is_member_of($1, 'omc');
-               }
-           } else {
-               push @nocla_reviewers, $1
-                   unless grep {$_ eq $1} @nocla_reviewers;
-           }
-       } else {
-           push @unknown_reviewers, $1
-               unless grep {$_ eq $1} @unknown_reviewers;
-       }
-    } elsif (/^--trivial$/) {
-        $trivial = 1;
+       try_add_reviewer($1);
+    } elsif (/^--verbose$/) {
+       $verbose = 1;
+    } elsif (/^--web$/) {
+        $WHAT = 'web';
+    } elsif (/--tools$/) {
+        $WHAT = 'tools'
     }
 }
+
+my @commit_message = map { (my $x = $_) =~ s|\R$||; $x } <STDIN>;
+my $trivial = !! grep(/^CLA:\s*Trivial\s*$/i, @commit_message);
+
+# If the author is a registered committer, that identity passes as a reviewer
+# too.  There is a twist, though...  see next comment
+if (my $rev = try_add_reviewer($ENV{GIT_AUTHOR_EMAIL})) {
+
+    # So here's the deal: We added the commit author because we need to keep
+    # count of the total amount of reviewers, which includes the commit author
+    # if it's a registered committer.  However, the author can "reviewed
+    # themselves", so no Reviewed-by: should be added for that identity.
+    # However, we still need to check the @reviewers count below, so the
+    # solution is to record this rev tag separately and remove it from
+    # @reviewers after the count check.
+    $skip_reviewer = $rev;
+
+} else {
+
+    # In case the author is unknown to our databases or is lacking a CLA,
+    # we need to be extra careful to check if this is supposed to be a
+    # trivial commit.
+    my $author = lc($ENV{GIT_AUTHOR_EMAIL});
+
+    # Note: it really should be enough to check if $author is unknown, since
+    # the databases are supposed to be consistent with each other.  However,
+    # let's be cautious and check both, in case someone has been registered
+    # as a known identity without having a CLA in place.
+    die "Commit author ",$author," has no CLA, and this is a non-trivial commit\n"
+       if !$trivial && grep { $_ eq $author } (@nocla_reviewers);
+
+    # Now that that's cleared, remove the author from anything that could cause
+    # more unnecessary errors (false positives).
+    @nocla_reviewers = grep { $_ ne $author } @nocla_reviewers;
+    @unknown_reviewers = grep { $_ ne $author } @unknown_reviewers;
+}
+
 if (@unknown_reviewers) {
     die "Unknown reviewers: ", join(", ", @unknown_reviewers), "\n";
 }
 if (@nocla_reviewers) {
     die "Reviewers without CLA: ", join(", ", @nocla_reviewers), "\n";
 }
+
+print STDERR "Detected trivial marker\n" if $verbose && $trivial;
+
+print STDERR "Going with these reviewers:\n  ", join("\n  ", @reviewers), "\n"
+    if $verbose;
+
 if (scalar @reviewers < 2) {
     die "Too few reviewers (total must be at least 2)\n";
 }
@@ -120,7 +166,6 @@ if ($skip_reviewer) {
     @nocla_reviewers = grep { $_ ne $skip_reviewer } @nocla_reviewers;
     @unknown_reviewers = grep { $_ ne $skip_reviewer } @unknown_reviewers;
 }
-print STDERR "DEBUG: \@reviewers = ( ", join(", ", @reviewers), " )\n";
 
 if ($skip == 1) {
     my $commit_id = $ENV{GIT_COMMIT};
@@ -142,35 +187,34 @@ if (scalar @reviewers == 0 && $rmrev == 0) {
     die "No reviewer set!\n";
 }
 
-my $last_line_blank = 0;
-my $have_rev = 0;
-while(<STDIN>) {
-    if (/^\(Merged from https:\/\/github\.com\/openssl\/openssl\/pull\//
-       || /^Reviewed-by:\s*(\S.*\S)\s*$/) {
+# Remove blank lines from the end of commit message
+pop @commit_message while $commit_message[$#commit_message] =~ m|^\s*$|;
+
+my $last_is_rev = 0;
+foreach (@commit_message) {
+    # Start each line with assuming it's not a reviewed-by line
+    $last_is_rev = 0;
+    if (/^\(Merged from https:\/\/github\.com\/openssl\/$WHAT\/pull\//) {
         next if $rmrev == 1;
-        $have_rev = 1;
-        # Skip if reviewer already in list
-        next if $1 && grep { $1 eq $_ } @reviewers;
+        $last_is_rev = 1;
+        next;                  # Because we're rewriting it below
+                               # (unless --nopr was given in addrev)
+    } elsif (/^Reviewed-by:\s*(\S.*\S)\s*$/) {
+        my $id = $1;
+        next if $rmrev == 1;
+        $last_is_rev = 1;
+        # Remove reviewers that are already in the message from our reviewer list
+        @reviewers = grep { $_ ne $id } @reviewers;
     }
-    print;
-    $last_line_blank = ($_ =~ /^\s*$/);
+    print $_,"\n";
 }
 if ($rmrev == 0) {
-    #Add a blank line unless the last one is already blank or a review line
-    print "\n" unless $last_line_blank || $have_rev;
+    #Add a blank line unless the last one is a review line
+    print "\n" unless $last_is_rev;
     foreach(@reviewers) {
        print "Reviewed-by: $_\n";
     }
-    if ($trivial) {
-        print "CLA: trivial\n";
-    }
 }
 
-print "(Merged from https://github.com/openssl/openssl/pull/$prnum)\n"
+print "(Merged from https://github.com/openssl/$WHAT/pull/$prnum)\n"
     if $prnum;
-
-my $email = $ENV{GIT_AUTHOR_EMAIL};
-
-if (!$trivial && !$query->has_cla(lc $email)) {
-    warn "\n\nWARNING: No CLA found for $email\n";
-}