summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean Whitton <spwhitton@spwhitton.name>2020-05-11 15:25:35 -0700
committerSean Whitton <spwhitton@spwhitton.name>2020-05-11 15:25:35 -0700
commit75df035544868a65e2a3d5603e65fc6f578e1550 (patch)
treeb8b0cfde404dcb0dd84e5b986f5107ae560b7b9c
parentc92a2b7d7aa9467e9ff69643ee46874dfb9eb829 (diff)
parentab4acbc313dc1a20e166045e61c603053c4f00dc (diff)
downloadmailscripts-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.rst60
-rw-r--r--debian/changelog28
-rw-r--r--debian/control1
-rwxr-xr-ximap-dl90
-rwxr-xr-xmaildir-import-patch98
-rw-r--r--maildir-import-patch.1.pod15
-rw-r--r--mailscripts.el27
-rwxr-xr-xnotmuch-slurp-debbug3
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,
diff --git a/imap-dl b/imap-dl
index 5a8494c..fac7487 100755
--- a/imap-dl
+++ b/imap-dl
@@ -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`);