misc: experimental script to get the list of the reviewers for a commit 39/33139/4
authorAndrew Yourtchenko <ayourtch@gmail.com>
Wed, 14 Jul 2021 20:44:05 +0000 (22:44 +0200)
committerDave Wallace <dwallacelf@gmail.com>
Tue, 20 Sep 2022 20:54:28 +0000 (20:54 +0000)
The script accepts zero or one argument (the commit hash), and outputs
the detected components, the component maintainers,
and the final suggested reviewer list. See the script
for the example output.

Change-Id: Ief671fe837c6201bb11fd05d02af881822b0bb33
Type: docs
Signed-off-by: Andrew Yourtchenko <ayourtch@gmail.com>
extras/scripts/vpp-review [new file with mode: 0755]

diff --git a/extras/scripts/vpp-review b/extras/scripts/vpp-review
new file mode 100755 (executable)
index 0000000..8ce4bd2
--- /dev/null
@@ -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);
+
+