[PATCH mumi 0/1] Add CLI client to search for issues

  • Done
  • quality assurance status badge
Details
4 participants
  • Arun Isaac
  • Felix Lechner
  • Ludovic Courtès
  • Ricardo Wurmus
Owner
unassigned
Submitted by
Arun Isaac
Severity
normal
A
A
Arun Isaac wrote on 20 Feb 2023 02:38
(address . guix-patches@gnu.org)
20230220013821.27440-1-arunisaac@systemreboot.net
Hi Ricardo,

This patch adds a mumi CLI client that lets one search for issues from
the command line by calling mumi's GraphQL API. This patch requires a
.mumi/config file in the guix repo with the following contents.

Toggle snippet (5 lines)
((debbugs-host . "debbugs.gnu.org")
(patch-email-address . "guix-patches@gnu.org")
(mumi-host . "issues.guix.gnu.org"))

After adding .mumi/config, you can run search queries like

$ /path/to/mumi/pre-inst-env mumi search zig is:open

This is only the beginning. I have WIP patches that will add a "mumi
send-email" subcommand that will finally free us from our debbugs
dance when sending multiple patches. I will send them separately once
I have tested it more.

Regards,
Arun

Arun Isaac (1):
client: Add CLI client to search for issues.

Makefile.am | 1 +
mumi/client.scm | 108 ++++++++++++++++++++++++++++++++++++++++++++++++
scripts/mumi.in | 8 +++-
3 files changed, 116 insertions(+), 1 deletion(-)
create mode 100644 mumi/client.scm

--
2.38.1
A
A
Arun Isaac wrote on 20 Feb 2023 02:41
[PATCH mumi 1/1] client: Add CLI client to search for issues.
(address . 61645@debbugs.gnu.org)
20230220014139.27804-1-arunisaac@systemreboot.net
* mumi/client.scm: New file.
* scripts/mumi.in: Import (mumi client).
(show-mumi-usage): Document search subcommand.
(main): Add search subcommand.
* Makefile.am (SOURCES): Add mumi/client.scm.
---
Makefile.am | 1 +
mumi/client.scm | 108 ++++++++++++++++++++++++++++++++++++++++++++++++
scripts/mumi.in | 8 +++-
3 files changed, 116 insertions(+), 1 deletion(-)
create mode 100644 mumi/client.scm

