#!/usr/bin/env 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 }); my $rmrev = 0; my @commits; my $skip = 0; my $matchstr; my $clatype; my $found = 0; my $num = 0; my $refuse = 0; my $prnum = 0; my $verbose = 0; my $WHAT = 'openssl'; my $query = OpenSSL::Query->new(); my @reviewers; my @nocla_reviewers; my @unknown_reviewers; my $skip_reviewer; my $otccount = 0; sub try_add_reviewer { my $id = 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(lc $rev); if ($cla) { unless (grep {$_ eq $rev} @reviewers) { $otccount++ if $query->is_member_of($id2, 'otc'); 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(lc $id)) { push @nocla_reviewers, $id unless grep {$_ eq $id} @nocla_reviewers; } } return $rc; } foreach (@ARGV) { if (/^--list$/) { my %list = (); foreach ($query->list_people()) { my $email_id = (grep { ref($_) eq "" && $_ =~ m|\@| } @$_)[0]; my $rev = $query->find_person_tag($email_id, 'rev'); my $otc = $query->is_member_of($email_id, 'otc'); next unless $query->has_cla(lc $rev); next unless $query->is_member_of($email_id, 'commit') || $otc; 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, otc => $otc }; } } foreach (sort { my $res = $list{$a}->{tag} cmp $list{$b}->{tag}; $res != 0 ? $res : ($a cmp $b) } keys %list) { printf "%-15s %-6s (%s)\n", $_, $list{$_}->{otc} ? "[OTC]" : "", $list{$_}->{tag}; } exit 0; } elsif (/^--reviewer=(.+)$/) { try_add_reviewer($1); } elsif (/^--prnum=(.+)$/) { $prnum = $1; } elsif (/^--commit=(.+)$/) { push @commits, $1; $skip = 1; } elsif (/^--rmreviewers$/) { $rmrev = 1; } elsif (/^--myemail=(.+\@.+)$/) { 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 } ; 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 = $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"; } if ($otccount < 1) { die "At least one of the reviewers must be an OTC member\n"; } if ($skip_reviewer) { @reviewers = grep { $_ ne $skip_reviewer } @reviewers; @nocla_reviewers = grep { $_ ne $skip_reviewer } @nocla_reviewers; @unknown_reviewers = grep { $_ ne $skip_reviewer } @unknown_reviewers; } if ($skip == 1) { my $commit_id = $ENV{GIT_COMMIT}; foreach(@commits) { if ($commit_id =~ /^$_/) { $skip = 0; last; } } if ($skip == 1) { while() { print; } exit(0); } } if (scalar @reviewers == 0 && $rmrev == 0) { die "No reviewer set!\n"; } # 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; $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 $_,"\n"; } if ($rmrev == 0) { #Add a blank line unless the last one is a review line print "\n" unless $last_is_rev; foreach(@reviewers) { print "Reviewed-by: $_\n"; } } print "(Merged from https://github.com/openssl/$WHAT/pull/$prnum)\n" if $prnum;