send-email: expose header information to git-send-email's sendemail-validate hook

To allow further flexibility in the Git hook, the SMTP header information of the email which git-send-email intends to send, is now passed as the 2nd argument to the sendemail-validate hook. As an example, this can be useful for acting upon keywords in the subject or specific email addresses. Cc: Luben Tuikov <luben.tuikov@amd.com> Cc: Junio C Hamano <gitster@pobox.com> Cc: Ævar Arnfjörð Bjarmason <avarab@gmail.com> Acked-by: Luben Tuikov <luben.tuikov@amd.com> Signed-off-by: Michael Strawbridge <michael.strawbridge@amd.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Michael Strawbridge committed Apr 19, 2023 at 16:27 UTC a8022c5f7b678189135b6caa3fadb3d8ec0c0d48
3 files changed +77 -25
Documentation/githooks.txt
+23 -4
@@ -595,10 +595,29 @@ processed by rebase.
595 sendemail-validate
596 ~~~~~~~~~~~~~~~~~~
597
598 -This hook is invoked by linkgit:git-send-email[1]. It takes a single parameter,
599 -the name of the file that holds the e-mail to be sent. Exiting with a
600 -non-zero status causes `git send-email` to abort before sending any
601 -e-mails.
598 +This hook is invoked by linkgit:git-send-email[1].
599 +
600 +It takes these command line arguments. They are,
601 +1. the name of the file which holds the contents of the email to be sent.
602 +2. The name of the file which holds the SMTP headers of the email.
603 +
604 +The SMTP headers are passed in the exact same way as they are passed to the
605 +user's Mail Transport Agent (MTA). In effect, the email given to the user's
606 +MTA, is the contents of $2 followed by the contents of $1.
607 +
608 +An example of a few common headers is shown below. Take notice of the
609 +capitalization and multi-line tab structure.
610 +
611 + From: Example <from@example.com>
612 + To: to@example.com
613 + Cc: cc@example.com,
614 + A <author@example.com>,
615 + One <one@example.com>,
616 + two@example.com
617 + Subject: PATCH-STRING
618 +
619 +Exiting with a non-zero status causes `git send-email` to abort
620 +before sending any e-mails.
621
622 fsmonitor-watchman
623 ~~~~~~~~~~~~~~~~~~
git-send-email.perl
+29 -19
@@ -792,16 +792,31 @@ if (@rev_list_opts) {
792 @rev_list_opts);
793 }
794
795 -@files = handle_backup_files(@files);
795 +if (defined $sender) {
796 + $sender =~ s/^\s+|\s+$//g;
797 + ($sender) = expand_aliases($sender);
798 +} else {
799 + $sender = $repoauthor->() || $repocommitter->() || '';
800 +}
801 +
802 +# $sender could be an already sanitized address
803 +# (e.g. sendemail.from could be manually sanitized by user).
804 +# But it's a no-op to run sanitize_address on an already sanitized address.
805 +$sender = sanitize_address($sender);
806 +
807 +$time = time - scalar $#files;
808
809 if ($validate) {
810 foreach my $f (@files) {
811 unless (-p $f) {
812 + pre_process_file($f, 1);
813 validate_patch($f, $target_xfer_encoding);
814 }
815 }
816 }
817
818 +@files = handle_backup_files(@files);
819 +
820 if (@files) {
821 unless ($quiet) {
822 print $_,"\n" for (@files);
@@ -1050,18 +1065,6 @@ if (!$force) {
1065 }
1066 }
1067
1053 -if (defined $sender) {
1054 - $sender =~ s/^\s+|\s+$//g;
1055 - ($sender) = expand_aliases($sender);
1056 -} else {
1057 - $sender = $repoauthor->() || $repocommitter->() || '';
1058 -}
1059 -
1060 -# $sender could be an already sanitized address
1061 -# (e.g. sendemail.from could be manually sanitized by user).
1062 -# But it's a no-op to run sanitize_address on an already sanitized address.
1063 -$sender = sanitize_address($sender);
1064 -
1068 my $to_whom = __("To whom should the emails be sent (if anyone)?");
1069 my $prompting = 0;
1070 if (!@initial_to && !defined $to_cmd) {
@@ -1221,10 +1224,6 @@ sub make_message_id {
1224 #print "new message id = $message_id\n"; # Was useful for debugging
1225 }
1226
1224 -
1225 -
1226 -$time = time - scalar $#files;
1227 -
1227 sub unquote_rfc2047 {
1228 local ($_) = @_;
1229 my $charset;
@@ -2108,10 +2107,21 @@ sub validate_patch {
2107 chdir($repo->wc_path() or $repo->repo_path())
2108 or die("chdir: $!");
2109 local $ENV{"GIT_DIR"} = $repo->repo_path();
2110 +
2111 + my ($recipients_ref, $to, $date, $gitversion, $cc, $ccline, $header) = gen_header();
2112 +
2113 + require File::Temp;
2114 + my ($header_filehandle, $header_filename) = File::Temp::tempfile(
2115 + TEMPLATE => ".gitsendemail.header.XXXXXX",
2116 + DIR => $repo->repo_path(),
2117 + UNLINK => 1,
2118 + );
2119 + print $header_filehandle $header;
2120 +
2121 my @cmd = ("git", "hook", "run", "--ignore-missing",
2122 $hook_name, "--");
2113 - my @cmd_msg = (@cmd, "<patch>");
2114 - my @cmd_run = (@cmd, $target);
2123 + my @cmd_msg = (@cmd, "<patch>", "<header>");
2124 + my @cmd_run = (@cmd, $target, $header_filename);
2125 $hook_error = system_or_msg(\@cmd_run, undef, "@cmd_msg");
2126 chdir($cwd_save) or die("chdir: $!");
2127 }
t/t9001-send-email.sh
+25 -2
@@ -540,7 +540,7 @@ test_expect_success $PREREQ "--validate respects relative core.hooksPath path" '
540 test_path_is_file my-hooks.ran &&
541 cat >expect <<-EOF &&
542 fatal: longline.patch: rejected by sendemail-validate hook
543 - fatal: command '"'"'git hook run --ignore-missing sendemail-validate -- <patch>'"'"' died with exit code 1
543 + fatal: command '"'"'git hook run --ignore-missing sendemail-validate -- <patch> <header>'"'"' died with exit code 1
544 warning: no patches were sent
545 EOF
546 test_cmp expect actual
@@ -559,12 +559,35 @@ test_expect_success $PREREQ "--validate respects absolute core.hooksPath path" '
559 test_path_is_file my-hooks.ran &&
560 cat >expect <<-EOF &&
561 fatal: longline.patch: rejected by sendemail-validate hook
562 - fatal: command '"'"'git hook run --ignore-missing sendemail-validate -- <patch>'"'"' died with exit code 1
562 + fatal: command '"'"'git hook run --ignore-missing sendemail-validate -- <patch> <header>'"'"' died with exit code 1
563 warning: no patches were sent
564 EOF
565 test_cmp expect actual
566 '
567
568 +test_expect_success $PREREQ "--validate hook supports header argument" '
569 + write_script my-hooks/sendemail-validate <<-\EOF &&
570 + if test "$#" -ge 2
571 + then
572 + grep "X-test-header: v1.0" "$2"
573 + else
574 + echo "No header arg passed"
575 + exit 1
576 + fi
577 + EOF
578 + test_config core.hooksPath "my-hooks" &&
579 + rm -fr outdir &&
580 + git format-patch \
581 + --add-header="X-test-header: v1.0" \
582 + -n HEAD^1 -o outdir &&
583 + git send-email \
584 + --dry-run \
585 + --to=nobody@example.com \
586 + --smtp-server="$(pwd)/fake.sendmail" \
587 + --validate \
588 + outdir/000?-*.patch
589 +'
590 +
591 for enc in 7bit 8bit quoted-printable base64
592 do
593 test_expect_success $PREREQ "--transfer-encoding=$enc produces correct header" '