Toggle diff (168 lines)
diff --git a/Makefile.am b/Makefile.am
index 8182fc3..a8c11a1 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -49,6 +49,7 @@ SOURCES = \
mumi/messages.scm \
mumi/jobs.scm \
mumi/send-email.scm \
+ mumi/client.scm \
mumi/config.scm \
mumi/debbugs.scm \
mumi/test-utils.scm \
diff --git a/mumi/client.scm b/mumi/client.scm
new file mode 100644
index 0000000..e4a0123
--- /dev/null
+++ b/mumi/client.scm
@@ -0,0 +1,108 @@
+;;; mumi -- Mediocre, uh, mail interface
+;;; Copyright © 2023 Arun Isaac <arunisaac@systemreboot.net>
+;;;
+;;; This file is part of mumi.
+;;;
+;;; mumi is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or
+;;; (at your option) any later version.
+;;;
+;;; mumi is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+;;; General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with mumi. If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (mumi client)
+ #:use-module (srfi srfi-19)
+ #:use-module (srfi srfi-43)
+ #:use-module (term ansi-color)
+ #:use-module (web uri)
+ #:use-module (kolam http)
+ #:use-module (mumi config)
+ #:use-module (mumi web view utils)
+ #:export (search))
+
+(define (git-top-level)
+ "Return the top-level directory of the current git repository."
+ (let loop ((curdir (getcwd)))
+ (cond
+ ((file-exists? (string-append curdir "/.git"))
+ curdir)
+ ((string=? curdir "/")
+ (error "No git top level found"))
+ (else
+ (loop (dirname curdir))))))
+
+(define (client-config-directory)
+ "Return client configuration directory."
+ (string-append (git-top-level) "/.mumi"))
+
+(define (client-config key)
+ "Return client configuration value corresponding to KEY."
+ (or (assq-ref (call-with-input-file (string-append (client-config-directory)
+ "/config")
+ read)
+ key)
+ (case key
+ ((mumi-scheme) 'https)
+ (else (format (current-error-port)
+ "Key '~a not configured for mumi client.~%"
+ key)))))
+
+(define (graphql-endpoint)
+ "Return GraphQL endpoint."
+ (uri->string
+ (build-uri (client-config 'mumi-scheme)
+ #:host (client-config 'mumi-host)
+ #:path "/graphql")))
+
+(define (iso8601->date str)
+ "Convert ISO-8601 date/time+zone string to date object."
+ (string->date str "~Y-~m-~dT~H:~M:~S~z"))
+
+(define (list-issue issue)
+ "List issue described by ISSUE association list."
+ (display (colorize-string
+ (string-append "#"
+ (number->string (assoc-ref issue "number")))
+ 'YELLOW))
+ (display " ")
+ (unless (assoc-ref issue "open")
+ (display (colorize-string "?" 'BOLD 'GREEN))
+ (display " "))
+ (display (colorize-string
+ (assoc-ref issue "title")
+ 'MAGENTA 'UNDERLINE))
+ (newline)
+ (display (string-append
+ "opened "
+ (colorize-string (time->string
+ (iso8601->date (assoc-ref issue "date")))
+ 'CYAN)
+ " by "
+ (colorize-string
+ (let ((submitter (assoc-ref issue "submitter")))
+ (if (eq? (assoc-ref submitter "name") 'null)
+ (assoc-ref submitter "address")
+ (assoc-ref submitter "name")))
+ 'CYAN)))
+ (newline))
+
+(define (search query)
+ "Search for issues with QUERY and list results."
+ (vector-for-each (lambda (_ issue)
+ (list-issue issue))
+ (assoc-ref
+ (graphql-http-get (graphql-endpoint)
+ `(document
+ (query (#(issues #:search ,query)
+ number
+ title
+ open
+ date
+ (submitter name address)))))
+ "issues")))
diff --git a/scripts/mumi.in b/scripts/mumi.in
index 755dfb3..9b61729 100644
--- a/scripts/mumi.in
+++ b/scripts/mumi.in
@@ -4,7 +4,7 @@
!#
;;; mumi -- Mediocre, uh, mail interface
;;; Copyright © 2016, 2017, 2019, 2020 Ricardo Wurmus <rekado@elephly.net>
-;;; Copyright © 2018, 2021 Arun Isaac <arunisaac@systemreboot.net>
+;;; Copyright © 2018, 2021, 2023 Arun Isaac <arunisaac@systemreboot.net>
;;;
;;; This file is part of mumi.
;;;
@@ -26,6 +26,7 @@
(system repl server)
(ice-9 match)
(ice-9 format)
+ ((mumi client) #:prefix client:)
(mumi config)
((mumi debbugs)
#:select (extract-bug-numbers))
@@ -116,6 +117,9 @@
(define (show-mumi-usage)
(format (current-error-port)
"
+ `mumi search QUERY':
+ search mumi for issues.
+
`mumi web [--address=address] [--port=port] [--listen-repl[=port]] [--disable-mailer]':
start the application web server.
@@ -132,6 +136,8 @@
(exit 1))
(match (cdr (program-arguments))
+ (("search" . query-strings)
+ (client:search (string-join query-strings)))
(("mailer" . rest)
(let* ((opts (parse-options rest))
(sender (assoc-ref opts 'sender))
--
2.38.1
A
A
Arun Isaac wrote on 21 Feb 2023 01:32
[PATCH v2 0/4] Add mumi CLI client.
(address . 61645@debbugs.gnu.org)
20230221003253.5285-1-arunisaac@systemreboot.net
On second thoughts, I think I'll also include the mumi send-email
patches in this bug report. To test this patchset, do the following.

The following needs to be put in .mumi/config in the guix git repo.
Toggle snippet (5 lines)
((debbugs-host . "debbugs.gnu.org")
(patch-email-address . "guix-patches@gnu.org")
(mumi-host . "issues.guix.gnu.org"))

Then, from the guix git repo, it is possible to search for issues with

$ mumi search zig is:open

Set a "current issue" with

$ mumi current 61645

The "current issue" is client-side state that represents the current
issue one is working on. It is used to automatically infer such things
as the email address to send patches to.

Send patches to the current issue with

$ mumi send-email file1.patch file2.patch

`mumi send-email' also works correctly with new issues. We first open
a new issue with

$ mumi new

Then, we send patches using `mumi send-email'. The first patch is sent
to guix-patches@gnu.org and the remaining patches to the bug specific
address---our usual debbugs dance, in other words.

$ mumi send-email file1.patch file2.patch

I think this patchset works, but it would be nice if someone
independently verified it and provided some feedback before it is
applied.

Arun Isaac (4):
client: Add CLI client to search for issues.
client: Support checking in to a specific issue.
client: Support sending email to issues.
Set only GUILE_LOAD_PATH when running tests.

Makefile.am | 5 +-
mumi/client.scm | 257 +++++++++++++++++++++++++++++++++++++++++++++++
scripts/mumi.in | 32 +++++-
tests/client.scm | 93 +++++++++++++++++
4 files changed, 384 insertions(+), 3 deletions(-)
create mode 100644 mumi/client.scm
create mode 100644 tests/client.scm

--
2.38.1
A
A
Arun Isaac wrote on 21 Feb 2023 01:33
[PATCH v2 1/4] client: Add CLI client to search for issues.
(address . 61645@debbugs.gnu.org)
20230221003318.5334-1-arunisaac@systemreboot.net
* mumi/client.scm: New file.
* scripts/mumi.in: Import (mumi client).
(show-mumi-usage): Document search subcommand.
(main): Add search subcommand.
* Makefile.am (SOURCES): Add mumi/client.scm.
---
Makefile.am | 1 +
mumi/client.scm | 108 ++++++++++++++++++++++++++++++++++++++++++++++++
scripts/mumi.in | 8 +++-
3 files changed, 116 insertions(+), 1 deletion(-)
create mode 100644 mumi/client.scm

Toggle diff (168 lines)
diff --git a/Makefile.am b/Makefile.am
index 8182fc3..a8c11a1 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -49,6 +49,7 @@ SOURCES = \
mumi/messages.scm \
mumi/jobs.scm \
mumi/send-email.scm \
+ mumi/client.scm \
mumi/config.scm \
mumi/debbugs.scm \
mumi/test-utils.scm \
diff --git a/mumi/client.scm b/mumi/client.scm
new file mode 100644
index 0000000..e4a0123
--- /dev/null
+++ b/mumi/client.scm
@@ -0,0 +1,108 @@
+;;; mumi -- Mediocre, uh, mail interface
+;;; Copyright © 2023 Arun Isaac <arunisaac@systemreboot.net>
+;;;
+;;; This file is part of mumi.
+;;;
+;;; mumi is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or
+;;; (at your option) any later version.
+;;;
+;;; mumi is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+;;; General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with mumi. If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (mumi client)
+ #:use-module (srfi srfi-19)
+ #:use-module (srfi srfi-43)
+ #:use-module (term ansi-color)
+ #:use-module (web uri)
+ #:use-module (kolam http)
+ #:use-module (mumi config)
+ #:use-module (mumi web view utils)
+ #:export (search))
+
+(define (git-top-level)
+ "Return the top-level directory of the current git repository."
+ (let loop ((curdir (getcwd)))
+ (cond
+ ((file-exists? (string-append curdir "/.git"))
+ curdir)
+ ((string=? curdir "/")
+ (error "No git top level found"))
+ (else
+ (loop (dirname curdir))))))
+
+(define (client-config-directory)
+ "Return client configuration directory."
+ (string-append (git-top-level) "/.mumi"))
+
+(define (client-config key)
+ "Return client configuration value corresponding to KEY."
+ (or (assq-ref (call-with-input-file (string-append (client-config-directory)
+ "/config")
+ read)
+ key)
+ (case key
+ ((mumi-scheme) 'https)
+ (else (format (current-error-port)
+ "Key '~a not configured for mumi client.~%"
+ key)))))
+
+(define (graphql-endpoint)
+ "Return GraphQL endpoint."
+ (uri->string
+ (build-uri (client-config 'mumi-scheme)
+ #:host (client-config 'mumi-host)
+ #:path "/graphql")))
+
+(define (iso8601->date str)
+ "Convert ISO-8601 date/time+zone string to date object."
+ (string->date str "~Y-~m-~dT~H:~M:~S~z"))
+
+(define (list-issue issue)
+ "List issue described by ISSUE association list."
+ (display (colorize-string
+ (string-append "#"
+ (number->string (assoc-ref issue "number")))
+ 'YELLOW))
+ (display " ")
+ (unless (assoc-ref issue "open")
+ (display (colorize-string "?" 'BOLD 'GREEN))
+ (display " "))
+ (display (colorize-string
+ (assoc-ref issue "title")
+ 'MAGENTA 'UNDERLINE))
+ (newline)
+ (display (string-append
+ "opened "
+ (colorize-string (time->string
+ (iso8601->date (assoc-ref issue "date")))
+ 'CYAN)
+ " by "
+ (colorize-string
+ (let ((submitter (assoc-ref issue "submitter")))
+ (if (eq? (assoc-ref submitter "name") 'null)
+ (assoc-ref submitter "address")
+ (assoc-ref submitter "name")))
+ 'CYAN)))
+ (newline))
+
+(define (search query)
+ "Search for issues with QUERY and list results."
+ (vector-for-each (lambda (_ issue)
+ (list-issue issue))
+ (assoc-ref
+ (graphql-http-get (graphql-endpoint)
+ `(document
+ (query (#(issues #:search ,query)
+ number
+ title
+ open
+ date
+ (submitter name address)))))
+ "issues")))
diff --git a/scripts/mumi.in b/scripts/mumi.in
index 755dfb3..9b61729 100644
--- a/scripts/mumi.in
+++ b/scripts/mumi.in
@@ -4,7 +4,7 @@
!#
;;; mumi -- Mediocre, uh, mail interface
;;; Copyright © 2016, 2017, 2019, 2020 Ricardo Wurmus <rekado@elephly.net>
-;;; Copyright © 2018, 2021 Arun Isaac <arunisaac@systemreboot.net>
+;;; Copyright © 2018, 2021, 2023 Arun Isaac <arunisaac@systemreboot.net>
;;;
;;; This file is part of mumi.
;;;
@@ -26,6 +26,7 @@
(system repl server)
(ice-9 match)
(ice-9 format)
+ ((mumi client) #:prefix client:)
(mumi config)
((mumi debbugs)
#:select (extract-bug-numbers))
@@ -116,6 +117,9 @@
(define (show-mumi-usage)
(format (current-error-port)
"
+ `mumi search QUERY':
+ search mumi for issues.
+
`mumi web [--address=address] [--port=port] [--listen-repl[=port]] [--disable-mailer]':
start the application web server.
@@ -132,6 +136,8 @@
(exit 1))
(match (cdr (program-arguments))
+ (("search" . query-strings)
+ (client:search (string-join query-strings)))
(("mailer" . rest)
(let* ((opts (parse-options rest))
(sender (assoc-ref opts 'sender))
--
2.38.1
A
A
Arun Isaac wrote on 21 Feb 2023 01:33
[PATCH v2 2/4] client: Support checking in to a specific issue.
(address . 61645@debbugs.gnu.org)
20230221003326.5353-1-arunisaac@systemreboot.net
* mumi/client.scm: Import (srfi srfi-26).
(current-issue-file, current-issue-number): New functions.
(print-current-issue, set-current-issue!, clear-current-issue!): New
public functions.
* scripts/mumi.in (show-mumi-usage): Document current and new
subcommands.
(main): Add current and new subcommands.
---
mumi/client.scm | 48 +++++++++++++++++++++++++++++++++++++++++++++++-
scripts/mumi.in | 20 ++++++++++++++++++++
2 files changed, 67 insertions(+), 1 deletion(-)

Toggle diff (109 lines)
diff --git a/mumi/client.scm b/mumi/client.scm
index e4a0123..ae3a0a9 100644
--- a/mumi/client.scm
+++ b/mumi/client.scm
@@ -18,13 +18,17 @@
(define-module (mumi client)
#:use-module (srfi srfi-19)
+ #:use-module (srfi srfi-26)
#:use-module (srfi srfi-43)
#:use-module (term ansi-color)
#:use-module (web uri)
#:use-module (kolam http)
#:use-module (mumi config)
#:use-module (mumi web view utils)
- #:export (search))
+ #:export (search
+ print-current-issue
+ set-current-issue!
+ clear-current-issue!))
(define (git-top-level)
"Return the top-level directory of the current git repository."
@@ -106,3 +110,45 @@
date
(submitter name address)))))
"issues")))
+
+(define (current-issue-file)
+ "Return path to current issue number file."
+ (string-append (client-config-directory) "/current-issue"))
+
+(define (current-issue-number)
+ "Return current issue number."
+ (let ((issue-file (current-issue-file)))
+ (and (file-exists? issue-file)
+ (call-with-input-file issue-file
+ read))))
+
+(define (print-current-issue)
+ "Print current issue."
+ (let ((issue-number (current-issue-number)))
+ (if issue-number
+ (list-issue
+ (assoc-ref
+ (graphql-http-get (graphql-endpoint)
+ `(document
+ (query (#(issue #:number ,issue-number)
+ number
+ title
+ open
+ date
+ (submitter name address)))))
+ "issue"))
+ (begin
+ (format (current-error-port) "No current issue!~%")
+ (exit #f)))))
+
+(define (set-current-issue! issue-number)
+ "Set current issue number."
+ ;; TODO: Write file atomically.
+ (call-with-output-file (current-issue-file)
+ (cut write issue-number <>)))
+
+(define (clear-current-issue!)
+ "Clear current issue."
+ (let ((issue-file (current-issue-file)))
+ (when (file-exists? issue-file)
+ (delete-file issue-file))))
diff --git a/scripts/mumi.in b/scripts/mumi.in
index 9b61729..dfd082d 100644
--- a/scripts/mumi.in
+++ b/scripts/mumi.in
@@ -120,6 +120,12 @@
`mumi search QUERY':
search mumi for issues.
+ `mumi current [ISSUE-NUMBER]':
+ print or set current issue.
+
+ `mumi new':
+ clear current issue presumably to open a new one.
+
`mumi web [--address=address] [--port=port] [--listen-repl[=port]] [--disable-mailer]':
start the application web server.
@@ -138,6 +144,20 @@
(match (cdr (program-arguments))
(("search" . query-strings)
(client:search (string-join query-strings)))
+ (("current")
+ (client:print-current-issue))
+ (("current" issue-number-string)
+ (let ((issue-number (string->number issue-number-string)))
+ (if issue-number
+ (client:set-current-issue! issue-number)
+ (begin
+ (format (current-error-port)
+ "Invalid issue number `~a'~%"
+ issue-number-string)
+ (exit #f))))
+ (client:print-current-issue))
+ (("new")
+ (client:clear-current-issue!))
(("mailer" . rest)
(let* ((opts (parse-options rest))
(sender (assoc-ref opts 'sender))
--
2.38.1
A
A
Arun Isaac wrote on 21 Feb 2023 01:33
[PATCH v2 3/4] client: Support sending email to issues.
(address . 61645@debbugs.gnu.org)
20230221003336.5374-1-arunisaac@systemreboot.net
* mumi/client.scm: Import (rnrs io ports), (srfi srfi-71), (srfi
srfi-171), (ice-9 match), (ice-9 popen), (web client), (web response)
and (email email).
(issue-number-of-message, call-with-input-pipe, git-send-email): New
functions.
(send-email): New public function.
* scripts/mumi.in (show-mumi-usage): Document send-email subcommand.
(main): Add send-email subcommand.
* tests/client.scm: New file.
* Makefile.am (SCM_TESTS): Add tests/client.scm.
---
Makefile.am | 1 +
mumi/client.scm | 105 ++++++++++++++++++++++++++++++++++++++++++++++-
scripts/mumi.in | 5 +++
tests/client.scm | 93 +++++++++++++++++++++++++++++++++++++++++
4 files changed, 203 insertions(+), 1 deletion(-)
create mode 100644 tests/client.scm

Toggle diff (267 lines)
diff --git a/Makefile.am b/Makefile.am
index a8c11a1..86ba4f0 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -58,6 +58,7 @@ SOURCES = \
TEST_EXTENSIONS = .scm
SCM_TESTS = \
+ tests/client.scm \
tests/debbugs.scm \
tests/xapian.scm
diff --git a/mumi/client.scm b/mumi/client.scm
index ae3a0a9..09f83ee 100644
--- a/mumi/client.scm
+++ b/mumi/client.scm
@@ -17,18 +17,27 @@
;;; along with mumi. If not, see <http://www.gnu.org/licenses/>.
(define-module (mumi client)
+ #:use-module (rnrs io ports)
#:use-module (srfi srfi-19)
#:use-module (srfi srfi-26)
#:use-module (srfi srfi-43)
+ #:use-module (srfi srfi-71)
+ #:use-module (srfi srfi-171)
+ #:use-module (ice-9 match)
+ #:use-module (ice-9 popen)
#:use-module (term ansi-color)
+ #:use-module (web client)
+ #:use-module (web response)
#:use-module (web uri)
+ #:use-module (email email)
#:use-module (kolam http)
#:use-module (mumi config)
#:use-module (mumi web view utils)
#:export (search
print-current-issue
set-current-issue!
- clear-current-issue!))
+ clear-current-issue!
+ send-email))
(define (git-top-level)
"Return the top-level directory of the current git repository."
@@ -152,3 +161,97 @@
(let ((issue-file (current-issue-file)))
(when (file-exists? issue-file)
(delete-file issue-file))))
+
+(define* (issue-number-of-message message-id #:optional (retries 15))
+ "Return issue number that MESSAGE-ID belongs to. Retry RETRIES number
+of times with an interval of 60 seconds between retries."
+ ;; TODO: Re-implement this using our GraphQL endpoint once it
+ ;; supports retrieving the issue from a message ID. Later,
+ ;; re-implement this using a GraphQL subscription when kolam
+ ;; supports it.
+ (define (poll-issue-number-of-message message-id)
+ (let ((response _ (http-get (build-uri (client-config 'mumi-scheme)
+ #:host (client-config 'mumi-host)
+ #:path (string-append "/msgid/" message-id)))))
+ (and (>= (response-code response) 300)
+ (< (response-code response) 400)
+ (match (split-and-decode-uri-path
+ (uri-path (response-location response)))
+ (("issue" issue-number)
+ (string->number issue-number))))))
+
+ (let loop ((i retries))
+ (if (zero? i)
+ (begin
+ (format (current-error-port)
+ "Mail not acknowledged by issue tracker. Giving up.~%")
+ (exit #f))
+ (or (poll-issue-number-of-message message-id)
+ (begin
+ (let ((retry-interval 60))
+ (format (current-error-port)
+ "Trial ~a/~a: Server has not yet received our email. Will retry in ~a seconds.~%"
+ (1+ i) retries retry-interval)
+ (sleep retry-interval))
+ (loop (1- retries)))))))
+
+(define (call-with-input-pipe command proc)
+ "Call PROC with input pipe to COMMAND. COMMAND is a list of program
+arguments."
+ (match command
+ ((prog args ...)
+ (let ((port #f))
+ (dynamic-wind
+ (lambda ()
+ (set! port (apply open-pipe* OPEN_READ prog args)))
+ (cut proc port)
+ (cut close-pipe port))))))
+
+(define (git-send-email to patch)
+ "Send email using git send-email and return the message ID of the sent
+email."
+ (let ((command (list "git" "send-email"
+ (string-append "--to=" to)
+ patch)))
+ (display (string-join command))
+ (newline)
+ (call-with-input-pipe command
+ (lambda (port)
+ ;; FIXME: This messes up the order of stdout and stderr.
+ (let ((message-id
+ ;; Read till you get the Message ID.
+ (port-transduce (tlog (lambda (_ line)
+ (display line)
+ (newline)))
+ (rany (lambda (line)
+ (and (string-prefix-ci? "Message-ID:" line)
+ (assq-ref
+ (parse-email-headers
+ (string-append line "\n"))
+ 'message-id))))
+ get-line
+ port)))
+ ;; Pass through the rest.
+ (display (get-string-all port))
+ message-id)))))
+
+(define (send-email patches)
+ "Send PATCHES via email."
+ (match patches
+ ((first-patch other-patches ...)
+ ;; If an issue is current, send patches to that issue's email
+ ;; address. Else, send first patch to the patch email address and
+ ;; get an issue number. Then, send the remaining patches to that
+ ;; issue's email address.
+ (for-each (cute git-send-email
+ (string-append (number->string
+ (or (current-issue-number)
+ (issue-number-of-message
+ (git-send-email (client-config 'patch-email-address)
+ first-patch))))
+ "@"
+ (client-config 'debbugs-host))
+ <>)
+ (if (current-issue-number)
+ patches
+ other-patches)))))
diff --git a/scripts/mumi.in b/scripts/mumi.in
index dfd082d..2295328 100644
--- a/scripts/mumi.in
+++ b/scripts/mumi.in
@@ -126,6 +126,9 @@
`mumi new':
clear current issue presumably to open a new one.
+ `mumi send-email':
+ send patches to debbugs.
+
`mumi web [--address=address] [--port=port] [--listen-repl[=port]] [--disable-mailer]':
start the application web server.
@@ -158,6 +161,8 @@
(client:print-current-issue))
(("new")
(client:clear-current-issue!))
+ (("send-email" . patches)
+ (client:send-email patches))
(("mailer" . rest)
(let* ((opts (parse-options rest))
(sender (assoc-ref opts 'sender))
diff --git a/tests/client.scm b/tests/client.scm
new file mode 100644
index 0000000..2948aed
--- /dev/null
+++ b/tests/client.scm
@@ -0,0 +1,93 @@
+;;; mumi -- Mediocre, uh, mail interface
+;;; Copyright © 2023 Arun Isaac <arunisaac@systemreboot.net>
+;;;
+;;; This program is free software: you can redistribute it and/or
+;;; modify it under the terms of the GNU Affero General Public License
+;;; as published by the Free Software Foundation, either version 3 of
+;;; the License, or (at your option) any later version.
+;;;
+;;; This program is distributed in the hope that it will be useful,
+;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+;;; Affero General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU Affero General Public
+;;; License along with this program. If not, see
+;;; <http://www.gnu.org/licenses/>.
+
+(use-modules (srfi srfi-26)
+ (srfi srfi-64)
+ (ice-9 match))
+
+(define (with-variable variable value thunk)
+ "Set VARIABLE to VALUE, run THUNK and restore the old value of
+VARIABLE. Return the value returned by THUNK."
+ (let ((old-value (variable-ref variable)))
+ (dynamic-wind
+ (cut variable-set! variable value)
+ thunk
+ (cut variable-set! variable old-value))))
+
+(define (with-variables variable-bindings thunk)
+ "Set VARIABLE-BINDINGS, run THUNK and restore the old values of the
+variables. Return the value returned by THUNK. VARIABLE-BINDINGS is a
+list of pairs mapping variables to their values."
+ (match variable-bindings
+ (((variable . value) tail ...)
+ (with-variable variable value
+ (cut with-variables tail thunk)))
+ (() (thunk))))
+
+(define-syntax-rule (var@@ module-name variable-name)
+ (module-variable (resolve-module 'module-name)
+ 'variable-name))
+
+(define (trace-calls function-variable thunk)
+ "Run THUNK and return a list of argument lists FUNCTION-VARIABLE is
+called with."
+ (let ((args-list (list)))
+ (with-variable function-variable (lambda args
+ (set! args-list
+ (cons args args-list)))
+ thunk)
+ (reverse args-list)))
+
+(define client-config-stub
+ (cons (var@@ (mumi client) client-config)
+ (lambda (key)
+ (case key
+ ((debbugs-host) "example.com")
+ ((patch-email-address) "foo@patches.com")
+ (else (error "Key unimplemented in stub" key))))))
+
+(test-begin "client")
+
+(test-equal "send patches to new issue"
+ '(("git" "send-email" "--to=foo@patches.com" "foo.patch")
+ ("git" "send-email" "--to=12345@example.com" "bar.patch")
+ ("git" "send-email" "--to=12345@example.com" "foobar.patch"))
+ (map (match-lambda
+ ((command _) command))
+ (trace-calls (var@@ (mumi client) call-with-input-pipe)
+ (lambda ()
+ (with-variables (list (cons (var@@ (mumi client) issue-number-of-message)
+ (const 12345))
+ client-config-stub)
+ (cut (@@ (mumi client) send-email)
+ (list "foo.patch" "bar.patch" "foobar.patch")))))))
+
+(test-equal "send patches to existing issue"
+ '(("git" "send-email" "--to=12345@example.com" "foo.patch")
+ ("git" "send-email" "--to=12345@example.com" "bar.patch")
+ ("git" "send-email" "--to=12345@example.com" "foobar.patch"))
+ (map (match-lambda
+ ((command _) command))
+ (trace-calls (var@@ (mumi client) call-with-input-pipe)
+ (lambda ()
+ (with-variables (list (cons (var@@ (mumi client) current-issue-number)
+ (const 12345))
+ client-config-stub)
+ (cut (@@ (mumi client) send-email)
+ (list "foo.patch" "bar.patch" "foobar.patch")))))))
+
+(test-end "client")
--
2.38.1
A
A
Arun Isaac wrote on 21 Feb 2023 01:33
[PATCH v2 4/4] Set only GUILE_LOAD_PATH when running tests.
(address . 61645@debbugs.gnu.org)
20230221003355.5393-1-arunisaac@systemreboot.net
Somehow, the stubs in tests/client.scm do not work when compiled
modules are found. This could be a guile bug.

* Makefile.am (SCM_LOG_DRIVER): Do not use pre-inst-env. Set only the
load path.
---
Makefile.am | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)

Toggle diff (16 lines)
diff --git a/Makefile.am b/Makefile.am
index 86ba4f0..3e57e63 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -70,8 +70,7 @@ EXTRA_DIST += $(TESTS) \
AM_TESTS_ENVIRONMENT = abs_top_srcdir="$(abs_top_srcdir)" GUILE_AUTO_COMPILE=0
SCM_LOG_DRIVER = \
- $(top_builddir)/pre-inst-env \
- $(GUILE) --no-auto-compile -e main \
+ $(GUILE) --no-auto-compile -L $(top_srcdir) -e main \
$(top_srcdir)/build-aux/test-driver.scm
AM_SCM_LOG_DRIVER_FLAGS = --brief=yes
--
2.38.1
L
L
Ludovic Courtès wrote on 8 Mar 2023 13:05
Re: bug#61645: [PATCH mumi 0/1] Add CLI client to search for issues
(name . Arun Isaac)(address . arunisaac@systemreboot.net)
87bkl3a1ni.fsf@gnu.org
Hi Arun,

Arun Isaac <arunisaac@systemreboot.net> skribis:

Toggle quote (12 lines)
> This patch adds a mumi CLI client that lets one search for issues from
> the command line by calling mumi's GraphQL API. This patch requires a
> .mumi/config file in the guix repo with the following contents.
>
> ((debbugs-host . "debbugs.gnu.org")
> (patch-email-address . "guix-patches@gnu.org")
> (mumi-host . "issues.guix.gnu.org"))
>
> After adding .mumi/config, you can run search queries like
>
> $ /path/to/mumi/pre-inst-env mumi search zig is:open

Yay, this is great!!

Toggle quote (5 lines)
> This is only the beginning. I have WIP patches that will add a "mumi
> send-email" subcommand that will finally free us from our debbugs
> dance when sending multiple patches. I will send them separately once
> I have tested it more.

Woow, sounds really nice. I’m all for it!

Thanks,
Ludo’.
A
A
Arun Isaac wrote on 8 Mar 2023 14:28
(name . Ludovic Courtès)(address . ludo@gnu.org)
87r0tzgyok.fsf@systemreboot.net
Toggle quote (5 lines)
>> This is only the beginning. I have WIP patches that will add a "mumi
>> send-email" subcommand that will finally free us from our debbugs
>> dance when sending multiple patches. I will send them separately once
>> I have tested it more.

I've already sent these patches as a v2 to this same issue. Waiting for
Ricardo to merge it now.

Toggle quote (2 lines)
> Woow, sounds really nice. I’m all for it!

Yep, it's going to be awesome! :-)
A
A
Arun Isaac wrote on 8 Mar 2023 16:36
[mumi v3 0/4] Add mumi CLI client
20230308153658.19929-1-arunisaac@systemreboot.net
Here is a v3 of the patchset.

This patchset reduces the number of `git send-email' invocations to a
maximum of 2 even when there are n patches. The first patch is sent
using a `git send-email' invocation and the remaining n-1 patches are
sent using another `git send-email' invocation.

With this patchset, when sending a single patch, the mumi server is
not polled for the issue number since that's not necessary.

I also fixed a bug to do with printing the number of retries left.

Arun Isaac (4):
client: Add CLI client to search for issues.
client: Support checking in to a specific issue.
client: Support sending email to issues.
Set only GUILE_LOAD_PATH when running tests.

Makefile.am | 5 +-
mumi/client.scm | 264 +++++++++++++++++++++++++++++++++++++++++++++++
scripts/mumi.in | 33 +++++-
tests/client.scm | 118 +++++++++++++++++++++
4 files changed, 417 insertions(+), 3 deletions(-)
create mode 100644 mumi/client.scm
create mode 100644 tests/client.scm

--
2.39.1
A
A
Arun Isaac wrote on 8 Mar 2023 16:36
[mumi v3 2/4] client: Support checking in to a specific issue.
20230308153658.19929-3-arunisaac@systemreboot.net
* mumi/client.scm: Import (srfi srfi-26).
(current-issue-file, current-issue-number): New functions.
(print-current-issue, set-current-issue!, clear-current-issue!): New
public functions.
* scripts/mumi.in (show-mumi-usage): Document current and new
subcommands.
(main): Add current and new subcommands.
---
mumi/client.scm | 48 +++++++++++++++++++++++++++++++++++++++++++++++-
scripts/mumi.in | 20 ++++++++++++++++++++
2 files changed, 67 insertions(+), 1 deletion(-)

Toggle diff (109 lines)
diff --git a/mumi/client.scm b/mumi/client.scm
index e4a0123..ae3a0a9 100644
--- a/mumi/client.scm
+++ b/mumi/client.scm
@@ -18,13 +18,17 @@
(define-module (mumi client)
#:use-module (srfi srfi-19)
+ #:use-module (srfi srfi-26)
#:use-module (srfi srfi-43)
#:use-module (term ansi-color)
#:use-module (web uri)
#:use-module (kolam http)
#:use-module (mumi config)
#:use-module (mumi web view utils)
- #:export (search))
+ #:export (search
+ print-current-issue
+ set-current-issue!
+ clear-current-issue!))
(define (git-top-level)
"Return the top-level directory of the current git repository."
@@ -106,3 +110,45 @@
date
(submitter name address)))))
"issues")))
+
+(define (current-issue-file)
+ "Return path to current issue number file."
+ (string-append (client-config-directory) "/current-issue"))
+
+(define (current-issue-number)
+ "Return current issue number."
+ (let ((issue-file (current-issue-file)))
+ (and (file-exists? issue-file)
+ (call-with-input-file issue-file
+ read))))
+
+(define (print-current-issue)
+ "Print current issue."
+ (let ((issue-number (current-issue-number)))
+ (if issue-number
+ (list-issue
+ (assoc-ref
+ (graphql-http-get (graphql-endpoint)
+ `(document
+ (query (#(issue #:number ,issue-number)
+ number
+ title
+ open
+ date
+ (submitter name address)))))
+ "issue"))
+ (begin
+ (format (current-error-port) "No current issue!~%")
+ (exit #f)))))
+
+(define (set-current-issue! issue-number)
+ "Set current issue number."
+ ;; TODO: Write file atomically.
+ (call-with-output-file (current-issue-file)
+ (cut write issue-number <>)))
+
+(define (clear-current-issue!)
+ "Clear current issue."
+ (let ((issue-file (current-issue-file)))
+ (when (file-exists? issue-file)
+ (delete-file issue-file))))
diff --git a/scripts/mumi.in b/scripts/mumi.in
index 9b61729..dfd082d 100644
--- a/scripts/mumi.in
+++ b/scripts/mumi.in
@@ -120,6 +120,12 @@
`mumi search QUERY':
search mumi for issues.
+ `mumi current [ISSUE-NUMBER]':
+ print or set current issue.
+
+ `mumi new':
+ clear current issue presumably to open a new one.
+
`mumi web [--address=address] [--port=port] [--listen-repl[=port]] [--disable-mailer]':
start the application web server.
@@ -138,6 +144,20 @@
(match (cdr (program-arguments))
(("search" . query-strings)
(client:search (string-join query-strings)))
+ (("current")
+ (client:print-current-issue))
+ (("current" issue-number-string)
+ (let ((issue-number (string->number issue-number-string)))
+ (if issue-number
+ (client:set-current-issue! issue-number)
+ (begin
+ (format (current-error-port)
+ "Invalid issue number `~a'~%"
+ issue-number-string)
+ (exit #f))))
+ (client:print-current-issue))
+ (("new")
+ (client:clear-current-issue!))
(("mailer" . rest)
(let* ((opts (parse-options rest))
(sender (assoc-ref opts 'sender))
--
2.39.1
A
A
Arun Isaac wrote on 8 Mar 2023 16:36
[mumi v3 1/4] client: Add CLI client to search for issues.
20230308153658.19929-2-arunisaac@systemreboot.net
* mumi/client.scm: New file.
* scripts/mumi.in: Import (mumi client).
(show-mumi-usage): Document search subcommand.
(main): Add search subcommand.
* Makefile.am (SOURCES): Add mumi/client.scm.
---
Makefile.am | 1 +
mumi/client.scm | 108 ++++++++++++++++++++++++++++++++++++++++++++++++
scripts/mumi.in | 8 +++-
3 files changed, 116 insertions(+), 1 deletion(-)
create mode 100644 mumi/client.scm

Toggle diff (168 lines)
diff --git a/Makefile.am b/Makefile.am
index 8182fc3..a8c11a1 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -49,6 +49,7 @@ SOURCES = \
mumi/messages.scm \
mumi/jobs.scm \
mumi/send-email.scm \
+ mumi/client.scm \
mumi/config.scm \
mumi/debbugs.scm \
mumi/test-utils.scm \
diff --git a/mumi/client.scm b/mumi/client.scm
new file mode 100644
index 0000000..e4a0123
--- /dev/null
+++ b/mumi/client.scm
@@ -0,0 +1,108 @@
+;;; mumi -- Mediocre, uh, mail interface
+;;; Copyright © 2023 Arun Isaac <arunisaac@systemreboot.net>
+;;;
+;;; This file is part of mumi.
+;;;
+;;; mumi is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or
+;;; (at your option) any later version.
+;;;
+;;; mumi is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+;;; General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with mumi. If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (mumi client)
+ #:use-module (srfi srfi-19)
+ #:use-module (srfi srfi-43)
+ #:use-module (term ansi-color)
+ #:use-module (web uri)
+ #:use-module (kolam http)
+ #:use-module (mumi config)
+ #:use-module (mumi web view utils)
+ #:export (search))
+
+(define (git-top-level)
+ "Return the top-level directory of the current git repository."
+ (let loop ((curdir (getcwd)))
+ (cond
+ ((file-exists? (string-append curdir "/.git"))
+ curdir)
+ ((string=? curdir "/")
+ (error "No git top level found"))
+ (else
+ (loop (dirname curdir))))))
+
+(define (client-config-directory)
+ "Return client configuration directory."
+ (string-append (git-top-level) "/.mumi"))
+
+(define (client-config key)
+ "Return client configuration value corresponding to KEY."
+ (or (assq-ref (call-with-input-file (string-append (client-config-directory)
+ "/config")
+ read)
+ key)
+ (case key
+ ((mumi-scheme) 'https)
+ (else (format (current-error-port)
+ "Key '~a not configured for mumi client.~%"
+ key)))))
+
+(define (graphql-endpoint)
+ "Return GraphQL endpoint."
+ (uri->string
+ (build-uri (client-config 'mumi-scheme)
+ #:host (client-config 'mumi-host)
+ #:path "/graphql")))
+
+(define (iso8601->date str)
+ "Convert ISO-8601 date/time+zone string to date object."
+ (string->date str "~Y-~m-~dT~H:~M:~S~z"))
+
+(define (list-issue issue)
+ "List issue described by ISSUE association list."
+ (display (colorize-string
+ (string-append "#"
+ (number->string (assoc-ref issue "number")))
+ 'YELLOW))
+ (display " ")
+ (unless (assoc-ref issue "open")
+ (display (colorize-string "?" 'BOLD 'GREEN))
+ (display " "))
+ (display (colorize-string
+ (assoc-ref issue "title")
+ 'MAGENTA 'UNDERLINE))
+ (newline)
+ (display (string-append
+ "opened "
+ (colorize-string (time->string
+ (iso8601->date (assoc-ref issue "date")))
+ 'CYAN)
+ " by "
+ (colorize-string
+ (let ((submitter (assoc-ref issue "submitter")))
+ (if (eq? (assoc-ref submitter "name") 'null)
+ (assoc-ref submitter "address")
+ (assoc-ref submitter "name")))
+ 'CYAN)))
+ (newline))
+
+(define (search query)
+ "Search for issues with QUERY and list results."
+ (vector-for-each (lambda (_ issue)
+ (list-issue issue))
+ (assoc-ref
+ (graphql-http-get (graphql-endpoint)
+ `(document
+ (query (#(issues #:search ,query)
+ number
+ title
+ open
+ date
+ (submitter name address)))))
+ "issues")))
diff --git a/scripts/mumi.in b/scripts/mumi.in
index 755dfb3..9b61729 100644
--- a/scripts/mumi.in
+++ b/scripts/mumi.in
@@ -4,7 +4,7 @@
!#
;;; mumi -- Mediocre, uh, mail interface
;;; Copyright © 2016, 2017, 2019, 2020 Ricardo Wurmus <rekado@elephly.net>
-;;; Copyright © 2018, 2021 Arun Isaac <arunisaac@systemreboot.net>
+;;; Copyright © 2018, 2021, 2023 Arun Isaac <arunisaac@systemreboot.net>
;;;
;;; This file is part of mumi.
;;;
@@ -26,6 +26,7 @@
(system repl server)
(ice-9 match)
(ice-9 format)
+ ((mumi client) #:prefix client:)
(mumi config)
((mumi debbugs)
#:select (extract-bug-numbers))
@@ -116,6 +117,9 @@
(define (show-mumi-usage)
(format (current-error-port)
"
+ `mumi search QUERY':
+ search mumi for issues.
+
`mumi web [--address=address] [--port=port] [--listen-repl[=port]] [--disable-mailer]':
start the application web server.
@@ -132,6 +136,8 @@
(exit 1))
(match (cdr (program-arguments))
+ (("search" . query-strings)
+ (client:search (string-join query-strings)))
(("mailer" . rest)
(let* ((opts (parse-options rest))
(sender (assoc-ref opts 'sender))
--
2.39.1
A
A
Arun Isaac wrote on 8 Mar 2023 16:36
[mumi v3 3/4] client: Support sending email to issues.
20230308153658.19929-4-arunisaac@systemreboot.net
* mumi/client.scm: Import (rnrs io ports), (srfi srfi-71), (srfi
srfi-171), (ice-9 match), (ice-9 popen), (web client), (web response)
and (email email).
(issue-number-of-message, call-with-input-pipe, git-send-email): New
functions.
(send-email): New public function.
* scripts/mumi.in (show-mumi-usage): Document send-email subcommand.
(main): Add send-email subcommand.
* tests/client.scm: New file.
* Makefile.am (SCM_TESTS): Add tests/client.scm.
---
Makefile.am | 1 +
mumi/client.scm | 112 +++++++++++++++++++++++++++++++++++++++++++-
scripts/mumi.in | 5 ++
tests/client.scm | 118 +++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 235 insertions(+), 1 deletion(-)
create mode 100644 tests/client.scm

Toggle diff (299 lines)
diff --git a/Makefile.am b/Makefile.am
index a8c11a1..86ba4f0 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -58,6 +58,7 @@ SOURCES = \
TEST_EXTENSIONS = .scm
SCM_TESTS = \
+ tests/client.scm \
tests/debbugs.scm \
tests/xapian.scm
diff --git a/mumi/client.scm b/mumi/client.scm
index ae3a0a9..b8d588b 100644
--- a/mumi/client.scm
+++ b/mumi/client.scm
@@ -17,18 +17,27 @@
;;; along with mumi. If not, see <http://www.gnu.org/licenses/>.
(define-module (mumi client)
+ #:use-module (rnrs io ports)
#:use-module (srfi srfi-19)
#:use-module (srfi srfi-26)
#:use-module (srfi srfi-43)
+ #:use-module (srfi srfi-71)
+ #:use-module (srfi srfi-171)
+ #:use-module (ice-9 match)
+ #:use-module (ice-9 popen)
#:use-module (term ansi-color)
+ #:use-module (web client)
+ #:use-module (web response)
#:use-module (web uri)
+ #:use-module (email email)
#:use-module (kolam http)
#:use-module (mumi config)
#:use-module (mumi web view utils)
#:export (search
print-current-issue
set-current-issue!
- clear-current-issue!))
+ clear-current-issue!
+ send-email))
(define (git-top-level)
"Return the top-level directory of the current git repository."
@@ -152,3 +161,104 @@
(let ((issue-file (current-issue-file)))
(when (file-exists? issue-file)
(delete-file issue-file))))
+
+(define* (issue-number-of-message message-id #:optional (retries 15))
+ "Return issue number that MESSAGE-ID belongs to. Retry RETRIES number
+of times with an interval of 60 seconds between retries."
+ ;; TODO: Re-implement this using our GraphQL endpoint once it
+ ;; supports retrieving the issue from a message ID. Later,
+ ;; re-implement this using a GraphQL subscription when kolam
+ ;; supports it.
+ (define (poll-issue-number-of-message message-id)
+ (let ((response _ (http-get (build-uri (client-config 'mumi-scheme)
+ #:host (client-config 'mumi-host)
+ #:path (string-append "/msgid/" message-id)))))
+ (and (>= (response-code response) 300)
+ (< (response-code response) 400)
+ (match (split-and-decode-uri-path
+ (uri-path (response-location response)))
+ (("issue" issue-number)
+ (string->number issue-number))))))
+
+ (let loop ((i retries))
+ (if (zero? i)
+ (begin
+ (format (current-error-port)
+ "Mail not acknowledged by issue tracker. Giving up.~%")
+ (exit #f))
+ (or (poll-issue-number-of-message message-id)
+ (begin
+ (let ((retry-interval 60))
+ (format (current-error-port)
+ "Server has not yet received our email. Will retry in ~a seconds. ~a retries remaining.~%"
+ retry-interval (1- i))
+ (sleep retry-interval))
+ (loop (1- i)))))))
+
+(define (call-with-input-pipe command proc)
+ "Call PROC with input pipe to COMMAND. COMMAND is a list of program
+arguments."
+ (match command
+ ((prog args ...)
+ (let ((port #f))
+ (dynamic-wind
+ (lambda ()
+ (set! port (apply open-pipe* OPEN_READ prog args)))
+ (cut proc port)
+ (cut close-pipe port))))))
+
+(define (git-send-email to patches)
+ "Send PATCHES using git send-email to the TO address and return the
+message ID of the first email sent."
+ (let ((command (cons* "git" "send-email"
+ (string-append "--to=" to)
+ patches)))
+ (display (string-join command))
+ (newline)
+ (call-with-input-pipe command
+ (lambda (port)
+ ;; FIXME: This messes up the order of stdout and stderr.
+ (let ((message-id
+ ;; Read till you get the Message ID.
+ (port-transduce (tlog (lambda (_ line)
+ (display line)
+ (newline)))
+ (rany (lambda (line)
+ (and (string-prefix-ci? "Message-ID:" line)
+ (assq-ref
+ (parse-email-headers
+ (string-append line "\n"))
+ 'message-id))))
+ get-line
+ port)))
+ ;; Pass through the rest.
+ (display (get-string-all port))
+ message-id)))))
+
+(define (send-email patches)
+ "Send PATCHES via email."
+ (if (current-issue-number)
+ ;; If an issue is current, send patches to that issue's email
+ ;; address.
+ (git-send-email (string-append (number->string (current-issue-number))
+ "@"
+ (client-config 'debbugs-host))
+ patches)
+ (match patches
+ ;; If it's a single patch, send it to the patch email address
+ ;; and be done with it
+ ((patch)
+ (git-send-email (client-config 'patch-email-address)
+ (list patch)))
+ ;; Else, send first patch to the patch email address and get an
+ ;; issue number. Then, send the remaining patches to that
+ ;; issue's email address.
+ ((first-patch other-patches ...)
+ (git-send-email
+ (string-append (number->string
+ (issue-number-of-message
+ (git-send-email (client-config 'patch-email-address)
+ (list first-patch))))
+ "@"
+ (client-config 'debbugs-host))
+ other-patches)))))
diff --git a/scripts/mumi.in b/scripts/mumi.in
index dfd082d..2295328 100644
--- a/scripts/mumi.in
+++ b/scripts/mumi.in
@@ -126,6 +126,9 @@
`mumi new':
clear current issue presumably to open a new one.
+ `mumi send-email':
+ send patches to debbugs.
+
`mumi web [--address=address] [--port=port] [--listen-repl[=port]] [--disable-mailer]':
start the application web server.
@@ -158,6 +161,8 @@
(client:print-current-issue))
(("new")
(client:clear-current-issue!))
+ (("send-email" . patches)
+ (client:send-email patches))
(("mailer" . rest)
(let* ((opts (parse-options rest))
(sender (assoc-ref opts 'sender))
diff --git a/tests/client.scm b/tests/client.scm
new file mode 100644
index 0000000..fb03713
--- /dev/null
+++ b/tests/client.scm
@@ -0,0 +1,118 @@
+;;; mumi -- Mediocre, uh, mail interface
+;;; Copyright © 2023 Arun Isaac <arunisaac@systemreboot.net>
+;;;
+;;; This program is free software: you can redistribute it and/or
+;;; modify it under the terms of the GNU Affero General Public License
+;;; as published by the Free Software Foundation, either version 3 of
+;;; the License, or (at your option) any later version.
+;;;
+;;; This program is distributed in the hope that it will be useful,
+;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+;;; Affero General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU Affero General Public
+;;; License along with this program. If not, see
+;;; <http://www.gnu.org/licenses/>.
+
+(use-modules (srfi srfi-26)
+ (srfi srfi-64)
+ (ice-9 match))
+
+(define (with-variable variable value thunk)
+ "Set VARIABLE to VALUE, run THUNK and restore the old value of
+VARIABLE. Return the value returned by THUNK."
+ (let ((old-value (variable-ref variable)))
+ (dynamic-wind
+ (cut variable-set! variable value)
+ thunk
+ (cut variable-set! variable old-value))))
+
+(define (with-variables variable-bindings thunk)
+ "Set VARIABLE-BINDINGS, run THUNK and restore the old values of the
+variables. Return the value returned by THUNK. VARIABLE-BINDINGS is a
+list of pairs mapping variables to their values."
+ (match variable-bindings
+ (((variable . value) tail ...)
+ (with-variable variable value
+ (cut with-variables tail thunk)))
+ (() (thunk))))
+
+(define-syntax-rule (var@@ module-name variable-name)
+ (module-variable (resolve-module 'module-name)
+ 'variable-name))
+
+(define (trace-calls function-variable thunk)
+ "Run THUNK and return a list of argument lists FUNCTION-VARIABLE is
+called with."
+ (let ((args-list (list)))
+ (with-variable function-variable (lambda args
+ (set! args-list
+ (cons args args-list)))
+ thunk)
+ (reverse args-list)))
+
+(define client-config-stub
+ (cons (var@@ (mumi client) client-config)
+ (lambda (key)
+ (case key
+ ((debbugs-host) "example.com")
+ ((patch-email-address) "foo@patches.com")
+ (else (error "Key unimplemented in stub" key))))))
+
+(test-begin "client")
+
+(test-equal "send patches to new issue"
+ '(("git" "send-email" "--to=foo@patches.com" "foo.patch")
+ ("git" "send-email" "--to=12345@example.com" "bar.patch" "foobar.patch"))
+ (map (match-lambda
+ ((command _) command))
+ (trace-calls (var@@ (mumi client) call-with-input-pipe)
+ (lambda ()
+ (with-variables (list (cons (var@@ (mumi client) issue-number-of-message)
+ (const 12345))
+ client-config-stub)
+ (cut (@@ (mumi client) send-email)
+ (list "foo.patch" "bar.patch" "foobar.patch")))))))
+
+(test-equal "send patches to existing issue"
+ '(("git" "send-email" "--to=12345@example.com" "foo.patch" "bar.patch" "foobar.patch"))
+ (map (match-lambda
+ ((command _) command))
+ (trace-calls (var@@ (mumi client) call-with-input-pipe)
+ (lambda ()
+ (with-variables (list (cons (var@@ (mumi client) current-issue-number)
+ (const 12345))
+ client-config-stub)
+ (cut (@@ (mumi client) send-email)
+ (list "foo.patch" "bar.patch" "foobar.patch")))))))
+
+(test-equal "send single patch to new issue"
+ '(("git" "send-email" "--to=foo@patches.com" "foo.patch"))
+ (map (match-lambda
+ ((command _) command))
+ (trace-calls (var@@ (mumi client) call-with-input-pipe)
+ (lambda ()
+ (with-variables (list (cons (var@@ (mumi client) issue-number-of-message)
+ (lambda _
+ (error "Do not poll server for issue number")))
+ client-config-stub)
+ (cut (@@ (mumi client) send-email)
+ (list "foo.patch")))))))
+
+(test-equal "send single patch to existing issue"
+ '(("git" "send-email" "--to=12345@example.com" "foo.patch"))
+ (map (match-lambda
+ ((command _) command))
+ (trace-calls (var@@ (mumi client) call-with-input-pipe)
+ (lambda ()
+ (with-variables (list (cons (var@@ (mumi client) current-issue-number)
+ (const 12345))
+ (cons (var@@ (mumi client) issue-number-of-message)
+ (lambda _
+ (error "Do not poll server for issue number")))
+ client-config-stub)
+ (cut (@@ (mumi client) send-email)
+ (list "foo.patch")))))))
+
+(test-end "client")
--
2.39.1
A
A
Arun Isaac wrote on 8 Mar 2023 16:36
[mumi v3 4/4] Set only GUILE_LOAD_PATH when running tests.
20230308153658.19929-5-arunisaac@systemreboot.net
Somehow, the stubs in tests/client.scm do not work when compiled
modules are found. This could be a guile bug.

* Makefile.am (SCM_LOG_DRIVER): Do not use pre-inst-env. Set only the
load path.
---
Makefile.am | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)

Toggle diff (16 lines)
diff --git a/Makefile.am b/Makefile.am
index 86ba4f0..3e57e63 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -70,8 +70,7 @@ EXTRA_DIST += $(TESTS) \
AM_TESTS_ENVIRONMENT = abs_top_srcdir="$(abs_top_srcdir)" GUILE_AUTO_COMPILE=0
SCM_LOG_DRIVER = \
- $(top_builddir)/pre-inst-env \
- $(GUILE) --no-auto-compile -e main \
+ $(GUILE) --no-auto-compile -L $(top_srcdir) -e main \
$(top_srcdir)/build-aux/test-driver.scm
AM_SCM_LOG_DRIVER_FLAGS = --brief=yes
--
2.39.1
L
L
Ludovic Courtès wrote on 30 Mar 2023 22:47
Re: bug#61645: [PATCH mumi 0/1] Add CLI client to search for issues
(name . Arun Isaac)(address . arunisaac@systemreboot.net)
87pm8qhspl.fsf@gnu.org
Hello,

Arun Isaac <arunisaac@systemreboot.net> skribis:

Toggle quote (8 lines)
>>> This is only the beginning. I have WIP patches that will add a "mumi
>>> send-email" subcommand that will finally free us from our debbugs
>>> dance when sending multiple patches. I will send them separately once
>>> I have tested it more.
>
> I've already sent these patches as a v2 to this same issue. Waiting for
> Ricardo to merge it now.

I suspect Ricardo is busy with other things. :-)

Should we set up a repo on Savannah (possibly within the ‘guix’ project)
or on notabug.org, sr.ht, codeberg.org or whatever to make it easier to
distribute permissions?

Ludo’.
R
R
Ricardo Wurmus wrote on 30 Mar 2023 22:57
(name . Ludovic Courtès)(address . ludo@gnu.org)
87bkkac4wf.fsf@elephly.net
Ludovic Courtès <ludo@gnu.org> writes:

Toggle quote (12 lines)
> Arun Isaac <arunisaac@systemreboot.net> skribis:
>
>>>> This is only the beginning. I have WIP patches that will add a "mumi
>>>> send-email" subcommand that will finally free us from our debbugs
>>>> dance when sending multiple patches. I will send them separately once
>>>> I have tested it more.
>>
>> I've already sent these patches as a v2 to this same issue. Waiting for
>> Ricardo to merge it now.
>
> I suspect Ricardo is busy with other things. :-)

Oof, indeed! Very sorry about that.

Arun, thank you for the patches!

I have applied the patches locally, but ever since I moved
git.elephly.net to gitile on the rockpro64 I haven’t been able to push
to my repos. I hadn’t been able to make any time to diagnose this
problem as yet. (Probably related to nginx rewrite rules.)

Toggle quote (4 lines)
> Should we set up a repo on Savannah (possibly within the ‘guix’ project)
> or on notabug.org, sr.ht, codeberg.org or whatever to make it easier to
> distribute permissions?

Whichever you decide on: it has my blessing.

--
Ricardo
R
R
Ricardo Wurmus wrote on 30 Mar 2023 23:57
(name . Ludovic Courtès)(address . ludo@gnu.org)
877cuxdhql.fsf@elephly.net
Ricardo Wurmus <rekado@elephly.net> writes:

Toggle quote (5 lines)
> I have applied the patches locally, but ever since I moved
> git.elephly.net to gitile on the rockpro64 I haven’t been able to push
> to my repos. I hadn’t been able to make any time to diagnose this
> problem as yet. (Probably related to nginx rewrite rules.)

Fixed and pushed.

--
Ricardo
Closed
L
L
Ludovic Courtès wrote on 31 Mar 2023 14:15
(name . Ricardo Wurmus)(address . rekado@elephly.net)
87h6u15d83.fsf@gnu.org
Hi!

Ricardo Wurmus <rekado@elephly.net> skribis:

Toggle quote (2 lines)
> Ludovic Courtès <ludo@gnu.org> writes:

[...]

Toggle quote (6 lines)
>> Should we set up a repo on Savannah (possibly within the ‘guix’ project)
>> or on notabug.org, sr.ht, codeberg.org or whatever to make it easier to
>> distribute permissions?
>
> Whichever you decide on: it has my blessing.

Awesome (I hope that suggestion didn’t come out as harsh!).

Arun, would you like to handle this? :-) To have it under the Guix
umbrella on Savannah, you need to file a “support request” on Savannah
asking for a new Git repo. If you choose something else, well, it’s
different (I’d personally recommend against git{hub,lab}.com).

Ludo’.
A
A
Arun Isaac wrote on 31 Mar 2023 22:32
(address . 61645@debbugs.gnu.org)
877cuwbr19.fsf@systemreboot.net
Hi Ludo and Ricardo,

Toggle quote (4 lines)
> Arun, would you like to handle this? :-) To have it under the Guix
> umbrella on Savannah, you need to file a “support request” on Savannah
> asking for a new Git repo.

Toggle quote (3 lines)
> If you choose something else, well, it’s different (I’d personally
> recommend against git{hub,lab}.com).

Definitely! I am no big fan of git{hub,lab} either.

From time to time, I need to update the running instance of mumi on the
server and refresh the xapian index. So, it might also help if I could
have access to the server running mumi. Would it be appropriate for me
to have access?

Thanks!
Arun
L
L
Ludovic Courtès wrote on 1 Apr 2023 00:15
(name . Arun Isaac)(address . arunisaac@systemreboot.net)
87tty0h8jn.fsf@gnu.org
Hello!

Arun Isaac <arunisaac@systemreboot.net> skribis:

Toggle quote (6 lines)
>> Arun, would you like to handle this? :-) To have it under the Guix
>> umbrella on Savannah, you need to file a “support request” on Savannah
>> asking for a new Git repo.
>
> Done! See https://savannah.nongnu.org/support/?110861

Yay!

Toggle quote (10 lines)
>> If you choose something else, well, it’s different (I’d personally
>> recommend against git{hub,lab}.com).
>
> Definitely! I am no big fan of git{hub,lab} either.
>
> From time to time, I need to update the running instance of mumi on the
> server and refresh the xapian index. So, it might also help if I could
> have access to the server running mumi. Would it be appropriate for me
> to have access?

I think so! Could you email guix-sysadmin@gnu.org so everyone involved
has a chance to see your request?

Thank you,
Ludo’.
A
A
Arun Isaac wrote on 1 Apr 2023 00:51
(name . Ludovic Courtès)(address . ludo@gnu.org)
871ql4bklh.fsf@systemreboot.net
Toggle quote (3 lines)
> I think so! Could you email guix-sysadmin@gnu.org so everyone involved
> has a chance to see your request?

Done, thanks!
A
A
Arun Isaac wrote on 1 Apr 2023 19:32
(name . Ludovic Courtès)(address . ludo@gnu.org)
87y1nba4oq.fsf@systemreboot.net
Hi Ludo,

Toggle quote (6 lines)
>>> Arun, would you like to handle this? :-) To have it under the Guix
>>> umbrella on Savannah, you need to file a “support request” on Savannah
>>> asking for a new Git repo.
>>
>> Done! See https://savannah.nongnu.org/support/?110861

They want an admin of the Guix project to confirm my request. Could you
please go to the support request page and do so?

Thanks!
Arun
A
A
Arun Isaac wrote on 24 Apr 2023 16:41
(address . 61645@debbugs.gnu.org)
87r0s9ibqa.fsf@systemreboot.net
Hi,

I have pushed a copy of the mumi repo to Savannah and updated our mumi
package to the latest commit of mumi. I will send out a separate email
to guix-devel explaining how to use the new mumi client.

At some point in the near future, we should consider making a mumi 0.1.0
release.

Regards,
Arun
L
L
Ludovic Courtès wrote on 24 Apr 2023 22:01
(name . Arun Isaac)(address . arunisaac@systemreboot.net)
87sfcp2go7.fsf@gnu.org
Hi,

Arun Isaac <arunisaac@systemreboot.net> skribis:

Toggle quote (4 lines)
> I have pushed a copy of the mumi repo to Savannah and updated our mumi
> package to the latest commit of mumi. I will send out a separate email
> to guix-devel explaining how to use the new mumi client.

Yay!

Toggle quote (3 lines)
> At some point in the near future, we should consider making a mumi 0.1.0
> release.

Maybe consider 1.0, even? After all, it does the job, right?


Ludo’.
A
A
Arun Isaac wrote on 25 Apr 2023 14:28
(name . Ludovic Courtès)(address . ludo@gnu.org)
871qk8i1sf.fsf@systemreboot.net
Toggle quote (5 lines)
>> At some point in the near future, we should consider making a mumi 0.1.0
>> release.
>
> Maybe consider 1.0, even? After all, it does the job, right?

Sure, 1.0.0 is good too!
F
F
Felix Lechner wrote on 23 Feb 14:23 +0100
(no subject)
(address . control@debbugs.gnu.org)
875xyf1fhb.fsf@lease-up.com
unarchive 68680
reassign 68680 mumi
archive 68680

unarchive 63802
reassign 63802 mumi
archive 63802

unarchive 63215
reassign 63215 mumi
archive 63215

unarchive 61645
reassign 61645 mumi
archive 61645

unarchive 60410
reassign 60410 mumi
archive 60410

unarchive 60292
reassign 60292 mumi
archive 60292

unarchive 60292
reassign 60292 mumi
archive 60292

unarchive 58573
reassign 58573 mumi
archive 58573

unarchive 54024
reassign 54024 mumi
archive 54024

unarchive 49115
reassign 49115 mumi
archive 49115

unarchive 48160
reassign 48160 mumi
archive 48160

unarchive 47739
reassign 47739 mumi
archive 47739

unarchive 47520
reassign 47520 mumi
archive 47520

unarchive 47121
reassign 47121 mumi
archive 47121

unarchive 45015
reassign 45015 mumi
archive 45015

unarchive 43661
reassign 43661 mumi
archive 43661

unarchiv 43166
reassign 43166 mumi
archive 43166

unarchive 41906
reassign 41906 mumi
archive 41906

unarchive 41098
reassign 41098 mumi
archive 41098

unarchive 39924
reassign 39924 mumi
archive 39924

unarchive 39924
reassign 39924 mumi
archive 39924

unarchive 39924
reassign 39924 mumi
archive 39924

thanks
?