| #!/usr/bin/perl |
| |
| # Generate an mbox of mail from the applied quilt series in the proper |
| # format for a -stable kernel review. |
| |
| use strict; |
| use warnings; |
| |
| use File::Basename; |
| use lib File::Basename::dirname($0); |
| use LinuxStableQueue qw($MAIL_FROM $MAIL_REVIEW_TO $MAIL_REVIEW_COVER_CC |
| $MAIL_REVIEW_CC $STABLE_RC_URL |
| base_version last_update common_mail_header |
| mime_entity_to_mbox print_signature parse_mailbox_addr |
| mailbox_addr_to_addr_spec make_mailbox_addr |
| parse_mailbox_list quilt_dir_from_git_dir); |
| use FileHandle; |
| use MIME::Parser (); |
| use MIME::Words qw(decode_mimewords); |
| use Mail::Field::AddrList (); |
| use POSIX qw(getcwd strftime); |
| |
| sub quilt_shortlog { |
| my ($dir, $series, $log_fh) = @_; |
| my %patches; |
| my $parser = new MIME::Parser; |
| $parser->output_to_core(1); |
| |
| for my $patch_name (@$series) { |
| my $message = $parser->parse_open("$dir/$patch_name") or return 0; |
| |
| # Extract author and subject |
| $message->head->unfold(); |
| my ($author) = |
| Mail::Field->new('From', |
| scalar(decode_mimewords($message->head->get('From')))) |
| ->names(); |
| my $subject = scalar(decode_mimewords($message->head->get('Subject'))); |
| chomp $subject; |
| if (!defined($author) || !defined($subject)) { |
| print STDERR "E: Author and subject not found in $patch_name\n"; |
| return 0; |
| } |
| |
| # Extract upstream commit hash |
| my $body = $message->bodyhandle->open('r'); |
| my $upstream; |
| for (<$body>) { |
| if (/^commit (.*) upstream\.\n/m) { |
| $upstream = $1; |
| } elsif (/^\[ Upstream commit (.*) \]\n/m) { |
| $upstream = $1; |
| } elsif (/^\(cherry picked from commit (.*)\)\n/m) { |
| $upstream = $1; |
| } |
| } |
| if (!defined($upstream)) { |
| # non-fatal; sometimes there is a good reason for this |
| print STDERR "W: Upstream commit hash not found in $patch_name\n"; |
| $upstream = 'not upstream'; |
| } |
| |
| $patches{$author} ||= {}; |
| $patches{$author}->{$subject} = $upstream; |
| } |
| |
| # Group and list ASCIIbetically by author and then subject |
| for my $author (sort(keys(%patches))) { |
| printf $log_fh "%s (%d):\n", $author, scalar(keys(%{$patches{$author}})); |
| for my $subject (sort(keys(%{$patches{$author}}))) { |
| my $upstream = $patches{$author}->{$subject}; |
| printf $log_fh " %s\n [%s]\n", $subject, $upstream; |
| } |
| printf $log_fh "\n"; |
| } |
| |
| return 1; |
| } |
| |
| my $update_rc_ver = `make kernelversion`; |
| chomp $update_rc_ver; |
| my $update_ver = $update_rc_ver; |
| $update_ver =~ s/-rc\d+$//; |
| my $last_update_ver = last_update($update_ver); |
| my $base_ver = base_version($update_ver); |
| |
| my $DATE = strftime('%a %b %d %H:%M:%S UTC %Y', gmtime(time() + 2 * 86400)); |
| |
| if ($update_rc_ver eq $update_ver) { |
| print STDERR "Makefile says the version is ${update_rc_ver}, did you forget to set the -rc version?\n"; |
| exit 1; |
| } |
| |
| my $MBOX = "linux-$update_rc_ver.mbox"; |
| |
| print "Creating the mailbox for kernel release ${update_rc_ver}\n"; |
| |
| my $mbox_fh = new FileHandle($MBOX, 'w'); |
| |
| my $quilt_dir = quilt_dir_from_git_dir(getcwd()) or die "Where are you?"; |
| my $series_fh = new FileHandle('quilt series |') or die "$!"; |
| my @series = <$series_fh> or die "$!"; |
| chomp @series; |
| |
| my $NUM_PATCHES = scalar(@series); |
| my $patch_num_fmt = '%0' . length($NUM_PATCHES) . 'd'; |
| |
| my $cover = MIME::Entity->build( |
| From => $MAIL_FROM, |
| To => $MAIL_REVIEW_TO, |
| Cc => "$MAIL_REVIEW_COVER_CC, $MAIL_REVIEW_CC", |
| common_mail_header(), |
| Subject => sprintf( |
| "[PATCH $base_ver $patch_num_fmt/$NUM_PATCHES] $update_rc_ver review", 0), |
| Type => 'text/plain', |
| Charset => 'UTF-8', |
| Encoding => '8bit', |
| Data => ''); |
| |
| my $body_fh = $cover->open('w'); |
| print $body_fh |
| "This is the start of the stable review cycle for the ${update_ver} release. |
| There are ${NUM_PATCHES} patches in this series, which will be posted as responses |
| to this one. If anyone has any issues with these being applied, please |
| let me know. |
| |
| Responses should be made by ${DATE}. |
| Anything received after that time might be too late. |
| |
| All the patches have also been committed to the linux-${base_ver}.y-rc branch of |
| ${STABLE_RC_URL} . |
| A shortlog and diffstat can be found below. |
| |
| Ben. |
| |
| ------------- |
| |
| "; |
| quilt_shortlog($quilt_dir, \@series, $body_fh) or exit 1; |
| print $body_fh `git diff --stat v$last_update_ver`; |
| print_signature($body_fh); |
| close $body_fh; |
| |
| # XXX I want to attach the combined patch, but I also want to sign it. |
| # And I don't want to have to load all the messages into a mailer. So |
| # this will have to wait for GPG integration. |
| |
| mime_entity_to_mbox($cover, $mbox_fh); |
| |
| my $parser = new MIME::Parser; |
| |
| for my $i (1..@series) { |
| my $patch_name = $series[$i - 1]; |
| |
| # Extract what we need |
| my $patch = $parser->parse_open("$quilt_dir/$patch_name"); |
| my $from = MIME::Words::decode_mimewords($patch->head->get('From')); |
| my $subject = $patch->head->get('Subject'); |
| my $body_text = $patch->bodyhandle->as_string; |
| my %extra_cc; |
| |
| # Remove temporary files |
| $patch->purge; |
| |
| # Add the author and everyone mentioned in the commit message to |
| # the Cc list. De-dupe, and fix quoting of names. |
| my ($name, $addr_spec) = parse_mailbox_addr($patch->head->get('From')); |
| $extra_cc{$addr_spec} = make_mailbox_addr($name, $addr_spec); |
| while ($body_text =~ /^(?:Signed-off-by|Acked-by|Suggested-by|Reviewed-by|Requested-by|Reported-by|Tested-by|Cc):\s*(.*>|\S*)/gim) { |
| ($name, $addr_spec) = parse_mailbox_addr($1); |
| $extra_cc{$addr_spec} ||= make_mailbox_addr($name, $addr_spec); |
| } |
| |
| # Also remove recipients that are on the fixed address list |
| for ($MAIL_FROM, parse_mailbox_list($MAIL_REVIEW_TO), |
| parse_mailbox_list($MAIL_REVIEW_CC)) { |
| delete $extra_cc{mailbox_addr_to_addr_spec($_)}; |
| } |
| |
| # Build a new message |
| $patch = MIME::Entity->build( |
| From => $MAIL_FROM, |
| To => $MAIL_REVIEW_TO, |
| Cc => join(', ', $MAIL_REVIEW_CC, values(%extra_cc)), |
| common_mail_header(), |
| Subject => sprintf("[PATCH $base_ver $patch_num_fmt/$NUM_PATCHES] %s", |
| $i, $subject), |
| Type => 'text/plain', |
| Charset => 'UTF-8', |
| Encoding => '8bit', |
| Data => ''); |
| |
| # Make it a reply to the cover |
| $patch->head->add('In-Reply-To' => $cover->head->get('Message-Id')); |
| |
| my $body_fh = $patch->open('w'); |
| print $body_fh |
| "$update_rc_ver review patch. If anyone has any objections, please let me know. |
| |
| ------------------ |
| |
| From: $from |
| "; |
| print $body_fh $body_text; |
| close $body_fh; |
| |
| mime_entity_to_mbox($patch, $mbox_fh); |
| } |
| |
| print "mbox is now in ${MBOX}\n"; |