diff options
author | Sean Whitton <spwhitton@spwhitton.name> | 2020-05-11 15:25:35 -0700 |
---|---|---|
committer | Sean Whitton <spwhitton@spwhitton.name> | 2020-05-11 15:25:35 -0700 |
commit | 75df035544868a65e2a3d5603e65fc6f578e1550 (patch) | |
tree | b8b0cfde404dcb0dd84e5b986f5107ae560b7b9c | |
parent | c92a2b7d7aa9467e9ff69643ee46874dfb9eb829 (diff) | |
parent | ab4acbc313dc1a20e166045e61c603053c4f00dc (diff) | |
download | mailscripts-75df035544868a65e2a3d5603e65fc6f578e1550.tar.gz |
Merge tag 'debian/0.20-1' into buster-bpo
mailscripts release 0.20-1 for unstable (sid) [dgit]
[dgit distro=debian no-split --quilt=linear]
# gpg: Signature made Tue 05 May 2020 10:57:07 AM MST
# gpg: using RSA key 9B917007AE030E36E4FC248B695B7AE4BF066240
# gpg: Good signature from "Sean Whitton <spwhitton@spwhitton.name>" [ultimate]
# Primary key fingerprint: 8DC2 487E 51AB DD90 B5C4 753F 0F56 D055 3B6D 411B
# Subkey fingerprint: 9B91 7007 AE03 0E36 E4FC 248B 695B 7AE4 BF06 6240
-rw-r--r-- | CONTRIBUTING.rst | 60 | ||||
-rw-r--r-- | debian/changelog | 28 | ||||
-rw-r--r-- | debian/control | 1 | ||||
-rwxr-xr-x | imap-dl | 90 | ||||
-rwxr-xr-x | maildir-import-patch | 98 | ||||
-rw-r--r-- | maildir-import-patch.1.pod | 15 | ||||
-rw-r--r-- | mailscripts.el | 27 | ||||
-rwxr-xr-x | notmuch-slurp-debbug | 3 |
8 files changed, 230 insertions, 92 deletions
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 707e2f6..7ef8682 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -1,3 +1,63 @@ +Submitting patches +================== + +Thank you for your interest in contributing to this project! + +Please **do not** submit a pull request on GitHub. The repository +there is an automated mirror, and I don't develop using GitHub's +platform. + +Project mailing lists +===================== + +There are two low-volume project mailing lists, shared with some other +small free software projects: + +- sgo-software-announce -- + <https://www.chiark.greenend.org.uk/mailman/listinfo/sgo-software-announce> + + For release announcements. + +- sgo-software-discuss -- + <https://www.chiark.greenend.org.uk/mailman/listinfo/sgo-software-discuss> + + For bug reports, posting patches, user questions and discussion. + +Please prepend ``[mailscripts]`` to the subject line of your e-mail, +and for patches, pass ``--subject-prefix="PATCH mailscripts"`` to +git-send-email(1). + +Posting to sgo-software-discuss +------------------------------- + +If you're not subscribed to the list, your posting will be held for +moderation; please be patient. + +Whether or not you're subscribed, chiark.greenend.org.uk has +aggressive antispam. If your messages aren't getting through, we can +easily add a bypass on chiark; please contact <spwhitton@spwhitton.name>. + +If you don't want to deal with the mailing list and just want to send +patches, you should feel free to pass ``--to=spwhitton@spwhitton.name`` +to git-send-email(1). + +Alternatively, publish a git branch somewhere publically accessible (a +GitHub fork is fine) and write to me asking me to merge it. I may +convert your branch back into patches when sending you feedback :) + +IRC channel +=========== + +You can ask questions and discuss mailscripts in ``#notmuch`` on +server ``chat.freenode.net``. + +Reporting bugs +============== + +Please read "How to Report Bugs Effectively" to ensure your bug report +constitutes a useful contribution to the project: +<https://www.chiark.greenend.org.uk/~sgtatham/bugs.html> + Signing off your commits ======================== diff --git a/debian/changelog b/debian/changelog index 1adf758..65f69a9 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,31 @@ +mailscripts (0.20-1) unstable; urgency=medium + + * imap-dl: Fix failure when python3-gssapi isn't installed (Closes: #955011). + Thanks to Robbie Harwood for the patch. + * imap-dl: Update for imaplib type changes (Closes: #959607). + Thanks to Robbie Harwood for the patch and David Bremner for testing. + * notmuch-slurp-debbug: Fix reading configuration file (Closes: #958391). + Thanks to intrigeri for the patch. + * mailscripts.el: + - Change prompt "branch name" -> "new branch name". + When the user inputs a branch name, it is used only to attempt the + creation of a new branch, but the old prompt suggests that inputting + an existing branch name would cause that branch to be checked out. + - Add mailscripts-detach-head-from-existing-branch defcustom. + - notmuch-extract-message-patches: set NO-STRICT-MIME argument of + `mm-dissect-buffer' to enable extracting patches from more messages. + - notmuch-extract-message-patches: attempt to improve regexp to + extract more wanted patches. + * maildir-import-patch: Prepend "[PATCH fooproject imported]" to the + subject lines of patches, unless the user supplies a --subject-prefix + option to be passed on to git-format-patch(1). + This makes patches generated by this script more easily + distinguishable from patches really received by e-mail. + - Add libgit-wrapper-perl to Recommends. + * Add mailing list & IRC channel info to CONTRIBUTING.rst. + + -- Sean Whitton <spwhitton@spwhitton.name> Tue, 05 May 2020 10:53:38 -0700 + mailscripts (0.19-1~bpo10+1) buster-backports; urgency=medium * Rebuild for buster-backports. diff --git a/debian/control b/debian/control index 3f089a0..0eb4ea2 100644 --- a/debian/control +++ b/debian/control @@ -48,6 +48,7 @@ Depends: Recommends: devscripts, git, + libgit-wrapper-perl, notmuch, python3-argcomplete, python3-gssapi, @@ -87,49 +87,52 @@ summary_splitter = Splitter('summary', _summary_re) _fetch_re = rb'^(?P<id>[0-9]+) \(UID (?P<uid>[0-9]+) (FLAGS \([\\A-Za-z ]*\) )?BODY\[\] \{(?P<size>[0-9]+)\}$' fetch_splitter = Splitter('fetch', _fetch_re) -def auth_builtin(username:str, imap:imaplib.IMAP4_SSL, +def auth_builtin(username:str, imap:imaplib.IMAP4, conf:configparser.ConfigParser, server:str) -> None: logging.info('Logging in as %s', username) resp:Tuple[str, List[Union[bytes,Tuple[bytes,bytes]]]] - resp = imap.login(username, conf.get('retriever', 'password')) - if resp[0] != 'OK': - raise Exception(f'login failed with {resp} as user {username} on {server}') - -# imaplib auth methods need to be in the form of callables, and they all -# requre both additional parameters and storage beyond what the function -# interface provides. -class GSSAPI_handler(): - gss_vc:gssapi.SecurityContext - username:str - - def __init__(self, server:str, username:str) -> None: - name = gssapi.Name(f'imap@{server}', gssapi.NameType.hostbased_service) - self.gss_vc = gssapi.SecurityContext(usage="initiate", name=name) - self.username = username - - def __call__(self, token:Optional[bytes]) -> bytes: - if token == b"": - token = None - if not self.gss_vc.complete: - response = self.gss_vc.step(token) - return response if response else b"" # type: ignore - elif token is None: - return b"" - - response = self.gss_vc.unwrap(token) - - # Preserve the "length" of the message we received, and set the first - # byte to one. If username is provided, it's next. - reply:List[int] = [] - reply[0:4] = response.message[0:4] - reply[0] = 1 - if self.username: - reply[5:] = self.username.encode("utf-8") - - response = self.gss_vc.wrap(bytes(reply), response.encrypted) - return response.message if response.message else b"" # type: ignore - -def auth_gssapi(username:str, imap:imaplib.IMAP4_SSL, + try: + imap.login(username, conf.get('retriever', 'password')) + except Exception as e: + raise Exception(f'login failed with {e} as user {username} on {server}') + +if gssapi: + # imaplib auth methods need to be in the form of callables, and they all + # requre both additional parameters and storage beyond what the function + # interface provides. + class GSSAPI_handler(): + gss_vc:gssapi.SecurityContext + username:str + + def __init__(self, server:str, username:str) -> None: + name = gssapi.Name(f'imap@{server}', + gssapi.NameType.hostbased_service) + self.gss_vc = gssapi.SecurityContext(usage="initiate", name=name) + self.username = username + + def __call__(self, token:Optional[bytes]) -> bytes: + if token == b"": + token = None + if not self.gss_vc.complete: + response = self.gss_vc.step(token) + return response if response else b"" # type: ignore + elif token is None: + return b"" + + response = self.gss_vc.unwrap(token) + + # Preserve the "length" of the message we received, and set the + # first byte to one. If username is provided, it's next. + reply:List[int] = [] + reply[0:4] = response.message[0:4] + reply[0] = 1 + if self.username: + reply[5:] = self.username.encode("utf-8") + + response = self.gss_vc.wrap(bytes(reply), response.encrypted) + return response.message if response.message else b"" # type: ignore + +def auth_gssapi(username:str, imap:imaplib.IMAP4, conf:configparser.ConfigParser, server:str) -> None: if not gssapi: raise Exception('Kerberos requested, but python3-gssapi not found') @@ -182,7 +185,7 @@ def scan_msgs(configfile:str, verbose:bool) -> None: ctx.set_ciphers(ssl_ciphers) server:str = conf.get('retriever', 'server') - with imaplib.IMAP4_SSL(host=server, #type: ignore + with imaplib.IMAP4_SSL(host=server, port=int(conf.get('retriever', 'port', fallback=993)), ssl_context=ctx) as imap: username:str = conf.get('retriever', 'username') @@ -210,7 +213,7 @@ def scan_msgs(configfile:str, verbose:bool) -> None: raise Exception(f'selection failed: {resp}') if len(resp[1]) != 1: raise Exception(f'expected exactly one EXISTS response from select, got {resp[1]}') - data:Union[bytes,Tuple[bytes,bytes]] = resp[1][0] + data:Optional[bytes] = resp[1][0] if not isinstance(data, bytes): raise Exception(f'expected bytes in response to SELECT, got {data}') n:int = int(data) @@ -220,10 +223,9 @@ def scan_msgs(configfile:str, verbose:bool) -> None: pull_msgs(imap, n, mdst, on_size_mismatch, delete) logging.getLogger().setLevel(oldloglevel) -def pull_msgs(imap:imaplib.IMAP4_SSL, n:int, mdst:mailbox.Maildir, +def pull_msgs(imap:imaplib.IMAP4, n:int, mdst:mailbox.Maildir, on_size_mismatch:OnSizeMismatch, delete:bool) -> None: sizes_mismatched:List[int] = [] - resp:Tuple[str, List[Union[bytes,Tuple[bytes,bytes]]]] resp = imap.fetch('1:%d'%(n), '(UID RFC822.SIZE)') if resp[0] != 'OK': raise Exception(f'initial FETCH 1:{n} not OK ({resp})') diff --git a/maildir-import-patch b/maildir-import-patch index f69a5e6..605435b 100755 --- a/maildir-import-patch +++ b/maildir-import-patch @@ -1,8 +1,8 @@ -#!/usr/bin/python3 +#!/usr/bin/perl # maildir-import-patch -- import a git patch series into a maildir -# Copyright (C) 2019 Sean Whitton +# Copyright (C) 2019-2020 Sean Whitton # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -17,40 +17,60 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <https://www.gnu.org/licenses/>. -import os -import sys -import mailbox -import shutil -import subprocess -import tempfile - -def eprint(*args, **kwargs): - print(*args, file=sys.stderr, **kwargs) - -us = os.path.basename(sys.argv[0]) - -if len(sys.argv) < 2: - eprint(us + ": usage: " + us + " MAILDIR [git-format-patch(1) args]") - sys.exit(1) - -if not shutil.which("git"): - eprint(us + ": this script requires git to be installed") - sys.exit(1) - -dest_path = sys.argv[1] -dest = mailbox.Maildir(dest_path) - -cmd = [ - "git", "format-patch", - "--thread=shallow", "--no-cc", "--no-to", - "--stdout", -] -user_args = sys.argv[2:] - -with tempfile.NamedTemporaryFile() as fp: - subprocess.run(cmd + user_args, stdout=fp) - source = mailbox.mbox(fp.name) - dest.lock() - for message in source: - dest.add(message) - dest.close() +use strict; +use warnings; + +use Cwd; +use File::Basename; +use File::Temp (); +use File::Which; +use List::MoreUtils "firstidx"; +use Mail::Box::Manager; + +my $us = basename $0; +my $maildir_dir = shift + or die "$us: usage: $us MAILDIR [git-format-patch(1) args]\n"; +which "git" or die "$us: this script requires git to be installed\n"; +eval { require Git::Wrapper } + or die "$us: this script requires Git::Wrapper (libgit-wrapper-perl)\n"; + +my $git = Git::Wrapper->new(getcwd); +my $toplevel; +{ + local $@; + eval { ($toplevel) = $git->rev_parse({ show_toplevel => 1 }) }; + $@ and die "$us: pwd doesn't look like a git repository ..\n"; +} +$toplevel =~ s/\.git$//; + +if (firstidx(sub { /^--subject-prefix/ }, @ARGV) == -1) { + my $subject_prefix; + my $rfc_idx = firstidx { /^--rfc$/ } @ARGV; + if ($rfc_idx == -1) { + $subject_prefix = "PATCH"; + } else { + $subject_prefix = "RFC PATCH"; + splice @ARGV, $rfc_idx, 1; + } + my ($project) = $git->config(qw|--local --get --default|, + basename($toplevel), "mailscripts.project-shortname"); + unshift @ARGV, "--subject-prefix=$subject_prefix $project imported"; +} + +my $mgr = Mail::Box::Manager->new; +my $maildir + = $mgr->open($maildir_dir, type => "maildir", access => "w", create => 1); + +my $mbox_file = File::Temp->new; +my $pid = fork; +die "fork() failed: $!" unless defined $pid; +unless ($pid) { + open STDOUT, ">&=", $mbox_file->fileno + or die "couldn't reopen child's STDOUT: $!"; + exec qw(git format-patch --no-cc --no-to --stdout --thread=shallow), @ARGV; +} +$mbox_file->close; +wait; + +my $mbox = $mgr->open($mbox_file->filename, type => "mbox", access => "r"); +$mgr->copyMessage($maildir, $_) for $mbox->messages; diff --git a/maildir-import-patch.1.pod b/maildir-import-patch.1.pod index a3ef110..7d728e4 100644 --- a/maildir-import-patch.1.pod +++ b/maildir-import-patch.1.pod @@ -34,6 +34,21 @@ Over in your MUA, you can then reply to each e-mail in the new thread generated by B<maildir-import-patch>, providing line-by-line feedback on Bob's work. +=head1 GIT CONFIGURATION KEYS + +=over 4 + +=item B<mailscripts.project-shortname> + +Short identifier for the project to be prepended to the subject lines +of generated patches, like this: "[PATCH shortname imported]". If +unset, defaults to the name of the root directory of the repository. + +Ignored if any I<--subject-prefix> option is present in the +arguments to be passed on to git-format-patch(1). + +=back + =head1 SEE ALSO notmuch-import-patch(1), git-format-patch(1), git-send-email(1) diff --git a/mailscripts.el b/mailscripts.el index 0658c76..2280f5a 100644 --- a/mailscripts.el +++ b/mailscripts.el @@ -1,7 +1,7 @@ ;;; mailscripts.el --- functions to access tools in the mailscripts package ;; Author: Sean Whitton <spwhitton@spwhitton.name> -;; Version: 0.18 +;; Version: 0.20 ;; Package-Requires: (notmuch projectile) ;; Copyright (C) 2018, 2019 Sean Whitton @@ -34,6 +34,17 @@ E.g. `email/'." :type 'string :group 'mailscripts) +(defcustom mailscripts-detach-head-from-existing-branch nil + "Whether to detach HEAD before applying patches to an existing branch. + +This is useful if you want to manually review the result of +applying patches before updating any of your existing branches, +or for quick, ad hoc testing of a patch series. + +Note that this does not prevent the creation of new branches." + :type 'boolean + :group 'mailscripts) + ;;;###autoload (defun notmuch-slurp-debbug (bug &optional no-open) "Slurp Debian bug with bug number BUG and open the thread in notmuch. @@ -67,7 +78,7 @@ See notmuch-extract-patch(1) manpage for limitations: in particular, this Emacs Lisp function supports passing only entire threads to the notmuch-extract-patch(1) command." (interactive - "Dgit repo: \nsbranch name (or leave blank to apply to current HEAD): \nP") + "Dgit repo: \nsnew branch name (or leave blank to apply to current HEAD): \nP") (let ((thread-id ;; If `notmuch-show' was called with a notmuch query rather ;; than a thread ID, as `org-notmuch-follow-link' in @@ -109,18 +120,18 @@ Patches are applied using git-am(1), so we only consider attachments with filenames which look like they were generated by git-format-patch(1)." (interactive - "Dgit repo: \nsbranch name (or leave blank to apply to current HEAD): ") + "Dgit repo: \nsnew branch name (or leave blank to apply to current HEAD): ") (with-current-notmuch-show-message (let ((default-directory (expand-file-name repo)) - (mm-handle (mm-dissect-buffer))) + (mm-handle (mm-dissect-buffer t))) (mailscripts--check-out-branch branch) (notmuch-foreach-mime-part (lambda (p) (let* ((disposition (mm-handle-disposition p)) (filename (cdr (assq 'filename disposition)))) (and filename - (string-match - "^\\(v[0-9]+-\\)?[0-9]+-.+\.\\(patch\\|diff\\|txt\\)$" filename) + (string-match "^\\(v?[0-9]+\\)-.+\\.\\(patch\\|diff\\|txt\\)$" + filename) (mm-pipe-part p "git am")))) mm-handle)))) @@ -131,7 +142,9 @@ git-format-patch(1)." (mailscripts--projectile-repo-and-branch 'notmuch-extract-message-patches)) (defun mailscripts--check-out-branch (branch) - (unless (string= branch "") + (if (string= branch "") + (when mailscripts-detach-head-from-existing-branch + (call-process-shell-command "git checkout --detach")) (call-process-shell-command (format "git checkout -b %s" (shell-quote-argument diff --git a/notmuch-slurp-debbug b/notmuch-slurp-debbug index c187596..ad0db47 100755 --- a/notmuch-slurp-debbug +++ b/notmuch-slurp-debbug @@ -43,8 +43,7 @@ my $maildir; my $conf_r = $ENV{XDG_CONFIG_HOME} || catfile $ENV{HOME}, ".config"; my $conf_f = catfile $conf_r, "mailscripts", "notmuch-slurp-debbug"; if (-f $conf_f) { - my $Config = Config::Tiny::read($conf_f); - $maildir = $Config->{_}->{maildir}; + $maildir = Config::Tiny->new->read($conf_f)->{_}->{maildir}; } else { # default to where a lot of people have their inbox chomp(my $database_path = `notmuch config get database.path`); |