diff options
-rwxr-xr-x | extras/scripts/vpp-review | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/extras/scripts/vpp-review b/extras/scripts/vpp-review new file mode 100755 index 00000000000..8ce4bd2d9c1 --- /dev/null +++ b/extras/scripts/vpp-review @@ -0,0 +1,240 @@ +#!/usr/bin/perl + +use Data::Dumper; +use Env; +use File::Glob ':glob'; + +# +# Use this script to determine the reviewers for a given commit, like so: +# +# ayourtch@ayourtch-lnx:~/vpp$ ./extras/scripts/vpp-review HEAD +# commit 7ea63c597f82412b68b5a565df10e13669b1decb +# Author: Andrew Yourtchenko <ayourtch@gmail.com> +# Date: Wed Jul 14 22:44:05 2021 +0200 +# +# misc: experimental script to get the list of the reviewers for a commit +# +# accepts zero or one argument (the commit hash), and outputs +# the detected components, the component maintainers, +# and the final suggested reviewer list +# +# Change-Id: Ief671fe837c6201bb11fd05d02af881822b0bb33 +# Type: docs +# Signed-off-by: Andrew Yourtchenko <ayourtch@gmail.com> +# +# :000000 100755 000000000 635dde59f A extras/scripts/vpp-review +# +# +# Components of files in the commit: +# misc: extras/scripts/vpp-review +# +# Component misc maintainers: +# vpp-dev Mailing List <vpp-dev@fd.io> +# +# +# Final reviewer list: +# vpp-dev Mailing List <vpp-dev@fd.io> +# ayourtch@ayourtch-lnx:~/vpp$ +# + +# +# Read the maintainers file into a hash, indexed by short component name. +# +sub read_maintainers() { + open(F, "MAINTAINERS") || die("Could not open MAINTAINERS file"); + my $short_name = ""; + my $long_name = ""; + my $in_component = 0; + my $maintainers = []; + my $paths = []; + my $exclude_paths = []; + my $feature_yaml = []; + my $comments = []; + my $out = {}; + while (<F>) { + chomp(); + my $aLine = $_; + # print ("LINE: $aLine\n"); + if (/^([A-Z]):\s+(.*)$/) { + my $aLetter = $1; + my $aValue = $2; + # print ("LETTER: $aLetter, VALUE: $aValue\n"); + if ($aLetter eq "I") { + $short_name = $aValue; + } + elsif ($aLetter eq "M") { + push(@{$maintainers}, $aValue); + } + elsif ($aLetter eq "F") { + push(@{$paths}, $aValue); + } + elsif ($aLetter eq "E") { + push(@{$exclude_paths}, $aValue); + } + elsif ($aLetter eq "Y") { + push(@{$feature_yaml}, $aValue); + } + elsif ($aLetter eq "C") { + push(@{$comments}, $aValue); + } else { + print ("LETTER: $aLetter, VALUE: $aValue\n"); + } + # FIXME: deal with all the other letters here + } elsif (/^(\s*)$/) { + if ($in_component) { + $in_component = 0; + if ($short_name ne "") { + my $comp = {}; + $comp->{'short_name'} = $short_name; + $comp->{'name'} = $long_name; + $comp->{'maintainers'} = $maintainers; + $comp->{'paths'} = $paths; + $comp->{'comments'} = $comments; + $comp->{'exclude_paths'} = $exclude_paths; + $comp->{'feature_yaml'} = $feature_yaml; + $out->{$short_name} = $comp; + # print("FEATURE: $short_name => $long_name\n"); + $short_name = ""; + $long_name = ""; + $maintainers = []; + $paths = []; + $exclude_paths = []; + $comments = []; + $feature_yaml = []; + } + } + # print ("END\n"); + } elsif (/^([^\s].*)$/) { + $in_component = 1; + $long_name = $1; + } + } + + if ($in_component) { + $in_component = 0; + if ($short_name ne "") { + my $comp = {}; + $comp->{'short_name'} = $short_name; + $comp->{'name'} = $long_name; + $comp->{'maintainers'} = $maintainers; + $comp->{'paths'} = $paths; + $comp->{'comments'} = $comments; + $comp->{'exclude_paths'} = $exclude_paths; + $comp->{'feature_yaml'} = $feature_yaml; + $out->{$short_name} = $comp; + } + + } + + return($out); +} + +sub match_my_path { + my $p = $_[0]; + my $apath = $_[1]; + my $root = $_[2]; + + my $p1 = $p; + $p1 =~ s#\*#[^/]*#g; + + my $pattern = "$root$p1"; + + if ($apath =~ /$pattern/) { + return 1; + } else { + return 0; + } +} + +sub match_path_to_list { + my $path_list = $_[0]; + my $apath = $_[1]; + my $root = $_[2]; + foreach $p (@{$path_list}) { + if (match_my_path($p, $apath, $root)) { + return 1; + } + } + return 0; +} + +sub match_path_to_comp_hash { + my $chash = $_[0]; + my $apath = $_[1]; + my $root = $_[2]; + my $is_included = match_path_to_list($chash->{'paths'}, $apath, $root); + my $is_excluded = match_path_to_list($chash->{'exclude_paths'}, $apath, $root); + return ($is_included && !$is_excluded); +} + + +sub match_path { + my $components = $_[0]; + my $apath = $_[1]; + my $root = $_[2]; + my $out = []; + + foreach $aCompName (keys %{$components}) { + my $chash = $components->{$aCompName}; + if (match_path_to_comp_hash($chash, $apath, $root)) { + push(@{$out}, $aCompName); + } + } + my $out1 = $out; + + if (scalar @{$out} > 1) { + # not very efficient way to filter out "misc" but oh well. + $out1 = []; + foreach $aval (@{$out}) { + if ($aval ne "misc") { + push(@{$out1}, $aval); + } + } + } + + return ($out1); +} + +my $commit_id = $ARGV[0]; +my $commit_log = `git log --raw -n 1 $commit_id`; +my $files = []; +foreach my $aLine (split(/[\r\n]+/, $commit_log)) { + if ($aLine =~ /^:[0-7]+\s+[0-7]+\s+[0-9a-f]+\s+[0-9a-f]+\s+\S+\s+(\S+)$/) { + push(@{$files}, $1); + } +} + +my $comp = read_maintainers(); +my $matched_comps = {}; +my $matched_reviewers = {}; + +print("$commit_log\n\n"); + +print("Components of files in the commit:\n"); + +foreach $aFile (@{$files}) { + my $matches = match_path($comp, $aFile, ''); + my $matches_str = join(" ", @{$matches}); + print (" $matches_str: $aFile\n"); + + foreach my $aComp (@{$matches}) { + $matched_comps->{$aComp} = 1; + } +} + +foreach my $aKey (keys %{$matched_comps}) { + print("\nComponent $aKey maintainers:\n"); + foreach my $aRV (@{$comp->{$aKey}->{'maintainers'}}) { + print(" $aRV\n"); + $matched_reviewers->{$aRV} = 1; + } +} + +print("\n\nFinal reviewer list:\n"); + +foreach my $aRV (keys %{$matched_reviewers}) { + print(" $aRV\n"); +} +# print Dumper(\$comp); + + |