[PATCHv2] cli: Hooks for tag-command
authorDominik Peteler <dominik@with-h.at>
Wed, 18 Jul 2012 18:09:30 +0000 (20:09 +0200)
committerW. Trevor King <wking@tremily.us>
Fri, 7 Nov 2014 17:48:22 +0000 (09:48 -0800)
25/3e05db5ba6493f5211171b696f95609518ab03 [new file with mode: 0644]

diff --git a/25/3e05db5ba6493f5211171b696f95609518ab03 b/25/3e05db5ba6493f5211171b696f95609518ab03
new file mode 100644 (file)
index 0000000..f9c488f
--- /dev/null
@@ -0,0 +1,337 @@
+Return-Path: <dominik@with-h.at>\r
+X-Original-To: notmuch@notmuchmail.org\r
+Delivered-To: notmuch@notmuchmail.org\r
+Received: from localhost (localhost [127.0.0.1])\r
+       by olra.theworths.org (Postfix) with ESMTP id D222F431FAF\r
+       for <notmuch@notmuchmail.org>; Wed, 18 Jul 2012 11:11:14 -0700 (PDT)\r
+X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
+X-Spam-Flag: NO\r
+X-Spam-Score: 0\r
+X-Spam-Level: \r
+X-Spam-Status: No, score=0 tagged_above=-999 required=5 tests=[none]\r
+       autolearn=disabled\r
+Received: from olra.theworths.org ([127.0.0.1])\r
+       by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)\r
+       with ESMTP id MF9c3hsdbnQO for <notmuch@notmuchmail.org>;\r
+       Wed, 18 Jul 2012 11:11:10 -0700 (PDT)\r
+Received: from www73.your-server.de (www73.your-server.de [213.133.104.73])\r
+       (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits))\r
+       (No client certificate requested)\r
+       by olra.theworths.org (Postfix) with ESMTPS id 6E715431FAE\r
+       for <notmuch@notmuchmail.org>; Wed, 18 Jul 2012 11:11:10 -0700 (PDT)\r
+Received: from [78.46.5.203] (helo=sslproxy01.your-server.de)\r
+       by www73.your-server.de with esmtpsa (TLSv1:AES256-SHA:256)\r
+       (Exim 4.74) (envelope-from <dominik@with-h.at>)\r
+       id 1SrYiF-0003Pp-V6; Wed, 18 Jul 2012 20:11:03 +0200\r
+Received: from [132.187.246.85] (helo=fuckup.wlan1.rzuw)\r
+       by sslproxy01.your-server.de with esmtpsa\r
+       (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.72)\r
+       (envelope-from <dominik@with-h.at>)\r
+       id 1SrYi8-0006ph-86; Wed, 18 Jul 2012 20:10:56 +0200\r
+From: Dominik Peteler <dominik@with-h.at>\r
+To: notmuch@notmuchmail.org\r
+Subject: [PATCHv2] cli: Hooks for tag-command\r
+Date: Wed, 18 Jul 2012 20:09:30 +0200\r
+Message-Id: <1342634970-12991-1-git-send-email-dominik@with-h.at>\r
+X-Mailer: git-send-email 1.7.11.2\r
+In-Reply-To:\r
+ <CAB+hUn_XHQQ9MDaVXvOMzfM8SdKCWUgnjNQvxJaQWWyc3H621Q@mail.gmail.com>\r
+X-Authenticated-Sender: dude@with-h.at\r
+X-Virus-Scanned: Clear (ClamAV 0.97.3/15151/Wed Jul 18 17:49:52 2012)\r
+X-Mailman-Approved-At: Wed, 18 Jul 2012 13:16:17 -0700\r
+Cc: Dominik Peteler <dominik@with-h.at>\r
+X-BeenThere: notmuch@notmuchmail.org\r
+X-Mailman-Version: 2.1.13\r
+Precedence: list\r
+List-Id: "Use and development of the notmuch mail system."\r
+       <notmuch.notmuchmail.org>\r
+List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
+       <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
+List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
+List-Post: <mailto:notmuch@notmuchmail.org>\r
+List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
+List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
+       <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
+X-List-Received-Date: Wed, 18 Jul 2012 18:11:15 -0000\r
+\r
+hello,\r
+\r
+I improved my patch according to Janis mail:\r
+ * new cli syntax: notmuch tag [ --no-hooks ] -- <tag ops> [ -- ] <search terms>\r
+ * adjusted man pages and wrote tests\r
+\r
+I had the idea to improve this feature by passing the message-ids or the filename to the hooks.\r
+What's your opinion about that ? Any suggestions ?\r
+\r
+regards\r
+\r
+dominik\r
+\r
+\r
+\r
+There are two hooks:\r
+ * pre-tag: Run before tagging\r
+ * post-tag: Run after\r
+\r
+This allows users to react on changes of tags. For example,\r
+you might want to move a message to a special Maildir\r
+depending on its notmuch tags.\r
+---\r
+ man/man1/notmuch-tag.1   | 22 +++++++++++++++++++-\r
+ man/man5/notmuch-hooks.5 | 19 ++++++++++++++++++\r
+ notmuch-tag.c            | 52 +++++++++++++++++++++++++++++++++++++++++++++---\r
+ test/hooks               | 36 +++++++++++++++++++++++++++++++++\r
+ test/tagging             | 28 ++++++++++++++++++++++++++\r
+ 5 files changed, 153 insertions(+), 4 deletions(-)\r
+\r
+diff --git a/man/man1/notmuch-tag.1 b/man/man1/notmuch-tag.1\r
+index d810e1b..e00e189 100644\r
+--- a/man/man1/notmuch-tag.1\r
++++ b/man/man1/notmuch-tag.1\r
+@@ -4,7 +4,11 @@ notmuch-tag \- add/remove tags for all messages matching the search terms\r
+ \r
+ .SH SYNOPSIS\r
+ .B notmuch tag\r
+-.RI  "+<" tag> "|\-<" tag "> [...] [\-\-] <" search-term ">..."\r
++.RI  "+<" tag "> |\-<" tag "> [...] [\-\-] <" search-term ">..."\r
++\r
++.B notmuch tag\r
++.RB "[" --no-hooks "]"\r
++.RI "\-\- +<" tag "> |\-<" tag "> [...] \-\- <" search-term ">..."\r
+ \r
+ .SH DESCRIPTION\r
+ \r
+@@ -29,6 +33,22 @@ updates the maildir flags according to tag changes if the\r
+ configuration option is enabled. See \fBnotmuch-config\fR(1) for\r
+ details.\r
+ \r
++The\r
++.B tag\r
++command supports hooks. See  \fBnotmuch-hooks(5)\fR\r
++for more details on hooks.\r
++\r
++Supported options for\r
++.B tag\r
++include\r
++.RS 4\r
++.TP 4\r
++.BR \-\-no\-hooks\r
++\r
++Prevents hooks from being run.\r
++.RE\r
++.RE\r
++\r
+ .SH SEE ALSO\r
+ \r
+ \fBnotmuch\fR(1), \fBnotmuch-config\fR(1), \fBnotmuch-count\fR(1),\r
+diff --git a/man/man5/notmuch-hooks.5 b/man/man5/notmuch-hooks.5\r
+index b914a29..e193ef5 100644\r
+--- a/man/man5/notmuch-hooks.5\r
++++ b/man/man5/notmuch-hooks.5\r
+@@ -38,6 +38,25 @@ the scan or import.\r
+ Typically this hook is used to perform additional query\-based tagging on the\r
+ imported messages.\r
+ .RE\r
++.RS 4\r
++.TP 4\r
++.B pre\-tag\r
++This hook is invoked by the\r
++.B tag\r
++command before tagging messages. If this\r
++hook exits with a non-zero status, notmuch will abort further processing of the\r
++.B tag\r
++command.\r
++.RE\r
++.RS 4\r
++.TP 4\r
++.B post\-tag\r
++This hook is invoked by the\r
++.B tag\r
++command after messages have been tagged. The hook will not be run if there have been any errors during\r
++the tagging.\r
++.RE\r
++\r
+ \r
+ .SH SEE ALSO\r
+ \r
+diff --git a/notmuch-tag.c b/notmuch-tag.c\r
+index 7d18639..7572059 100644\r
+--- a/notmuch-tag.c\r
++++ b/notmuch-tag.c\r
+@@ -174,9 +174,17 @@ notmuch_tag_command (void *ctx, int argc, char *argv[])\r
+     int tag_ops_count = 0;\r
+     char *query_string;\r
+     notmuch_config_t *config;\r
++    const char *db_path;\r
+     notmuch_database_t *notmuch;\r
+     struct sigaction action;\r
+     notmuch_bool_t synchronize_flags;\r
++    /* Points to the position of the "--" delimiters, e. g.\r
++     *    <optional arguments> arg_delimiters[0] <tag ops> arg_delimiters[1] <search terms>\r
++     *\r
++     * arg_delimiters[0] may remain -1 if there are no arguments given\r
++     * arg_delimiters[0] may remain -1 if there is no delimiter between tag ops and search terms */\r
++    int arg_delimiters[2] = {-1, -1};\r
++    notmuch_bool_t run_hooks = TRUE;\r
+     int i;\r
+     int ret;\r
+ \r
+@@ -197,11 +205,37 @@ notmuch_tag_command (void *ctx, int argc, char *argv[])\r
+       return 1;\r
+     }\r
+ \r
++    /* Determine position of delimiters */\r
+     for (i = 0; i < argc; i++) {\r
+       if (strcmp (argv[i], "--") == 0) {\r
+-          i++;\r
+-          break;\r
++          if (arg_delimiters[1] == -1) {\r
++              arg_delimiters[1] = i;\r
++          } else if (arg_delimiters[0] == -1) {\r
++              arg_delimiters[0] = arg_delimiters[1];\r
++              arg_delimiters[1] = i;\r
++          } else {\r
++              fprintf (stderr, "Error: 'notmuch tag' requires delimiter \"--\" at most two times.\n");\r
++              return 1;\r
++          }\r
+       }\r
++    }\r
++\r
++    /* Process arguments if present */\r
++    for (i = 0; i < arg_delimiters[0]; i++) {\r
++      if (strcmp (argv[i], "--no-hooks") == 0) {\r
++          run_hooks = FALSE;\r
++      } else {\r
++          fprintf (stderr, "Error: 'notmuch tag' doesn't recognize argument '%s'.\n", argv[i]);\r
++          return 1;\r
++      }\r
++    }\r
++\r
++    /* Set arg_delimiters[1] to argc if no delimiters at all are present */\r
++    if (arg_delimiters[1] == -1)\r
++          arg_delimiters[1] = argc;\r
++\r
++    /* Read tag ops */\r
++    for (i = arg_delimiters[0]+1; i < arg_delimiters[1]; i++) {\r
+       if (argv[i][0] == '+' || argv[i][0] == '-') {\r
+           tag_ops[tag_ops_count].tag = argv[i] + 1;\r
+           tag_ops[tag_ops_count].remove = (argv[i][0] == '-');\r
+@@ -229,7 +263,15 @@ notmuch_tag_command (void *ctx, int argc, char *argv[])\r
+     if (config == NULL)\r
+       return 1;\r
+ \r
+-    if (notmuch_database_open (notmuch_config_get_database_path (config),\r
++    db_path = notmuch_config_get_database_path (config);\r
++\r
++    if (run_hooks) {\r
++      ret = notmuch_run_hook (db_path, "pre-tag");\r
++      if (ret)\r
++          return ret;\r
++    }\r
++\r
++    if (notmuch_database_open (db_path,\r
+                              NOTMUCH_DATABASE_MODE_READ_WRITE, &notmuch))\r
+       return 1;\r
+ \r
+@@ -239,5 +281,9 @@ notmuch_tag_command (void *ctx, int argc, char *argv[])\r
+ \r
+     notmuch_database_destroy (notmuch);\r
+ \r
++    if (!ret && run_hooks) {\r
++      ret = notmuch_run_hook (db_path, "post-tag");\r
++    }\r
++\r
+     return ret;\r
+ }\r
+diff --git a/test/hooks b/test/hooks\r
+index 77e8569..ae857cc 100755\r
+--- a/test/hooks\r
++++ b/test/hooks\r
+@@ -31,6 +31,7 @@ rm_hooks () {\r
+ # add a message to generate mail dir and database\r
+ add_message\r
+ \r
++# {pre,post}-new hooks\r
+ test_begin_subtest "pre-new is run"\r
+ rm_hooks\r
+ generate_message\r
+@@ -101,4 +102,39 @@ EOF\r
+ chmod +x "${HOOK_DIR}/pre-new"\r
+ test_expect_code 1 "hook execution failure" "notmuch new"\r
+ \r
++\r
++\r
++# {pre,post}-tag hooks\r
++test_begin_subtest "pre-tag is run"\r
++rm_hooks\r
++generate_message\r
++create_echo_hook "pre-tag" expected output\r
++notmuch tag +foo -- '*' > /dev/null\r
++test_expect_equal_file expected output\r
++\r
++test_begin_subtest "post-tag is run"\r
++rm_hooks\r
++generate_message\r
++create_echo_hook "post-tag" expected output\r
++notmuch tag +foo -- '*'  > /dev/null\r
++test_expect_equal_file expected output\r
++\r
++test_begin_subtest "pre-tag is run before post-new"\r
++rm_hooks\r
++generate_message\r
++create_echo_hook "pre-tag" pre-tag.expected pre-tag.output\r
++create_echo_hook "post-tag" post-tag.expected post-tag.output\r
++notmuch tag +foo -- '*'  > /dev/null\r
++test_expect_equal_file post-tag.expected post-tag.output\r
++\r
++test_begin_subtest "pre-tag non-zero exit status (hook status)"\r
++rm_hooks\r
++generate_message\r
++create_failing_hook "pre-tag"\r
++output=`notmuch tag +foo -- '*'  2>&1`\r
++test_expect_equal "$output" "Error: pre-tag hook failed with status 13"\r
++\r
++# depends on the previous subtest leaving broken hook behind\r
++test_expect_code 1 "pre-tag non-zero exit status (notmuch status)" "notmuch tag +foo -- '*'"\r
++\r
+ test_done\r
+diff --git a/test/tagging b/test/tagging\r
+index e4782ed..5167f4f 100755\r
+--- a/test/tagging\r
++++ b/test/tagging\r
+@@ -46,4 +46,32 @@ test_expect_equal "$output" "\\r
+ thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; One (:\"  inbox tag1 unread)\r
+ thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Two (inbox tag1 tag4 unread)"\r
+ \r
++test_begin_subtest "Arguments mixed with tag ops"\r
++notmuch tag +-no-hooks --no-hooks -- One\r
++notmuch tag --no-hooks +-no-hooks -tag4 -- Two\r
++output=$(notmuch search \* | notmuch_search_sanitize)\r
++test_expect_equal "$output" "\\r
++thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; One (:\"  inbox tag1 unread)\r
++thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Two (-no-hooks inbox tag1 unread)"\r
++notmuch tag --no-hooks -- Two\r
++\r
++test_begin_subtest "Arguments with correct position"\r
++notmuch tag --no-hooks -- +tag4 -tag4 -- One\r
++output=$(notmuch search \* | notmuch_search_sanitize)\r
++test_expect_equal "$output" "\\r
++thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; One (:\"  inbox tag1 unread)\r
++thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Two (inbox tag1 unread)"\r
++\r
++test_begin_subtest "Missing arguments"\r
++notmuch tag -- +tag4 -tag4 -- One\r
++output=$(notmuch search \* | notmuch_search_sanitize)\r
++test_expect_equal "$output" "\\r
++thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; One (:\"  inbox tag1 unread)\r
++thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Two (inbox tag1 unread)"\r
++\r
++test_begin_subtest "Unknown argument"\r
++output=$(notmuch tag --no-blubb -- +tag4 -tag4 -- One 2>&1)\r
++test_expect_equal "$output" "\\r
++Error: 'notmuch tag' doesn't recognize argument '--no-blubb'."\r
++\r
+ test_done\r
+-- \r
+1.7.11.2\r
+\r