[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH for-4.11 v2 2/2] Add new add_maintainers.pl script to optimise the workflow when using git format-patch with get_maintainer.pl



The tool covers step 2 of the following workflow

  Step 1: git format-patch ... -o <patchdir> ...
  Step 2: ./scripts/add_maintainers.pl -d <patchdir>
          This overwrites  *.patch files in <patchdir>
  Step 3: git send-email -to xen-devel@xxxxxxxxxxxxxxxxxxxx <patchdir>/*.patch

I manually tested all options and the most common combinations
on Mac.

Changes in v2
- Added RAB (indicated by Juergen on IRC that this is OK)
- Remove trailing whitespaces
- Renamed --prefix to --reroll-count
- Cleaned up short options -v, ... to be in line with git
- Added --tags|-t option to add AB, RAB and RB emails to CC list
- Added --insert|-i mode to allow for people adding CCs to commit message
  instead of the e-mail header (the header is the default)
- Moved common code into functions
- Added logic, such that the tool only insert's To: and Cc: statements
  which were not there before, allowing for running the tool multiple times
  on the same <patchdir>

Cc: Andrew Cooper <andrew.cooper3@xxxxxxxxxx>
Cc: George Dunlap <George.Dunlap@xxxxxxxxxxxxx>
Cc: Ian Jackson <ian.jackson@xxxxxxxxxxxxx>
Cc: Jan Beulich <jbeulich@xxxxxxxx>
Cc: Julien Grall <julien.grall@xxxxxxx>
Cc: Konrad Rzeszutek Wilk <konrad.wilk@xxxxxxxxxx>
Cc: Stefano Stabellini <sstabellini@xxxxxxxxxx>
Cc: Tim Deegan <tim@xxxxxxx>
Cc: Wei Liu <wei.liu2@xxxxxxxxxx>
Cc: Juergen Gross <jgross@xxxxxxxx>
Signed-off-by: Lars Kurth <lars.kurth@xxxxxxxxxx>

Release-acked-by: Juergen Gross <jgross@xxxxxxxx>
---
 scripts/add_maintainers.pl | 380 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 380 insertions(+)
 create mode 100755 scripts/add_maintainers.pl

diff --git a/scripts/add_maintainers.pl b/scripts/add_maintainers.pl
new file mode 100755
index 0000000000..616c13caa1
--- /dev/null
+++ b/scripts/add_maintainers.pl
@@ -0,0 +1,380 @@
+#!/usr/bin/perl -w
+# (c) 2018, Lars Kurth <lars.kurth@xxxxxxxxxx>
+#
+# Add maintainers to patches generated with git format-patch
+#
+# Usage: perl scripts/add_maintainers.pl [OPTIONS] -patchdir <patchdir>
+#
+# Prerequisites: Execute
+#                git format-patch ... -o <patchdir> ...
+#
+#                ./scripts/get_maintainer.pl is present in the tree
+#
+# Licensed under the terms of the GNU GPL License version 2
+
+use strict;
+
+use Getopt::Long qw(:config no_auto_abbrev);
+use File::Basename;
+use List::MoreUtils qw(uniq);
+
+# Tool Variables
+my $tool = $0;
+my $toolversion = "1.0";
+
+# Arguments / Options
+my $version = 0;
+my $help = 0;
+my $patch_dir = 0;
+my $get_maintainer_args = "";
+my $verbose = 0;
+my $rerollcount = 0;
+my $patch_prefix = "0"; # Use a 0, such that v* does not get picked up
+                        # Obviously this will only work for series with
+                        # < 999 patches, which should be fine
+my $mode = "top";
+my @modes = ("top", "ccbody");
+my $tags = 0;
+
+# Constants
+my @tags                = ("Acked-by:",
+                           "Release-acked-by:",
+                           "Reviewed-by:");
+my $CC                  = "Cc:"; # Note: git-send-mail requires Cc:
+my $TO                  = "To:";
+my $AT                  = "@";
+my $cc_insert_before    = "Signed-off-by:";
+my $to_insert_before    = "Date:";
+my $cover_letter        = "0000-cover-letter.patch";
+my $get_maintainer      = "./scripts/get_maintainer.pl";
+my $patch_ext           = ".patch";
+my $mailing_lists       = $AT."lists.";
+
+my @lists; #Needed for <<EOT
+my $usage = <<EOT;
+USAGE: $tool [options] (--patchdir|-d) <patchdir>
+VERSION: $toolversion
+
+OPTIONS:
+--------
+  [(--reroll-count|-v) <n>]
+    Choose patch files for specific version. This results into the
+    following filters on <patchdir>
+    0: default - *.patch
+    >1: v<n>*.patch
+  [(--insert|-i) (top|ccbody)]
+    top: default - insert everything in the e-mail header
+    ccbody: insert CCs into body (this means the CC list will be stored in
+            the commit message
+  [(--tags|-t)]
+    Read email addresses from tags and add to CC list.
+  [(--args|-a) <arguments>]
+    Arguments passed on to $get_maintainer
+  [--verbose]
+    Show more output
+  [--version]
+    Show version
+  --h|help|usage
+    Show this help information
+
+WORKFLOW:
+---------
+  This script is intended to be used as part of the following workflow
+
+  Step 1: git format-patch ... -o <patchdir> ...
+  Step 2: ./scripts/add_maintainers.pl -d <patchdir>
+          This overwrites *.patch files in <patchdir> but makes a backup
+  Step 3: git send-email -to xen-devel@xxxxxxxxxxxxxxxxxxxx <patchdir>/*.patch
+EOT
+
+if (!GetOptions(
+                'd|patchdir=s'     => \$patch_dir,
+                'v|reroll-count=i' => \$rerollcount,
+                'i|insert=s'       => \$mode,
+                't|tags'           => \$tags,
+                'a|args=s'         => \$get_maintainer_args,
+                'verbose'          => \$verbose,
+                'version'          => \$version,
+                'h|help|usage'     => \$help,
+                )) {
+    die "$tool: invalid argument - use --help if necessary\n";
+}
+
+if ($help != 0) {
+    print $usage;
+    exit 0;
+}
+
+if ($version != 0) {
+    print("$tool: version $toolversion\n");
+    exit 0;
+}
+
+if (! -e $get_maintainer) {
+    die "$tool: The tool requires $get_maintainer\n";
+}
+
+if (!$patch_dir) {
+    die "$tool: Directory -d|--patchdir not specified\n";
+}
+
+if (! -e $patch_dir) {
+    die "$tool: Directory $patch_dir does not exist\n";
+}
+
+if ($rerollcount > 0) {
+    $patch_prefix = "v".$rerollcount;
+}
+
+if ( ! grep $_ eq $mode, @modes ) {
+    die "$tool: Invalid (-i|--insert) value\n";
+}
+
+# Get the list of patches
+my $pattern = $patch_dir.'/'.$patch_prefix.'*'.$patch_ext;
+my @patches = glob($pattern);
+my $has_cover_letter = 0;
+my $cover_letter_file;
+
+if (!scalar @patches) {
+    die "$tool: Directory $patch_dir contains no patches\n";
+}
+
+# Do the actual processing
+my $file;
+my @combined_to;
+my @combined_cc;
+
+foreach my $file (@patches) {
+    if (index($file, $cover_letter) != -1) {
+        $has_cover_letter = 1;
+        $cover_letter_file = $file;
+    } else {
+        print "Processing: ".basename($file)."\n";
+        my @to;         # To: lists returned by get_maintainers.pl
+        my @topatch;    # To: entries in *.patch
+        my @cc;         # Cc: maintainers returned by get_maintainers.pl
+        my @ccpatch;    # Cc: entries in *.patch
+        my @extrapatch; # Cc: for AB, RB, RAB in *.patch
+
+        # Read tags from output of get_maintainers.pl
+        getmaintainers($file, \@to, \@cc);
+
+        # Read all lines with CC & TO from the patch file (these will
+        # likely come from the commit message). Also read tags.
+        gettagsfrompatch($file, \@topatch, \@ccpatch, \@extrapatch);
+
+        # With -t|--tags add @extrapatch to @cc and @combined_cc
+        if ($tags) {
+            push @cc, @extrapatch;
+            push @combined_cc, @extrapatch;
+        }
+
+        # In this section we normalize the lists. We remove entries
+        # that are already in the patch, from @cc and @to
+        my @to_only = normalize(\@to, \@topatch);
+        my @cc_only = normalize(\@cc, \@ccpatch);
+
+        # Insert CC's and TO's at the top of the *.patch file
+        if ($mode eq "top") {     
+            my $insert = join("\n", uniq (@to_only, @cc_only))."\n";
+            # Insert snippets into files
+            # $insert before "Signed-off-by:"
+            insert_before($file , $insert, $to_insert_before);
+        }
+        elsif ($mode eq "ccbody") {
+            my $to = join("\n", uniq @to_only)."\n";
+            my $cc = join("\n", uniq @cc_only)."\n";
+
+            # Insert snippets into files
+            # $cc before "Signed-off-by:"f
+            insert_before($file , $cc, $cc_insert_before);
+            # $to before suitable header line
+            insert_before($file , $to, $to_insert_before);
+        }
+    }
+}
+
+# Deal with the cover letter
+if ($has_cover_letter) {
+    my @topatch;    # To: entries in *.patch
+    my @ccpatch;    # Cc: entries in *.patch
+
+    # Read all lines with CC & TO from the patch file such that subsequent
+    # calls don't lead to duplication
+    gettagsfrompatch($cover_letter_file, \@topatch, \@ccpatch);
+
+    # In this section we normalize the lists. We remove entries
+    # that are already in the patch, from @cc and @to
+    my @to_only = normalize(\@combined_to, \@topatch);
+    my @cc_only = normalize(\@combined_cc, \@ccpatch);
+
+    my $insert = join("\n", uniq (@to_only, @cc_only))."\n";
+
+    print "Processing: ".basename($cover_letter_file)."\n";
+
+    # Insert snippets into files
+    # $to and $cc before suitable header line
+    insert_before($cover_letter_file, $insert, $to_insert_before);
+
+    print "\nDon't forget to add the subject and message to ".
+          $cover_letter_file."\n";
+}
+
+print "Then perform:\n".
+      "git send-email -to xen-devel".$AT."lists.xenproject.org ".
+      $patch_dir.'/'.$patch_prefix."*.patch"."\n";
+
+exit 1;
+
+sub getmaintainers {
+    my ($file, $rto, $rcc) = @_;
+    my $cmd = $get_maintainer." ".$get_maintainer_args." < ".$file;
+    my $fh;
+
+    open($fh, "$cmd|")
+        or die "Failed to open '$cmd'\n";
+    while(<$fh>) {
+        chomp;
+        # Keep lists and CC's separately as we dont want them in
+        # the commit message under a Cc: line
+        if (index($_, $mailing_lists) != -1) {
+            push @{$rto}, $TO." ".$_;
+            push @combined_to, $TO." ".$_;
+        } else {
+            push @{$rcc}, $CC." ".$_;
+            push @combined_cc, $CC." ".$_;
+        }
+    }
+    close $fh;
+}
+
+sub gettagsfrompatch {
+    my ($file, $rto, $rcc, $rextra) = @_;
+    my $fh;
+
+    open($fh, "<", $file)
+        or die "Failed to open '$file'\n";
+    while(<$fh>) {
+        chomp;
+        my $line = $_;
+        my $nline;
+
+        if (hastag($line, $TO, \$nline)) {
+            push @{$rto}, $nline;
+            push @combined_to, $nline;
+        }
+        if (hastag($line, $CC, \$nline)) {
+            push @{$rcc}, $nline;
+            push @combined_cc, $nline;
+        }
+        # If there is an $rextra, then get various tags and add
+        # email addresses to the CC list
+        if ($rextra) {
+            my $item;
+            foreach $item (@tags) {
+                if (hastag($line, $item, \$nline)) {
+                    # Replace tag with CC, then push
+                    $nline =~ s/$item/$CC/;
+                    push @{$rextra}, $nline;
+                }
+            }
+        }
+    }
+    close $fh;
+}
+
+sub hastag ()
+{
+    my ($line, $tag, $rline) = @_;
+    my $index = index(lc $line, lc $tag);
+    if ($index != -1) {
+        if ($index == 0) {
+            # If at first position, then just return
+            ${$rline} = $line;
+            return 1;
+        } else {
+            # If not at first position, then remove white
+            # spaces before the tag and return a normalized
+            # string
+            if (substr($line, 0, $index) =~ /^\s*$/) {
+                ${$rline} = substr($line, $index);
+                return 1;
+            }
+        }
+    }
+    return 0;
+}
+
+sub normalize {
+    # Note: you cannot pass two arrays to a subroutine without loosing the
+    # information which entry belongs to which array. Thus, pass as references.
+    my ($ra, $rb) = @_;
+    my @aonly = ();
+    my %seen;
+    my $item;
+
+    foreach $item (@{$rb}) {
+        $seen{$item} = 1;
+    }
+    foreach $item (@{$ra}) {
+        unless ($seen{$item}) {
+            # it's not in %seen, so add to @aonly
+            push @aonly, $item;
+        }
+    }
+
+    return @aonly;
+}
+
+sub readfile {
+    my ($file) = @_;
+    my $fh;
+    my $content;
+    open($fh, "<", $file)
+         or die "Could not open file '$file' $!";
+    $content = do { local $/; <$fh> };
+    close $fh;
+
+    return $content;
+}
+
+sub writefile {
+    my ($content, $file) = @_;
+    my $fh;
+    open($fh, ">", $file)
+         or die "Could not open file '$file' $!";
+    print $fh $content;
+    close $fh;
+
+    return 1;
+}
+
+sub insert_before {
+    my ($file, $ins, $before) = @_;
+    my $content;
+
+    if ($ins eq "\n") {
+        # Not inserting anything
+        # I added this here such that I don't have to run the check
+        # when calling the function.
+        return 0;
+    }
+
+    # Read file
+    $content = readfile($file);
+
+    # Split the string and generate new content
+    my $i  = index($content, $before);
+    my $p1 = substr $content, 0, $i;
+    my $p2 = substr $content, $i;
+
+    writefile($p1.$ins.$p2, $file);
+
+    if ($verbose) {
+        print "\nInserted into ".basename($file).' before "'.$before."'".
+              "\n-----\n".$ins."-----\n";
+    }
+
+    return 1;
+}
-- 
2.13.0

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/mailman/listinfo/xen-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.