all messages for Guix-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
* [bug#37413] [PATCH 0/9] Channel news distribution mechanism
@ 2019-09-15 22:10 Ludovic Courtès
  2019-09-15 22:20 ` [bug#37413] [PATCH 1/9] pull: '--news' shows the list of channels added or removed Ludovic Courtès
                   ` (2 more replies)
  0 siblings, 3 replies; 45+ messages in thread
From: Ludovic Courtès @ 2019-09-15 22:10 UTC (permalink / raw)
  To: 37413

Hello Guix!

This patch series adds a mechanism for channel authors to distribute
“news entries” alongside their channels.  The goal is to provide a
way for channel authors (including authors of the 'guix channel!)
to inform their users about important changes.

For example, you’d tell people about package upgrades that require
manual intervention, about important new features, bug fixes, etc.

This is done by adding a “news file” to the channel.  Each entry in
that file is associated with a commit, meaning that the entry
describes changes related to that commit.  This information is what
allows ‘guix pull --news’ to determine whether a news entry is
relevant or not for the user: only entries corresponding to commits
that were just pulled are displayed.

It’s a bit weird to refer to commits from within a file in the repo
because it almost forces you to push so that you know the commit ID
that your news entry should refer to.  News entries become obsolete
if you rebase, too.  I think it’s acceptable, though.

Regarding i18n, the title and body of an entry can be provided in
several languages.  I wonder what workflow we should adopt;
obviously the TP wouldn’t be appropriate, and there’s no gettext
involved.

Anyway, this is it.  Feedback welcome!

Ludo’.

Ludovic Courtès (9):
  pull: '--news' shows the list of channels added or removed.
  git: 'update-cached-checkout' avoids network access when unnecessary.
  git: Add 'commit-difference'.
  channels: Add support for a news file.
  ui: Add 'current-message-language'.
  pull: Display channel news.
  pull: '-l' displays channel news.
  Add '.guix-channel' file.
  DRAFT etc: Add channel news file.

 .dir-locals.el        |   1 +
 .guix-channel         |   5 ++
 Makefile.am           |   8 ++-
 doc/guix.texi         |  62 +++++++++++++++++++--
 etc/news.scm          |  13 +++++
 guix/channels.scm     | 121 ++++++++++++++++++++++++++++++++++++++---
 guix/git.scm          |  58 +++++++++++++++++++-
 guix/scripts/pull.scm | 122 +++++++++++++++++++++++++++++++++++++++---
 guix/tests/git.scm    | 102 +++++++++++++++++++++++++++++++++++
 guix/ui.scm           |  18 +++++++
 tests/channels.scm    |  96 +++++++++++++++++++++++++++++++++
 tests/git.scm         |  99 ++++++++++++++++++++++++++++++++++
 12 files changed, 685 insertions(+), 20 deletions(-)
 create mode 100644 .guix-channel
 create mode 100644 etc/news.scm
 create mode 100644 guix/tests/git.scm
 create mode 100644 tests/git.scm

-- 
2.23.0

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [bug#37413] [PATCH 1/9] pull: '--news' shows the list of channels added or removed.
  2019-09-15 22:10 [bug#37413] [PATCH 0/9] Channel news distribution mechanism Ludovic Courtès
@ 2019-09-15 22:20 ` Ludovic Courtès
  2019-09-15 22:20   ` [bug#37413] [PATCH 2/9] git: 'update-cached-checkout' avoids network access when unnecessary Ludovic Courtès
                     ` (7 more replies)
  2019-09-16  9:31 ` [bug#37413] [PATCH 0/9] Channel news distribution mechanism Ricardo Wurmus
  2019-09-16 21:25 ` Ludovic Courtès
  2 siblings, 8 replies; 45+ messages in thread
From: Ludovic Courtès @ 2019-09-15 22:20 UTC (permalink / raw)
  To: 37413

* guix/scripts/pull.scm (display-channel, channel=?)
(display-channel-news, display-news): New procedures.
(process-query): Call 'display-news' instead of 'display-profile-news'.
---
 guix/scripts/pull.scm | 61 ++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 57 insertions(+), 4 deletions(-)

diff --git a/guix/scripts/pull.scm b/guix/scripts/pull.scm
index 54bbaddf30..ca3b36f98c 100644
--- a/guix/scripts/pull.scm
+++ b/guix/scripts/pull.scm
@@ -192,6 +192,62 @@ newest generation of PROFILE."
                                         (G_ "New in this revision:\n")))))
     (_ #t)))
 
+(define (display-channel channel)
+  "Display information about CHANNEL."
+  (format (current-error-port)
+          ;; TRANSLATORS: This describes a "channel"; the first placeholder is
+          ;; the channel name (e.g., "guix") and the second placeholder is its
+          ;; URL.
+          (G_ "    ~a at ~a~%")
+          (channel-name channel)
+          (channel-url channel)))
+
+(define (channel=? channel1 channel2)
+  "Return true if CHANNEL1 and CHANNEL2 are the same for all practical
+purposes."
+  ;; Assume that the URL matters less than the name.
+  (eq? (channel-name channel1) (channel-name channel2)))
+
+(define (display-channel-news profile)
+  "Display new about the channels of PROFILE "
+  (define previous
+    (and=> (relative-generation profile -1)
+           (cut generation-file-name profile <>)))
+
+  (when previous
+    (let ((old-channels (profile-channels previous))
+          (new-channels (profile-channels profile)))
+      (and (pair? old-channels) (pair? new-channels)
+           (begin
+             (match (lset-difference channel=? new-channels old-channels)
+               (()
+                #t)
+               (new
+                (let ((count (length new)))
+                  (format (current-error-port)
+                          (N_ "  ~*One new channel:~%"
+                              "  ~a new channels:~%" count)
+                          count)
+                  (for-each display-channel new))))
+             (match (lset-difference channel=? old-channels new-channels)
+               (()
+                #t)
+               (removed
+                (let ((count (length removed)))
+                  (format (current-error-port)
+                          (N_ "  ~*One channel removed:~%"
+                              "  ~a channels removed:~%" count)
+                          count)
+                  (for-each display-channel removed)))))))))
+
+(define (display-news profile)
+  ;; Display profile news, with the understanding that this process represents
+  ;; the newest generation.
+  (display-profile-news profile
+                        #:current-is-newer? #t)
+
+  (display-channel-news profile))
+
 (define* (build-and-install instances profile
                             #:key use-substitutes? verbose? dry-run?)
   "Build the tool from SOURCE, and install it in PROFILE.  When DRY-RUN? is
@@ -493,10 +549,7 @@ list of package changes.")))))
                ((numbers ...)
                 (list-generations profile numbers)))))))
     (('display-news)
-     ;; Display profile news, with the understanding that this process
-     ;; represents the newest generation.
-     (display-profile-news profile
-                           #:current-is-newer? #t))))
+     (display-news profile))))
 
 (define (channel-list opts)
   "Return the list of channels to use.  If OPTS specify a channel file,
-- 
2.23.0

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [bug#37413] [PATCH 2/9] git: 'update-cached-checkout' avoids network access when unnecessary.
  2019-09-15 22:20 ` [bug#37413] [PATCH 1/9] pull: '--news' shows the list of channels added or removed Ludovic Courtès
@ 2019-09-15 22:20   ` Ludovic Courtès
  2019-09-15 22:21   ` [bug#37413] [PATCH 3/9] git: Add 'commit-difference' Ludovic Courtès
                     ` (6 subsequent siblings)
  7 siblings, 0 replies; 45+ messages in thread
From: Ludovic Courtès @ 2019-09-15 22:20 UTC (permalink / raw)
  To: 37413

* guix/git.scm (reference-available?): New procedure.
(update-cached-checkout): Avoid call to 'remote-fetch' when REPOSITORY
already contains REF.
---
 guix/git.scm | 18 +++++++++++++++++-
 1 file changed, 17 insertions(+), 1 deletion(-)

diff --git a/guix/git.scm b/guix/git.scm
index de98fed40c..92a7353b5a 100644
--- a/guix/git.scm
+++ b/guix/git.scm
@@ -220,6 +220,21 @@ dynamic extent of EXP."
               (G_ "Support for submodules is missing; \
 please upgrade Guile-Git.~%"))))
 
+(define (reference-available? repository ref)
+  "Return true if REF, a reference such as '(commit . \"cabba9e\"), is
+definitely available in REPOSITORY, false otherwise."
+  (match ref
+    (('commit . commit)
+     (catch 'git-error
+       (lambda ()
+         (->bool (commit-lookup repository (string->oid commit))))
+       (lambda (key error . rest)
+         (if (= GIT_ENOTFOUND (git-error-code error))
+             #f
+             (apply throw key error rest)))))
+    (_
+     #f)))
+
 (define* (update-cached-checkout url
                                  #:key
                                  (ref '(branch . "master"))
@@ -254,7 +269,8 @@ When RECURSIVE? is true, check out submodules as well, if any."
                              (repository-open cache-directory)
                              (clone* url cache-directory))))
      ;; Only fetch remote if it has not been cloned just before.
-     (when cache-exists?
+     (when (and cache-exists?
+                (not (reference-available? repository ref)))
        (remote-fetch (remote-lookup repository "origin")))
      (when recursive?
        (update-submodules repository #:log-port log-port))
-- 
2.23.0

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [bug#37413] [PATCH 3/9] git: Add 'commit-difference'.
  2019-09-15 22:20 ` [bug#37413] [PATCH 1/9] pull: '--news' shows the list of channels added or removed Ludovic Courtès
  2019-09-15 22:20   ` [bug#37413] [PATCH 2/9] git: 'update-cached-checkout' avoids network access when unnecessary Ludovic Courtès
@ 2019-09-15 22:21   ` Ludovic Courtès
  2019-09-15 22:21   ` [bug#37413] [PATCH 4/9] channels: Add support for a news file Ludovic Courtès
                     ` (5 subsequent siblings)
  7 siblings, 0 replies; 45+ messages in thread
From: Ludovic Courtès @ 2019-09-15 22:21 UTC (permalink / raw)
  To: 37413

* guix/git.scm (commit-closure, commit-difference): New procedures.
* guix/tests/git.scm, tests/git.scm: New files.
* Makefile.am (dist_noinst_DATA): Add guix/tests/git.scm.
(SCM_TESTS): Add tests/git.scm.
---
 .dir-locals.el     |  1 +
 Makefile.am        |  6 ++-
 guix/git.scm       | 40 +++++++++++++++++++
 guix/tests/git.scm | 97 +++++++++++++++++++++++++++++++++++++++++++++
 tests/git.scm      | 99 ++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 242 insertions(+), 1 deletion(-)
 create mode 100644 guix/tests/git.scm
 create mode 100644 tests/git.scm

diff --git a/.dir-locals.el b/.dir-locals.el
index 228685a69f..22aac2c402 100644
--- a/.dir-locals.el
+++ b/.dir-locals.el
@@ -90,6 +90,7 @@
    (eval . (put 'eventually 'scheme-indent-function 1))
 
    (eval . (put 'call-with-progress-reporter 'scheme-indent-function 1))
+   (eval . (put 'with-temporary-git-repository 'scheme-indent-function 2))
 
    ;; This notably allows '(' in Paredit to not insert a space when the
    ;; preceding symbol is one of these.
diff --git a/Makefile.am b/Makefile.am
index 7e3b5c1070..8683622a3a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -306,7 +306,10 @@ STORE_MODULES =					\
 MODULES += $(STORE_MODULES)
 
 # Internal modules with test suite support.
-dist_noinst_DATA = guix/tests.scm guix/tests/http.scm
+dist_noinst_DATA =				\
+  guix/tests.scm				\
+  guix/tests/http.scm				\
+  guix/tests/git.scm
 
 # Auxiliary files for packages.
 AUX_FILES =						\
@@ -390,6 +393,7 @@ SCM_TESTS =					\
   tests/file-systems.scm			\
   tests/gem.scm				\
   tests/gexp.scm				\
+  tests/git.scm					\
   tests/glob.scm				\
   tests/gnu-maintenance.scm			\
   tests/grafts.scm				\
diff --git a/guix/git.scm b/guix/git.scm
index 92a7353b5a..d7dddde3a7 100644
--- a/guix/git.scm
+++ b/guix/git.scm
@@ -28,6 +28,7 @@
   #:use-module (guix utils)
   #:use-module (guix records)
   #:use-module (guix gexp)
+  #:use-module (guix sets)
   #:use-module (rnrs bytevectors)
   #:use-module (ice-9 match)
   #:use-module (srfi srfi-1)
@@ -37,8 +38,10 @@
   #:export (%repository-cache-directory
             honor-system-x509-certificates!
 
+            with-repository
             update-cached-checkout
             latest-repository-commit
+            commit-difference
 
             git-checkout
             git-checkout?
@@ -339,6 +342,43 @@ Log progress and checkout info to LOG-PORT."
 
 (set-exception-printer! 'git-error print-git-error)
 
+\f
+;;;
+;;; Commit difference.
+;;;
+
+(define (commit-closure commit)
+  "Return the closure of COMMIT as a set."
+  (let loop ((commits (list commit))
+             (visited (setq)))
+    (match commits
+      (()
+       visited)
+      ((head . tail)
+       (if (set-contains? visited head)
+           (loop tail visited)
+           (loop (append (commit-parents head) tail)
+                 (set-insert head visited)))))))
+
+(define (commit-difference new old)
+  "Return the list of commits between NEW and OLD, where OLD is assumed to be
+an ancestor of NEW.
+
+Essentially, this computes the set difference between the closure of NEW and
+that of OLD."
+  (let loop ((commits (list new))
+             (result '())
+             (visited (commit-closure old)))
+    (match commits
+      (()
+       (reverse result))
+      ((head . tail)
+       (if (set-contains? visited head)
+           (loop tail result visited)
+           (loop (append (commit-parents head) tail)
+                 (cons head result)
+                 (set-insert head visited)))))))
+
 \f
 ;;;
 ;;; Checkouts.
diff --git a/guix/tests/git.scm b/guix/tests/git.scm
new file mode 100644
index 0000000000..52abe77c83
--- /dev/null
+++ b/guix/tests/git.scm
@@ -0,0 +1,97 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2019 Ludovic Courtès <ludo@gnu.org>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix 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.
+;;;
+;;; GNU Guix 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 GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (guix tests git)
+  #:use-module (git)
+  #:use-module (guix utils)
+  #:use-module (guix build utils)
+  #:use-module (ice-9 match)
+  #:use-module (ice-9 control)
+  #:export (git-command
+            with-temporary-git-repository
+            find-commit))
+
+(define git-command
+  (make-parameter "git"))
+
+(define (populate-git-repository directory directives)
+  "Initialize a new Git checkout and repository in DIRECTORY and apply
+DIRECTIVES.  Each element of DIRECTIVES is an sexp like:
+
+  (add \"foo.txt\" \"hi!\")
+
+Return DIRECTORY on success."
+
+  ;; Note: As of version 0.2.0, Guile-Git lacks the necessary bindings to do
+  ;; all this, so resort to the "git" command.
+  (define (git command . args)
+    (apply invoke (git-command) "-C" directory
+           command args))
+
+  (mkdir-p directory)
+  (git "init")
+
+  (let loop ((directives directives))
+    (match directives
+      (()
+       directory)
+      ((('add file contents) rest ...)
+       (let ((file (string-append directory "/" file)))
+         (mkdir-p (dirname file))
+         (call-with-output-file file
+           (lambda (port)
+             (display contents port)))
+         (git "add" file)
+         (loop rest)))
+      ((('commit text) rest ...)
+       (git "commit" "-m" text)
+       (loop rest))
+      ((('branch name) rest ...)
+       (git "branch" name)
+       (loop rest))
+      ((('checkout branch) rest ...)
+       (git "checkout" branch)
+       (loop rest))
+      ((('merge branch message) rest ...)
+       (git "merge" branch "-m" message)
+       (loop rest)))))
+
+(define (call-with-temporary-git-repository directives proc)
+  (call-with-temporary-directory
+   (lambda (directory)
+     (populate-git-repository directory directives)
+     (proc directory))))
+
+(define-syntax-rule (with-temporary-git-repository directory
+                                                   directives exp ...)
+  "Evaluate EXP in a context where DIRECTORY contains a checkout populated as
+per DIRECTIVES."
+  (call-with-temporary-git-repository directives
+                                      (lambda (directory)
+                                        exp ...)))
+
+(define (find-commit repository message)
+  "Return the commit in REPOSITORY whose message includes MESSAGE, a string."
+  (let/ec return
+    (fold-commits (lambda (commit _)
+                    (and (string-contains (commit-message commit)
+                                          message)
+                         (return commit)))
+                  #f
+                  repository)
+    (error "commit not found" message)))
diff --git a/tests/git.scm b/tests/git.scm
new file mode 100644
index 0000000000..8ba10ece51
--- /dev/null
+++ b/tests/git.scm
@@ -0,0 +1,99 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2019 Ludovic Courtès <ludo@gnu.org>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix 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.
+;;;
+;;; GNU Guix 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 GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (test-git)
+  #:use-module (git)
+  #:use-module (guix git)
+  #:use-module (guix tests git)
+  #:use-module (guix build utils)
+  #:use-module (srfi srfi-1)
+  #:use-module (srfi srfi-64))
+
+;; Test the (guix git) tools.
+
+(test-begin "git")
+
+;; 'with-temporary-git-repository' relies on the 'git' command.
+(unless (which (git-command)) (test-skip 1))
+(test-assert "commit-difference, linear history"
+  (with-temporary-git-repository directory
+      '((add "a.txt" "A")
+        (commit "first commit")
+        (add "b.txt" "B")
+        (commit "second commit")
+        (add "c.txt" "C")
+        (commit "third commit")
+        (add "d.txt" "D")
+        (commit "fourth commit"))
+    (with-repository directory repository
+      (let ((commit1 (find-commit repository "first"))
+            (commit2 (find-commit repository "second"))
+            (commit3 (find-commit repository "third"))
+            (commit4 (find-commit repository "fourth")))
+        (and (lset= eq? (commit-difference commit4 commit1)
+                    (list commit2 commit3 commit4))
+             (lset= eq? (commit-difference commit4 commit2)
+                    (list commit3 commit4))
+             (equal? (commit-difference commit3 commit2)
+                     (list commit3))
+
+             ;; COMMIT4 is not an ancestor of COMMIT1 so we should get the
+             ;; empty list.
+             (null? (commit-difference commit1 commit4)))))))
+
+(unless (which (git-command)) (test-skip 1))
+(test-assert "commit-difference, fork"
+  (with-temporary-git-repository directory
+      '((add "a.txt" "A")
+        (commit "first commit")
+        (branch "devel")
+        (checkout "devel")
+        (add "devel/1.txt" "1")
+        (commit "first devel commit")
+        (add "devel/2.txt" "2")
+        (commit "second devel commit")
+        (checkout "master")
+        (add "b.txt" "B")
+        (commit "second commit")
+        (add "c.txt" "C")
+        (commit "third commit")
+        (merge "devel" "merge")
+        (add "d.txt" "D")
+        (commit "fourth commit"))
+    (with-repository directory repository
+      (let ((master1 (find-commit repository "first commit"))
+            (master2 (find-commit repository "second commit"))
+            (master3 (find-commit repository "third commit"))
+            (master4 (find-commit repository "fourth commit"))
+            (devel1  (find-commit repository "first devel"))
+            (devel2  (find-commit repository "second devel"))
+            (merge   (find-commit repository "merge")))
+        (and (equal? (commit-difference master4 merge)
+                     (list master4))
+             (lset= eq? (commit-difference master3 master1)
+                    (list master3 master2))
+             (lset= eq? (commit-difference devel2 master1)
+                    (list devel2 devel1))
+
+             ;; The merge occurred between MASTER2 and MASTER4 so here we
+             ;; expect to see all the commits from the "devel" branch in
+             ;; addition to those on "master".
+             (lset= eq? (commit-difference master4 master2)
+                    (list master4 merge master3 devel1 devel2)))))))
+
+(test-end "git")
-- 
2.23.0

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [bug#37413] [PATCH 4/9] channels: Add support for a news file.
  2019-09-15 22:20 ` [bug#37413] [PATCH 1/9] pull: '--news' shows the list of channels added or removed Ludovic Courtès
  2019-09-15 22:20   ` [bug#37413] [PATCH 2/9] git: 'update-cached-checkout' avoids network access when unnecessary Ludovic Courtès
  2019-09-15 22:21   ` [bug#37413] [PATCH 3/9] git: Add 'commit-difference' Ludovic Courtès
@ 2019-09-15 22:21   ` Ludovic Courtès
  2019-09-15 22:21   ` [bug#37413] [PATCH 5/9] ui: Add 'current-message-language' Ludovic Courtès
                     ` (4 subsequent siblings)
  7 siblings, 0 replies; 45+ messages in thread
From: Ludovic Courtès @ 2019-09-15 22:21 UTC (permalink / raw)
  To: 37413

* guix/channels.scm (<channel-metadata>)[news-file]: New field.
(read-channel-metadata): Set the 'news-file' field.
(read-channel-metadata-from-source): Likewise.
(<channel-news>, <channel-news-entry>): New record types.
(sexp->channel-news-entry, read-channel-news)
(channel-news-for-commit): New procedures.
* guix/tests/git.scm (populate-git-repository): For 'add', allow
CONTENTS to be a procedure.
* tests/channels.scm ("channel-news, no news")
("channel-news, one entry"): New tests.
* doc/guix.texi (Channels): Document it.
---
 doc/guix.texi      |  51 +++++++++++++++++++
 guix/channels.scm  | 121 ++++++++++++++++++++++++++++++++++++++++++---
 guix/tests/git.scm |   7 ++-
 tests/channels.scm |  96 +++++++++++++++++++++++++++++++++++
 4 files changed, 266 insertions(+), 9 deletions(-)

diff --git a/doc/guix.texi b/doc/guix.texi
index 39d4b865f6..c7fe9f3907 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -3946,6 +3946,57 @@ add a meta-data file @file{.guix-channel} that contains:
   (directory "guix"))
 @end lisp
 
+@cindex news, for channels
+@subsection Writing Channel News
+
+Channel authors may occasionally want to communicate to their users
+information about important changes in the channel.  You'd send them all
+an email, but that's not convenient.
+
+Instead, channels can provide a @dfn{news file}; when the channel users
+run @command{guix pull}, that news file is automatically read and
+@command{guix pull --news} can display the announcements that correspond
+to the new commits that have been pulled, if any.
+
+To do that, channel authors must first declare the name of the news file
+in their @file{.guix-channel} file:
+
+@lisp
+(channel
+  (version 0)
+  (news-file "etc/news.txt"))
+@end lisp
+
+The news file itself, @file{etc/news.txt} in this example, must look
+something like this:
+
+@lisp
+(channel-news
+  (version 0)
+  (entry (commit "d894ab8e9bfabcefa6c49d9ba2e834dd5a73a300")
+         (title ("en" "Fixed terrible bug")
+                ("fr" "Oh la la"))
+         (body ("en" "@@emph@{Good news@}!  It's fixed!")))
+  (entry (commit "bdcabe815cd28144a2d2b4bc3c5057b051fa9906")
+         (title ("en" "Added a great package")
+                ("ca" "Què vol dir guix?"))
+         (body ("en" "Don't miss the @@code@{hello@} package!"))))
+@end lisp
+
+The file consists of a list of @dfn{news entries}.  Each entry is
+associated with a commit: it describes changes made in this commit,
+possibly in preceding commits as well.  Users see entries only the first
+time they obtain the commit the entry refers to.
+
+The @code{title} field should be a one-line summary while @code{body}
+can be arbitrary long, and both can contain Texinfo markup
+(@pxref{Overview,,, texinfo, GNU Texinfo}).  Both the title and body are
+a list of language tag/message tuples, which allows @command{guix pull}
+to display news in the language that corresponds to the user's locale.
+
+So yes, you could use your channel as a blog.  But beware, this is
+@emph{not quite} what your users might expect.
+
 @subsection Replicating Guix
 
 @cindex pinning, channels
diff --git a/guix/channels.scm b/guix/channels.scm
index ebb2cacbc7..63e3e2f49e 100644
--- a/guix/channels.scm
+++ b/guix/channels.scm
@@ -19,6 +19,7 @@
 ;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
 
 (define-module (guix channels)
+  #:use-module (git)
   #:use-module (guix git)
   #:use-module (guix records)
   #:use-module (guix gexp)
@@ -29,6 +30,7 @@
   #:use-module (guix derivations)
   #:use-module (guix combinators)
   #:use-module (guix diagnostics)
+  #:use-module (guix sets)
   #:use-module (guix store)
   #:use-module (guix i18n)
   #:use-module ((guix utils)
@@ -67,7 +69,14 @@
             %channel-profile-hooks
             channel-instances->derivation
 
-            profile-channels))
+            profile-channels
+
+            channel-news-entry?
+            channel-news-entry-commit
+            channel-news-entry-title
+            channel-news-entry-body
+
+            channel-news-for-commit))
 
 ;;; Commentary:
 ;;;
@@ -110,10 +119,11 @@
   (checkout  channel-instance-checkout))
 
 (define-record-type <channel-metadata>
-  (channel-metadata directory dependencies)
+  (channel-metadata directory dependencies news-file)
   channel-metadata?
   (directory     channel-metadata-directory)      ;string with leading slash
-  (dependencies  channel-metadata-dependencies))  ;list of <channel>
+  (dependencies  channel-metadata-dependencies)   ;list of <channel>
+  (news-file     channel-metadata-news-file))     ;string | #f
 
 (define (channel-reference channel)
   "Return the \"reference\" for CHANNEL, an sexp suitable for
@@ -129,12 +139,13 @@ if valid metadata could not be read from PORT."
   (match (read port)
     (('channel ('version 0) properties ...)
      (let ((directory    (and=> (assoc-ref properties 'directory) first))
-           (dependencies (or (assoc-ref properties 'dependencies) '())))
+           (dependencies (or (assoc-ref properties 'dependencies) '()))
+           (news-file    (and=> (assoc-ref properties 'news-file) first)))
        (channel-metadata
-        (cond ((not directory) "/")
+        (cond ((not directory) "/")               ;directory
               ((string-prefix? "/" directory) directory)
               (else (string-append "/" directory)))
-        (map (lambda (item)
+        (map (lambda (item)                       ;dependencies
                (let ((get (lambda* (key #:optional default)
                             (or (and=> (assoc-ref item key) first) default))))
                  (and-let* ((name (get 'name))
@@ -145,7 +156,8 @@ if valid metadata could not be read from PORT."
                     (branch branch)
                     (url url)
                     (commit (get 'commit))))))
-             dependencies))))
+             dependencies)
+        news-file)))                              ;news-file
     ((and ('channel ('version version) _ ...) sexp)
      (raise (condition
              (&message (message "unsupported '.guix-channel' version"))
@@ -169,7 +181,7 @@ doesn't exist."
         read-channel-metadata))
     (lambda args
       (if (= ENOENT (system-error-errno args))
-          (channel-metadata "/" '())
+          (channel-metadata "/" '() #f)
           (apply throw args)))))
 
 (define (channel-instance-metadata instance)
@@ -560,3 +572,96 @@ PROFILE is not a profile created by 'guix pull', return the empty list."
               ;; Show most recently installed packages last.
               (reverse
                (manifest-entries (profile-manifest profile)))))
+
+\f
+;;;
+;;; News.
+;;;
+
+;; Channel news.
+(define-record-type <channel-news>
+  (channel-news entries)
+  channel-news?
+  (entries channel-news-entries))                 ;list of <channel-news-entry>
+
+;; News entry, associated with a specific commit of the channel.
+(define-record-type <channel-news-entry>
+  (channel-news-entry commit title body)
+  channel-news-entry?
+  (commit  channel-news-entry-commit)             ;hex string
+  (title   channel-news-entry-title)              ;list of language tag/string pairs
+  (body    channel-news-entry-body))              ;list of language tag/string pairs
+
+(define (sexp->channel-news-entry entry)
+  "Return the <channel-news-entry> record corresponding to ENTRY, an sexp."
+  (match entry
+    (('entry ('commit commit)
+             ('title (title-tags titles) ...)
+             ('body (body-tags bodies) ...)
+             _ ...)
+     (channel-news-entry commit
+                         (map cons title-tags titles)
+                         (map cons body-tags bodies)))
+    (_
+     (raise (condition
+             (&message (message "invalid channel news entry"))
+             (&error-location
+              (location (source-properties->location
+                         (source-properties entry)))))))))
+
+(define (read-channel-news port)
+  "Read a channel news feed from PORT and return it as a <channel-news>
+record."
+  (match (false-if-exception (read port))
+    (('channel-news ('version 0) entries ...)
+     (channel-news (map sexp->channel-news-entry entries)))
+    ((and ('channel-news ('version version) _ ...) sexp)
+     (raise (condition
+             (&message (message "unsupported channel news file"))
+             (&error-location
+              (location (source-properties->location
+                         (source-properties sexp)))))))
+    (#f
+     (raise (condition
+             (&message (message "syntactically invalid channel news file")))))
+    (sexp
+     (raise (condition
+             (&message (message "invalid channel news file"))
+             (&error-location
+              (location (source-properties->location
+                         (source-properties sexp)))))))))
+
+(define* (channel-news-for-commit channel new #:optional old)
+  "Return a list of <channel-news-entry> for CHANNEL between commits OLD and
+NEW.  When OLD is omitted or is #f, return all the news entries of CHANNEL."
+  (catch 'git-error
+    (lambda ()
+      (let* ((checkout  (update-cached-checkout (channel-url channel)
+                                                #:ref `(commit . ,new)))
+             (metadata  (read-channel-metadata-from-source checkout))
+             (news-file (channel-metadata-news-file metadata))
+             (news-file (and news-file
+                             (string-append checkout "/" news-file))))
+        (if (and news-file (file-exists? news-file))
+            (let ((entries (channel-news-entries (call-with-input-file news-file
+                                                   read-channel-news))))
+              (if old
+                  (with-repository checkout repository
+                    (let* ((new     (commit-lookup repository (string->oid new)))
+                           (old     (commit-lookup repository (string->oid old)))
+                           (commits (list->set
+                                     (map (compose oid->string commit-id)
+                                          (commit-difference new old)))))
+                      (filter (lambda (entry)
+                                (set-contains? commits
+                                               (channel-news-entry-commit entry)))
+                              entries)))
+                  entries))
+            '())))
+    (lambda (key error . rest)
+      ;; If commit NEW or commit OLD cannot be found, then something must be
+      ;; wrong (for example, the history of CHANNEL was rewritten and these
+      ;; commits no longer exist upstream), so quietly return the empty list.
+      (if (= GIT_ENOTFOUND (git-error-code error))
+          '()
+          (apply throw key error rest)))))
diff --git a/guix/tests/git.scm b/guix/tests/git.scm
index 52abe77c83..9d5b1ae321 100644
--- a/guix/tests/git.scm
+++ b/guix/tests/git.scm
@@ -18,6 +18,7 @@
 
 (define-module (guix tests git)
   #:use-module (git)
+  #:use-module ((guix git) #:select (with-repository))
   #:use-module (guix utils)
   #:use-module (guix build utils)
   #:use-module (ice-9 match)
@@ -55,7 +56,11 @@ Return DIRECTORY on success."
          (mkdir-p (dirname file))
          (call-with-output-file file
            (lambda (port)
-             (display contents port)))
+             (display (if (string? contents)
+                          contents
+                          (with-repository directory repository
+                            (contents repository)))
+                      port)))
          (git "add" file)
          (loop rest)))
       ((('commit text) rest ...)
diff --git a/tests/channels.scm b/tests/channels.scm
index e83b5437d3..686bd3acee 100644
--- a/tests/channels.scm
+++ b/tests/channels.scm
@@ -28,6 +28,10 @@
   #:use-module (guix gexp)
   #:use-module ((guix utils)
                 #:select (error-location? error-location location-line))
+  #:use-module ((guix build utils) #:select (which))
+  #:use-module (git)
+  #:use-module (guix git)
+  #:use-module (guix tests git)
   #:use-module (srfi srfi-1)
   #:use-module (srfi srfi-26)
   #:use-module (srfi srfi-34)
@@ -246,4 +250,96 @@
                (depends? drv3
                          (list drv2 drv0) (list))))))))
 
+(unless (which (git-command)) (test-skip 1))
+(test-equal "channel-news, no news"
+  '()
+  (with-temporary-git-repository directory
+      '((add "a.txt" "A")
+        (commit "the commit"))
+    (with-repository directory repository
+      (let ((channel (channel (url (string-append "file://" directory))
+                              (name 'foo)))
+            (latest  (reference-name->oid repository "HEAD")))
+        (channel-news-for-commit channel (oid->string latest))))))
+
+(unless (which (git-command)) (test-skip 1))
+(test-assert "channel-news, one entry"
+  (with-temporary-git-repository directory
+      `((add ".guix-channel"
+             ,(object->string
+               '(channel (version 0)
+                         (news-file "news.scm"))))
+        (commit "first commit")
+        (add "src/a.txt" "A")
+        (commit "second commit")
+        (add "news.scm"
+             ,(lambda (repository)
+                (let ((previous
+                       (reference-name->oid repository "HEAD")))
+                  (object->string
+                   `(channel-news
+                     (version 0)
+                     (entry (commit ,(oid->string previous))
+                            (title ("en" "New file!"))
+                            (body ("en" "Yeah, a.txt."))))))))
+        (commit "third commit")
+        (add "src/b.txt" "B")
+        (commit "fourth commit")
+        (add "news.scm"
+             ,(lambda (repository)
+                (let ((second
+                       (commit-id
+                        (find-commit repository "second commit")))
+                      (previous
+                       (reference-name->oid repository "HEAD")))
+                  (object->string
+                   `(channel-news
+                     (version 0)
+                     (entry (commit ,(oid->string previous))
+                            (title ("en" "Another file!"))
+                            (body ("en" "Yeah, b.txt.")))
+                     (entry (commit ,(oid->string second))
+                            (title ("en" "Old news."))
+                            (body ("en" "For a.txt"))))))))
+        (commit "fifth commit"))
+    (with-repository directory repository
+      (define (find-commit* message)
+        (oid->string (commit-id (find-commit repository message))))
+
+      (let ((channel (channel (url (string-append "file://" directory))
+                              (name 'foo)))
+            (commit1 (find-commit* "first commit"))
+            (commit2 (find-commit* "second commit"))
+            (commit3 (find-commit* "third commit"))
+            (commit4 (find-commit* "fourth commit"))
+            (commit5 (find-commit* "fifth commit")))
+        ;; First try fetching all the news up to a given commit.
+        (and (null? (channel-news-for-commit channel commit2))
+             (lset= string=?
+                    (map channel-news-entry-commit
+                         (channel-news-for-commit channel commit5))
+                    (list commit2 commit4))
+             (lset= equal?
+                    (map channel-news-entry-title
+                         (channel-news-for-commit channel commit5))
+                    '((("en" . "Another file!")) (("en" . "Old news."))))
+             (lset= string=?
+                    (map channel-news-entry-commit
+                         (channel-news-for-commit channel commit3))
+                    (list commit2))
+
+             ;; Now fetch news entries that apply to a commit range.
+             (lset= string=?
+                    (map channel-news-entry-commit
+                         (channel-news-for-commit channel commit3 commit1))
+                    (list commit2))
+             (lset= string=?
+                    (map channel-news-entry-commit
+                         (channel-news-for-commit channel commit5 commit3))
+                    (list commit4))
+             (lset= string=?
+                    (map channel-news-entry-commit
+                         (channel-news-for-commit channel commit5 commit1))
+                    (list commit4 commit2)))))))
+
 (test-end "channels")
-- 
2.23.0

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [bug#37413] [PATCH 5/9] ui: Add 'current-message-language'.
  2019-09-15 22:20 ` [bug#37413] [PATCH 1/9] pull: '--news' shows the list of channels added or removed Ludovic Courtès
                     ` (2 preceding siblings ...)
  2019-09-15 22:21   ` [bug#37413] [PATCH 4/9] channels: Add support for a news file Ludovic Courtès
@ 2019-09-15 22:21   ` Ludovic Courtès
  2019-09-15 22:21   ` [bug#37413] [PATCH 6/9] pull: Display channel news Ludovic Courtès
                     ` (3 subsequent siblings)
  7 siblings, 0 replies; 45+ messages in thread
From: Ludovic Courtès @ 2019-09-15 22:21 UTC (permalink / raw)
  To: 37413

* guix/ui.scm (%default-message-language): New variable.
(current-message-language): New procedure.
---
 guix/ui.scm | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/guix/ui.scm b/guix/ui.scm
index 7920335928..42043b546c 100644
--- a/guix/ui.scm
+++ b/guix/ui.scm
@@ -120,6 +120,10 @@
             roll-back*
             switch-to-generation*
             delete-generation*
+
+            %default-message-language
+            current-message-language
+
             run-guix-command
             run-guix
             guix-main))
@@ -427,6 +431,20 @@ exiting.  ARGS is the list of arguments received by the 'throw' handler."
 report them in a user-friendly way."
   (call-with-unbound-variable-handling (lambda () exp ...)))
 
+(define %default-message-language
+  ;; Default language to use for messages.
+  (make-parameter "en"))
+
+(define (current-message-language)
+  "Return the language used for messages according to the current locale.
+Return %DEFAULT-MESSAGE-LANGUAGE if that information could not be obtained.  The
+result is an ISO-639-2 language code such as \"ar\", without the territory
+part."
+  (let ((locale (setlocale LC_MESSAGES)))
+    (match (string-index locale #\_)
+      (#f    locale)
+      (index (string-take locale index)))))
+
 (define (install-locale)
   "Install the current locale settings."
   (catch 'system-error
-- 
2.23.0

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [bug#37413] [PATCH 6/9] pull: Display channel news.
  2019-09-15 22:20 ` [bug#37413] [PATCH 1/9] pull: '--news' shows the list of channels added or removed Ludovic Courtès
                     ` (3 preceding siblings ...)
  2019-09-15 22:21   ` [bug#37413] [PATCH 5/9] ui: Add 'current-message-language' Ludovic Courtès
@ 2019-09-15 22:21   ` Ludovic Courtès
  2019-09-15 22:21   ` [bug#37413] [PATCH 7/9] pull: '-l' displays " Ludovic Courtès
                     ` (2 subsequent siblings)
  7 siblings, 0 replies; 45+ messages in thread
From: Ludovic Courtès @ 2019-09-15 22:21 UTC (permalink / raw)
  To: 37413

* guix/scripts/pull.scm (display-news-entry)
(display-channel-specific-news): New procedures.
(display-channel-news): Call it.
(display-new/upgraded-packages): Adjust hint message.
* doc/guix.texi (Invoking guix pull): Mention it.
---
 doc/guix.texi         | 11 +++++---
 guix/scripts/pull.scm | 61 ++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 65 insertions(+), 7 deletions(-)

diff --git a/doc/guix.texi b/doc/guix.texi
index c7fe9f3907..f6e5e919db 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -3709,13 +3709,16 @@ Read the list of channels from @var{file} instead of
 evaluates to a list of channel objects.  @xref{Channels}, for more
 information.
 
+@cindex channel news
 @item --news
 @itemx -N
-Display the list of packages added or upgraded since the previous generation.
+Display the list of packages added or upgraded since the previous
+generation, as well as, occasionally, news written by channel authors
+for their users (@pxref{Channels, Writing Channel News}).
 
-This is the same information as displayed upon @command{guix pull} completion,
-but without ellipses; it is also similar to the output of @command{guix pull
--l} for the last generation (see below).
+The package information is the same as displayed upon @command{guix
+pull} completion, but without ellipses; it is also similar to the output
+of @command{guix pull -l} for the last generation (see below).
 
 @item --list-generations[=@var{pattern}]
 @itemx -l [@var{pattern}]
diff --git a/guix/scripts/pull.scm b/guix/scripts/pull.scm
index ca3b36f98c..8edff1a502 100644
--- a/guix/scripts/pull.scm
+++ b/guix/scripts/pull.scm
@@ -19,6 +19,7 @@
 
 (define-module (guix scripts pull)
   #:use-module (guix ui)
+  #:use-module (guix colors)
   #:use-module (guix utils)
   #:use-module ((guix status) #:select (with-status-verbosity))
   #:use-module (guix scripts)
@@ -208,6 +209,48 @@ purposes."
   ;; Assume that the URL matters less than the name.
   (eq? (channel-name channel1) (channel-name channel2)))
 
+(define (display-news-entry entry language port)
+  "Display ENTRY, a <channel-news-entry>, in LANGUAGE, a language code, to
+PORT."
+  (let ((title (channel-news-entry-title entry))
+        (body  (channel-news-entry-body entry)))
+    (format port "  ~a~%"
+            (highlight
+             (string-trim-right
+              (texi->plain-text (or (assoc-ref title language)
+                                    (assoc-ref title (%default-message-language))
+                                    "")))))
+    (format port (G_ "    commit ~a~%")
+            (channel-news-entry-commit entry))
+    (newline port)
+    (format port "    ~a~%"
+            (indented-string
+             (parameterize ((%text-width (- (%text-width) 4)))
+               (string-trim-right
+                (texi->plain-text (or (assoc-ref body language)
+                                      (assoc-ref body (%default-message-language))
+                                      ""))))
+             4))))
+
+(define* (display-channel-specific-news new old
+                                        #:key (port (current-output-port)))
+  "Display channel news applicable the commits between OLD and NEW, where OLD
+and NEW are <channel> records with a proper 'commit' field."
+  (let ((channel new)
+        (old     (channel-commit old))
+        (new     (channel-commit new)))
+    (when (and old new)
+      (let ((language (current-message-language)))
+        (match (channel-news-for-commit channel new old)
+          (()                                     ;no news is good news
+           #t)
+          ((entries ...)
+           (newline port)
+           (format port (G_ "News for channel '~a'~%")
+                   (channel-name channel))
+           (for-each (cut display-news-entry <> language port) entries)
+           (newline port)))))))
+
 (define (display-channel-news profile)
   "Display new about the channels of PROFILE "
   (define previous
@@ -238,7 +281,20 @@ purposes."
                           (N_ "  ~*One channel removed:~%"
                               "  ~a channels removed:~%" count)
                           count)
-                  (for-each display-channel removed)))))))))
+                  (for-each display-channel removed))))
+
+             ;; Display channel-specific news for those channels that were
+             ;; here before and are still around afterwards.
+             (for-each (match-lambda
+                         ((new old)
+                          (display-channel-specific-news new old)))
+                       (filter-map (lambda (new)
+                                     (define old
+                                       (find (cut channel=? new <>)
+                                             old-channels))
+
+                                     (and old (list new old)))
+                                   new-channels)))))))
 
 (define (display-news profile)
   ;; Display profile news, with the understanding that this process represents
@@ -506,8 +562,7 @@ display long package lists that would fill the user's screen."
     (when (and concise?
                (or (> new-count concise/max-item-count)
                    (> upgraded-count concise/max-item-count)))
-      (display-hint (G_ "Run @command{guix pull --news} to view the complete
-list of package changes.")))))
+      (display-hint (G_ "Run @command{guix pull --news} to read all the news.")))))
 
 (define (display-profile-content-diff profile gen1 gen2)
   "Display the changes in PROFILE GEN2 compared to generation GEN1."
-- 
2.23.0

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [bug#37413] [PATCH 7/9] pull: '-l' displays channel news.
  2019-09-15 22:20 ` [bug#37413] [PATCH 1/9] pull: '--news' shows the list of channels added or removed Ludovic Courtès
                     ` (4 preceding siblings ...)
  2019-09-15 22:21   ` [bug#37413] [PATCH 6/9] pull: Display channel news Ludovic Courtès
@ 2019-09-15 22:21   ` Ludovic Courtès
  2019-09-15 22:21   ` [bug#37413] [PATCH 8/9] Add '.guix-channel' file Ludovic Courtès
  2019-09-15 22:21   ` [bug#37413] [PATCH 9/9] DRAFT etc: Add channel news file Ludovic Courtès
  7 siblings, 0 replies; 45+ messages in thread
From: Ludovic Courtès @ 2019-09-15 22:21 UTC (permalink / raw)
  To: 37413

* guix/scripts/pull.scm (display-channel-news): Make 'previous' a
parameter.
(process-query)[list-generations]: Call 'display-channel-news'.
---
 guix/scripts/pull.scm | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/guix/scripts/pull.scm b/guix/scripts/pull.scm
index 8edff1a502..91ffd0ab98 100644
--- a/guix/scripts/pull.scm
+++ b/guix/scripts/pull.scm
@@ -251,12 +251,12 @@ and NEW are <channel> records with a proper 'commit' field."
            (for-each (cut display-news-entry <> language port) entries)
            (newline port)))))))
 
-(define (display-channel-news profile)
-  "Display new about the channels of PROFILE "
-  (define previous
-    (and=> (relative-generation profile -1)
-           (cut generation-file-name profile <>)))
-
+(define* (display-channel-news profile
+                               #:optional
+                               (previous
+                                (and=> (relative-generation profile -1)
+                                       (cut generation-file-name profile <>))))
+  "Display news about the channels of PROFILE compared to PREVIOUS."
   (when previous
     (let ((old-channels (profile-channels previous))
           (new-channels (profile-channels profile)))
@@ -586,6 +586,8 @@ display long package lists that would fill the user's screen."
               ((first second rest ...)
                (display-profile-content-diff profile
                                              first second)
+               (display-channel-news (generation-file-name profile second)
+                                     (generation-file-name profile first))
                (loop (cons second rest)))
               ((_) #t)
               (()  #t))))))
-- 
2.23.0

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [bug#37413] [PATCH 8/9] Add '.guix-channel' file.
  2019-09-15 22:20 ` [bug#37413] [PATCH 1/9] pull: '--news' shows the list of channels added or removed Ludovic Courtès
                     ` (5 preceding siblings ...)
  2019-09-15 22:21   ` [bug#37413] [PATCH 7/9] pull: '-l' displays " Ludovic Courtès
@ 2019-09-15 22:21   ` Ludovic Courtès
  2019-09-15 22:21   ` [bug#37413] [PATCH 9/9] DRAFT etc: Add channel news file Ludovic Courtès
  7 siblings, 0 replies; 45+ messages in thread
From: Ludovic Courtès @ 2019-09-15 22:21 UTC (permalink / raw)
  To: 37413

* .guix-channel: New file.
* Makefile.am (EXTRA_DIST): Add it.
---
 .guix-channel | 5 +++++
 Makefile.am   | 1 +
 2 files changed, 6 insertions(+)
 create mode 100644 .guix-channel

diff --git a/.guix-channel b/.guix-channel
new file mode 100644
index 0000000000..3e618d79f8
--- /dev/null
+++ b/.guix-channel
@@ -0,0 +1,5 @@
+;; This is a Guix channel.
+
+(channel
+  (version 0)
+  (news-file "etc/news.scm"))
diff --git a/Makefile.am b/Makefile.am
index 8683622a3a..fe8aa3f52b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -532,6 +532,7 @@ EXTRA_DIST +=						\
   TODO							\
   CODE-OF-CONDUCT					\
   .dir-locals.el					\
+  .guix-channel						\
   scripts/guix.in					\
   etc/guix-install.sh					\
   build-aux/build-self.scm				\
-- 
2.23.0

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [bug#37413] [PATCH 9/9] DRAFT etc: Add channel news file.
  2019-09-15 22:20 ` [bug#37413] [PATCH 1/9] pull: '--news' shows the list of channels added or removed Ludovic Courtès
                     ` (6 preceding siblings ...)
  2019-09-15 22:21   ` [bug#37413] [PATCH 8/9] Add '.guix-channel' file Ludovic Courtès
@ 2019-09-15 22:21   ` Ludovic Courtès
  7 siblings, 0 replies; 45+ messages in thread
From: Ludovic Courtès @ 2019-09-15 22:21 UTC (permalink / raw)
  To: 37413

DRAFT: Update commit ID before pushing.

* etc/news.scm: New file.
* Makefile.am (EXTRA_DIST): Add it.
---
 Makefile.am  |  1 +
 etc/news.scm | 13 +++++++++++++
 2 files changed, 14 insertions(+)
 create mode 100644 etc/news.scm

diff --git a/Makefile.am b/Makefile.am
index fe8aa3f52b..6fcd367d63 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -535,6 +535,7 @@ EXTRA_DIST +=						\
   .guix-channel						\
   scripts/guix.in					\
   etc/guix-install.sh					\
+  etc/news.scm						\
   build-aux/build-self.scm				\
   build-aux/compile-all.scm				\
   build-aux/hydra/evaluate.scm				\
diff --git a/etc/news.scm b/etc/news.scm
new file mode 100644
index 0000000000..a0a5e15766
--- /dev/null
+++ b/etc/news.scm
@@ -0,0 +1,13 @@
+;; GNU Guix news, for use by 'guix pull'.
+
+(channel-news
+ (version 0)
+ (entry (commit "ddbc4977068ac273127f98321956c807ae030cf1")
+        (title ("en" "New channel news mechanism")
+               ("fr" "Nouveau mécanisme d'information sur les canaux"))
+        (body
+         ("en" "You are reading this message through the new channel news
+mechanism, congratulations!  This mechanism allows channel authors to provide
+@dfn{news entries} that their users can view with @command{guix pull}.  Run
+@command{info \"(guix) Invoking guix pull\"} for more info.")
+         ("fr" "Vous lisez ce message à travers bla bla bla FIXME"))))
-- 
2.23.0

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [bug#37413] [PATCH 0/9] Channel news distribution mechanism
  2019-09-15 22:10 [bug#37413] [PATCH 0/9] Channel news distribution mechanism Ludovic Courtès
  2019-09-15 22:20 ` [bug#37413] [PATCH 1/9] pull: '--news' shows the list of channels added or removed Ludovic Courtès
@ 2019-09-16  9:31 ` Ricardo Wurmus
  2019-09-16 12:59   ` Ludovic Courtès
  2019-09-16 21:25 ` Ludovic Courtès
  2 siblings, 1 reply; 45+ messages in thread
From: Ricardo Wurmus @ 2019-09-16  9:31 UTC (permalink / raw)
  To: Ludovic Courtès; +Cc: 37413


Ludovic Courtès <ludo@gnu.org> writes:

> It’s a bit weird to refer to commits from within a file in the repo
> because it almost forces you to push so that you know the commit ID
> that your news entry should refer to.  News entries become obsolete
> if you rebase, too.  I think it’s acceptable, though.

Would tags be acceptable instead of commit IDs?  I think version tags
would be easier to deal with than commit IDs and it wouldn’t require
time travel as you can just make up a new version and refer to that in
the news file.

--
Ricardo

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [bug#37413] [PATCH 0/9] Channel news distribution mechanism
  2019-09-16  9:31 ` [bug#37413] [PATCH 0/9] Channel news distribution mechanism Ricardo Wurmus
@ 2019-09-16 12:59   ` Ludovic Courtès
  2019-09-16 13:16     ` Ricardo Wurmus
  0 siblings, 1 reply; 45+ messages in thread
From: Ludovic Courtès @ 2019-09-16 12:59 UTC (permalink / raw)
  To: Ricardo Wurmus; +Cc: 37413

Hello,

Ricardo Wurmus <rekado@elephly.net> skribis:

> Ludovic Courtès <ludo@gnu.org> writes:
>
>> It’s a bit weird to refer to commits from within a file in the repo
>> because it almost forces you to push so that you know the commit ID
>> that your news entry should refer to.  News entries become obsolete
>> if you rebase, too.  I think it’s acceptable, though.
>
> Would tags be acceptable instead of commit IDs?  I think version tags
> would be easier to deal with than commit IDs and it wouldn’t require
> time travel as you can just make up a new version and refer to that in
> the news file.

So you would add a Git tag like “news-xyz” on the commit of interest and
then refer to it in the news entry?

I guess that would work.  Compared to this patch set, we’d just need to
check whether the entry’s commit is a commit or a tag, and in the latter
case resolve it.  How does that sound?

I tend to use tags sparsely, almost exclusively for releases, so I would
not naturally propose to use them for this purpose, but maybe it’s not a
problem to have many tags in the repo after all?

Thanks,
Ludo’.

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [bug#37413] [PATCH 0/9] Channel news distribution mechanism
  2019-09-16 12:59   ` Ludovic Courtès
@ 2019-09-16 13:16     ` Ricardo Wurmus
  2019-09-16 15:10       ` Ludovic Courtès
  0 siblings, 1 reply; 45+ messages in thread
From: Ricardo Wurmus @ 2019-09-16 13:16 UTC (permalink / raw)
  To: Ludovic Courtès; +Cc: 37413


Hi Ludo,

> Ricardo Wurmus <rekado@elephly.net> skribis:
>
>> Ludovic Courtès <ludo@gnu.org> writes:
>>
>>> It’s a bit weird to refer to commits from within a file in the repo
>>> because it almost forces you to push so that you know the commit ID
>>> that your news entry should refer to.  News entries become obsolete
>>> if you rebase, too.  I think it’s acceptable, though.
>>
>> Would tags be acceptable instead of commit IDs?  I think version tags
>> would be easier to deal with than commit IDs and it wouldn’t require
>> time travel as you can just make up a new version and refer to that in
>> the news file.
>
> So you would add a Git tag like “news-xyz” on the commit of interest and
> then refer to it in the news entry?

No, I actually meant for releases.  For a channel it seems to me that a
release would make sense whenever information must be displayed to the
users (presumably to announce breaking changes).

--
Ricardo

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [bug#37413] [PATCH 0/9] Channel news distribution mechanism
  2019-09-16 13:16     ` Ricardo Wurmus
@ 2019-09-16 15:10       ` Ludovic Courtès
  2019-09-16 17:16         ` Ricardo Wurmus
  0 siblings, 1 reply; 45+ messages in thread
From: Ludovic Courtès @ 2019-09-16 15:10 UTC (permalink / raw)
  To: Ricardo Wurmus; +Cc: 37413

Hi,

Ricardo Wurmus <rekado@elephly.net> skribis:

>> Ricardo Wurmus <rekado@elephly.net> skribis:
>>
>>> Ludovic Courtès <ludo@gnu.org> writes:
>>>
>>>> It’s a bit weird to refer to commits from within a file in the repo
>>>> because it almost forces you to push so that you know the commit ID
>>>> that your news entry should refer to.  News entries become obsolete
>>>> if you rebase, too.  I think it’s acceptable, though.
>>>
>>> Would tags be acceptable instead of commit IDs?  I think version tags
>>> would be easier to deal with than commit IDs and it wouldn’t require
>>> time travel as you can just make up a new version and refer to that in
>>> the news file.
>>
>> So you would add a Git tag like “news-xyz” on the commit of interest and
>> then refer to it in the news entry?
>
> No, I actually meant for releases.  For a channel it seems to me that a
> release would make sense whenever information must be displayed to the
> users (presumably to announce breaking changes).

The way I see it, we could use it in between release.  I’d have used it
to announce lzip support (and then explain how users can migrate, which
I think many haven’t done because they’re unaware), improvements to
‘guix pack’, the addition of ‘guix deploy’, things like that.

Does that make sense?

Authors of third-party channels may also want to use it anytime.

Thanks,
Ludo’.

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [bug#37413] [PATCH 0/9] Channel news distribution mechanism
  2019-09-16 15:10       ` Ludovic Courtès
@ 2019-09-16 17:16         ` Ricardo Wurmus
  0 siblings, 0 replies; 45+ messages in thread
From: Ricardo Wurmus @ 2019-09-16 17:16 UTC (permalink / raw)
  To: Ludovic Courtès; +Cc: 37413


Ludovic Courtès <ludo@gnu.org> writes:

> Hi,
>
> Ricardo Wurmus <rekado@elephly.net> skribis:
>
>>> Ricardo Wurmus <rekado@elephly.net> skribis:
>>>
>>>> Ludovic Courtès <ludo@gnu.org> writes:
>>>>
>>>>> It’s a bit weird to refer to commits from within a file in the repo
>>>>> because it almost forces you to push so that you know the commit ID
>>>>> that your news entry should refer to.  News entries become obsolete
>>>>> if you rebase, too.  I think it’s acceptable, though.
>>>>
>>>> Would tags be acceptable instead of commit IDs?  I think version tags
>>>> would be easier to deal with than commit IDs and it wouldn’t require
>>>> time travel as you can just make up a new version and refer to that in
>>>> the news file.
>>>
>>> So you would add a Git tag like “news-xyz” on the commit of interest and
>>> then refer to it in the news entry?
>>
>> No, I actually meant for releases.  For a channel it seems to me that a
>> release would make sense whenever information must be displayed to the
>> users (presumably to announce breaking changes).
>
> The way I see it, we could use it in between release.  I’d have used it
> to announce lzip support (and then explain how users can migrate, which
> I think many haven’t done because they’re unaware), improvements to
> ‘guix pack’, the addition of ‘guix deploy’, things like that.
>
> Does that make sense?

I see.  Well, I think there’s no harm in pushing the changes first and
then pushing a follow-up commit with the news entry referencing the
commit.

This all looks good to me.  Sure seems useful!

--
Ricardo

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [bug#37413] [PATCH 0/9] Channel news distribution mechanism
  2019-09-15 22:10 [bug#37413] [PATCH 0/9] Channel news distribution mechanism Ludovic Courtès
  2019-09-15 22:20 ` [bug#37413] [PATCH 1/9] pull: '--news' shows the list of channels added or removed Ludovic Courtès
  2019-09-16  9:31 ` [bug#37413] [PATCH 0/9] Channel news distribution mechanism Ricardo Wurmus
@ 2019-09-16 21:25 ` Ludovic Courtès
  2019-09-16 21:49   ` Julien Lepiller
  2019-09-21 21:12   ` [bug#37413] [PATCH v2 00/11] " Ludovic Courtès
  2 siblings, 2 replies; 45+ messages in thread
From: Ludovic Courtès @ 2019-09-16 21:25 UTC (permalink / raw)
  To: 37413, Julien Lepiller, "pelzflorian (Florian Pelz)"

Howdy,

Ludovic Courtès <ludo@gnu.org> skribis:

> This patch series adds a mechanism for channel authors to distribute
> “news entries” alongside their channels.  The goal is to provide a
> way for channel authors (including authors of the 'guix channel!)
> to inform their users about important changes.
>
> For example, you’d tell people about package upgrades that require
> manual intervention, about important new features, bug fixes, etc.

[...]

> Regarding i18n, the title and body of an entry can be provided in
> several languages.  I wonder what workflow we should adopt;
> obviously the TP wouldn’t be appropriate, and there’s no gettext
> involved.

Julien, Florian: any thoughts on how we could organize to translate
these news entries?  (See <https://issues.guix.gnu.org/issue/37413>.)

I’m thinking we could have an alias with volunteer translators who
would provide text fragments by email to the developer who wrote the
news?  Does that sound reasonable?

Thanks,
Ludo’.

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [bug#37413] [PATCH 0/9] Channel news distribution mechanism
  2019-09-16 21:25 ` Ludovic Courtès
@ 2019-09-16 21:49   ` Julien Lepiller
  2019-09-16 22:52     ` pelzflorian (Florian Pelz)
  2019-09-21 21:12   ` [bug#37413] [PATCH v2 00/11] " Ludovic Courtès
  1 sibling, 1 reply; 45+ messages in thread
From: Julien Lepiller @ 2019-09-16 21:49 UTC (permalink / raw)
  To: Ludovic Courtès; +Cc: 37413

Le Mon, 16 Sep 2019 23:25:55 +0200,
Ludovic Courtès <ludo@gnu.org> a écrit :

> Howdy,
> 
> Ludovic Courtès <ludo@gnu.org> skribis:
> 
> > This patch series adds a mechanism for channel authors to distribute
> > “news entries” alongside their channels.  The goal is to provide a
> > way for channel authors (including authors of the 'guix channel!)
> > to inform their users about important changes.
> >
> > For example, you’d tell people about package upgrades that require
> > manual intervention, about important new features, bug fixes, etc.  
> 
> [...]
> 
> > Regarding i18n, the title and body of an entry can be provided in
> > several languages.  I wonder what workflow we should adopt;
> > obviously the TP wouldn’t be appropriate, and there’s no gettext
> > involved.  
> 
> Julien, Florian: any thoughts on how we could organize to translate
> these news entries?  (See <https://issues.guix.gnu.org/issue/37413>.)
> 
> I’m thinking we could have an alias with volunteer translators who
> would provide text fragments by email to the developer who wrote the
> news?  Does that sound reasonable?
> 
> Thanks,
> Ludo’.

As long as the volume is low, I think it's reasonable, but it should be
documented.

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [bug#37413] [PATCH 0/9] Channel news distribution mechanism
  2019-09-16 21:49   ` Julien Lepiller
@ 2019-09-16 22:52     ` pelzflorian (Florian Pelz)
  2019-09-17 12:44       ` Ludovic Courtès
  0 siblings, 1 reply; 45+ messages in thread
From: pelzflorian (Florian Pelz) @ 2019-09-16 22:52 UTC (permalink / raw)
  To: Julien Lepiller; +Cc: 37413

On Mon, Sep 16, 2019 at 11:49:50PM +0200, Julien Lepiller wrote:
> Le Mon, 16 Sep 2019 23:25:55 +0200,
> Ludovic Courtès <ludo@gnu.org> a écrit :
> > I’m thinking we could have an alias with volunteer translators who
> > would provide text fragments by email to the developer who wrote the
> > news?  Does that sound reasonable?
> > 
> > Thanks,
> > Ludo’.
> 
> As long as the volume is low, I think it's reasonable, but it should be
> documented.

Yes, I want to provide such a service, but package names and
descriptions should be translated too.  I think best practice would be
if channel authors marked their news file along with the package names
and descriptions and submit those to the Translation Project instead.
I do not know if it is possible to tell xgettext (or maybe
intltool-extract) to mark an entire file for translation, maybe it is.
If not, we could provide tooling that formats a file’s contents as a
PO entry though.  The PO entry could then be prepended to the
extracted package name and description strings with the msgcat
program.  The resulting POT file should be submitted to the
Translation Project.

Regards,
Florian

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [bug#37413] [PATCH 0/9] Channel news distribution mechanism
  2019-09-16 22:52     ` pelzflorian (Florian Pelz)
@ 2019-09-17 12:44       ` Ludovic Courtès
  2019-09-17 13:33         ` pelzflorian (Florian Pelz)
  0 siblings, 1 reply; 45+ messages in thread
From: Ludovic Courtès @ 2019-09-17 12:44 UTC (permalink / raw)
  To: pelzflorian (Florian Pelz); +Cc: 37413

Hi Florian,

"pelzflorian (Florian Pelz)" <pelzflorian@pelzflorian.de> skribis:

> On Mon, Sep 16, 2019 at 11:49:50PM +0200, Julien Lepiller wrote:
>> Le Mon, 16 Sep 2019 23:25:55 +0200,
>> Ludovic Courtès <ludo@gnu.org> a écrit :
>> > I’m thinking we could have an alias with volunteer translators who
>> > would provide text fragments by email to the developer who wrote the
>> > news?  Does that sound reasonable?
>> > 
>> > Thanks,
>> > Ludo’.
>> 
>> As long as the volume is low, I think it's reasonable, but it should be
>> documented.

For Guix proper, I think the volume would be quite low (once per
month?).  Translators may have to react within a couple of days though,
basically while the change in question is under review.  It’s OK to
translate later, after the initial news has been pushed, but fewer
people will see it.

> Yes, I want to provide such a service, but package names and
> descriptions should be translated too.

<https://translationproject.org/domain/guix-packages.html> is about
translating packages and descriptions (but not package names), but I
don’t see how this relates to news entries.  What do you have in mind?

The reasons I think the TP is inadequate for news entries is that the TP
is geared towards translating releases of software packages, and those
tiny snippets could come anytime and there would be no PO file for them.

Thanks,
Ludo’.

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [bug#37413] [PATCH 0/9] Channel news distribution mechanism
  2019-09-17 12:44       ` Ludovic Courtès
@ 2019-09-17 13:33         ` pelzflorian (Florian Pelz)
  2019-09-17 13:39           ` Ludovic Courtès
  0 siblings, 1 reply; 45+ messages in thread
From: pelzflorian (Florian Pelz) @ 2019-09-17 13:33 UTC (permalink / raw)
  To: Ludovic Courtès; +Cc: 37413

On Tue, Sep 17, 2019 at 02:44:41PM +0200, Ludovic Courtès wrote:
> "pelzflorian (Florian Pelz)" <pelzflorian@pelzflorian.de> skribis:
> > Yes, I want to provide such a service, but package names and
> > descriptions should be translated too.
> 
> <https://translationproject.org/domain/guix-packages.html> is about
> translating packages and descriptions (but not package names),

Yes, I was confused. :)


> but I
> don’t see how this relates to news entries.  What do you have in mind?
>

I thought everything about translating the channel should be in a
single location, so translators can ensure the same terminology is
used in news file and package information.


> The reasons I think the TP is inadequate for news entries is that the TP
> is geared towards translating releases of software packages, and those
> tiny snippets could come anytime and there would be no PO file for them.
> 
> Thanks,
> Ludo’.

You mean it takes too long for the TP coordinator to accept new
versions of PO files?

Regards,
Florian

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [bug#37413] [PATCH 0/9] Channel news distribution mechanism
  2019-09-17 13:33         ` pelzflorian (Florian Pelz)
@ 2019-09-17 13:39           ` Ludovic Courtès
  2019-09-17 14:28             ` pelzflorian (Florian Pelz)
  0 siblings, 1 reply; 45+ messages in thread
From: Ludovic Courtès @ 2019-09-17 13:39 UTC (permalink / raw)
  To: pelzflorian (Florian Pelz); +Cc: 37413

Hi,

"pelzflorian (Florian Pelz)" <pelzflorian@pelzflorian.de> skribis:

>> but I
>> don’t see how this relates to news entries.  What do you have in mind?
>>
>
> I thought everything about translating the channel should be in a
> single location, so translators can ensure the same terminology is
> used in news file and package information.

What do you mean by “everything about translating the channel”?  You
mean instructions on how to do it?

I agree that using consistent terminology is important, but I think it’s
up to translators to do the right thing here.

>> The reasons I think the TP is inadequate for news entries is that the TP
>> is geared towards translating releases of software packages, and those
>> tiny snippets could come anytime and there would be no PO file for them.
>> 
>> Thanks,
>> Ludo’.
>
> You mean it takes too long for the TP coordinator to accept new
> versions of PO files?

I mean two things: (1) there will not be PO files (see the format of
news entries proposed in this issue), and (2) I think this is outside
the scope of the TP.

Does that clarify?

Ludo’.

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [bug#37413] [PATCH 0/9] Channel news distribution mechanism
  2019-09-17 13:39           ` Ludovic Courtès
@ 2019-09-17 14:28             ` pelzflorian (Florian Pelz)
  2019-09-17 15:27               ` Ludovic Courtès
  0 siblings, 1 reply; 45+ messages in thread
From: pelzflorian (Florian Pelz) @ 2019-09-17 14:28 UTC (permalink / raw)
  To: Ludovic Courtès; +Cc: 37413

On Tue, Sep 17, 2019 at 03:39:38PM +0200, Ludovic Courtès wrote:
> "pelzflorian (Florian Pelz)" <pelzflorian@pelzflorian.de> skribis:
> > You mean it takes too long for the TP coordinator to accept new
> > versions of PO files?
> 
> I mean two things: (1) there will not be PO files (see the format of
> news entries proposed in this issue),

It is easy to write a Guile script that prints a PO file with the news
file content inserted as the msgid of a single entry, if xgettext does
not support this already.  Then there would be two PO files, one for
the news file and one for the package info, that could be concatenated
by msgcat to a single file.  Guix could ship with this tooling for
channel authors and an explanation for how to submit the resulting POT
file to the Translation Project.

I think a single file is easier for translators to manage than two
files and typically we’d want news files and packages to be translated
by the same (group of) translator(s).


> and (2) I think this is outside
> the scope of the TP.
> 
> Does that clarify?
> 
> Ludo’.

I do not see why such translation should be outside the scope of the
TP?  I can only think of the speed of POT file acceptance and PO file
translators.  I am unsure if setting up Guix’ own translation team
would attract quicker translators than relying on the TP.

Regards,
Florian

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [bug#37413] [PATCH 0/9] Channel news distribution mechanism
  2019-09-17 14:28             ` pelzflorian (Florian Pelz)
@ 2019-09-17 15:27               ` Ludovic Courtès
  2019-09-17 17:41                 ` pelzflorian (Florian Pelz)
  0 siblings, 1 reply; 45+ messages in thread
From: Ludovic Courtès @ 2019-09-17 15:27 UTC (permalink / raw)
  To: pelzflorian (Florian Pelz); +Cc: 37413

Hello,

"pelzflorian (Florian Pelz)" <pelzflorian@pelzflorian.de> skribis:

> On Tue, Sep 17, 2019 at 03:39:38PM +0200, Ludovic Courtès wrote:
>> "pelzflorian (Florian Pelz)" <pelzflorian@pelzflorian.de> skribis:
>> > You mean it takes too long for the TP coordinator to accept new
>> > versions of PO files?
>> 
>> I mean two things: (1) there will not be PO files (see the format of
>> news entries proposed in this issue),
>
> It is easy to write a Guile script that prints a PO file with the news
> file content inserted as the msgid of a single entry, if xgettext does
> not support this already.  Then there would be two PO files, one for
> the news file and one for the package info, that could be concatenated
> by msgcat to a single file.  Guix could ship with this tooling for
> channel authors and an explanation for how to submit the resulting POT
> file to the Translation Project.
>
> I think a single file is easier for translators to manage than two
> files and typically we’d want news files and packages to be translated
> by the same (group of) translator(s).

As you’ve seen, the format I proposed does not rely on PO files and
gettext at all:

  https://issues.guix.gnu.org/issue/37413#5

Are you suggesting that it should rely on PO files?  We can discuss it,
but that would be a significant change with technical challenges
compared to what I propose.  (Also keep in mind that the news mechanism
aims to be available to third-party channels as well.)

Also, why do you mention “the package info”?  There’s no notion of a
package here, so I wonder if there’s a misunderstanding.

>> and (2) I think this is outside
>> the scope of the TP.
>> 
>> Does that clarify?
>> 
>> Ludo’.
>
> I do not see why such translation should be outside the scope of the
> TP?  I can only think of the speed of POT file acceptance and PO file
> translators.

Speed would be a problem (POT files have to be manually accepted by
Benno.)  But also, like I wrote earlier, (1) the TP is geared towards
translating releases of software packages, and (2) and those news
snippets could arrive anytime, not in sync with a “release.”

> I am unsure if setting up Guix’ own translation team would attract
> quicker translators than relying on the TP.

Yeah, having a real translation team is best, but in this case I don’t
see how that could work.

Julien mentioned some time ago that we could run our own Pootle
instance.  Maybe that could be helpful in this case.

Thank you,
Ludo’.

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [bug#37413] [PATCH 0/9] Channel news distribution mechanism
  2019-09-17 15:27               ` Ludovic Courtès
@ 2019-09-17 17:41                 ` pelzflorian (Florian Pelz)
  2019-09-17 18:21                   ` Julien Lepiller
  0 siblings, 1 reply; 45+ messages in thread
From: pelzflorian (Florian Pelz) @ 2019-09-17 17:41 UTC (permalink / raw)
  To: Ludovic Courtès; +Cc: 37413

On Tue, Sep 17, 2019 at 05:27:37PM +0200, Ludovic Courtès wrote:
> As you’ve seen, the format I proposed does not rely on PO files and
> gettext at all:
> 
>   https://issues.guix.gnu.org/issue/37413#5
>

I did not read carefully before, but even with this format, a Guile
script could turn it into a POT file with multiple entries and another
Guile script could add translations from a PO file into such a news
file.




> Are you suggesting that it should rely on PO files?


Yes.  The format need not be changed.  PO file conversion scripts can
be added later, but should be a goal.




> We can discuss it,
> but that would be a significant change with technical challenges
> compared to what I propose.
> (Also keep in mind that the news mechanism
> aims to be available to third-party channels as well.)
> 

3rd parties would not need to use the PO file conversion scripts.



> Also, why do you mention “the package info”?  There’s no notion of a
> package here, so I wonder if there’s a misunderstanding.
> 

Channels contain packages whose summaries and descriptions need
translations as well, or do I misunderstand?  Admittedly, it will take
some time until guix-packages is translated, but it should be a goal.




> >> and (2) I think this is outside
> >> the scope of the TP.
> >> 
> >> Does that clarify?
> >> 
> >> Ludo’.
> >
> > I do not see why such translation should be outside the scope of the
> > TP?  I can only think of the speed of POT file acceptance and PO file
> > translators.
> 
> Speed would be a problem (POT files have to be manually accepted by
> Benno.)  But also, like I wrote earlier, (1) the TP is geared towards
> translating releases of software packages, and (2) and those news
> snippets could arrive anytime, not in sync with a “release.”
>
> > I am unsure if setting up Guix’ own translation team would attract
> > quicker translators than relying on the TP.
> 
> Yeah, having a real translation team is best, but in this case I don’t
> see how that could work.
> 
> Julien mentioned some time ago that we could run our own Pootle
> instance.  Maybe that could be helpful in this case.
> 
> Thank you,
> Ludo’.

I do not know what Benno thinks about this.  I would prefer this to be
handled by some Translation Project eventually.

Regards,
Florian

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [bug#37413] [PATCH 0/9] Channel news distribution mechanism
  2019-09-17 17:41                 ` pelzflorian (Florian Pelz)
@ 2019-09-17 18:21                   ` Julien Lepiller
  2019-09-17 19:44                     ` pelzflorian (Florian Pelz)
  2019-09-18  9:12                     ` Ludovic Courtès
  0 siblings, 2 replies; 45+ messages in thread
From: Julien Lepiller @ 2019-09-17 18:21 UTC (permalink / raw)
  To: 37413

Le Tue, 17 Sep 2019 19:41:02 +0200,
"pelzflorian (Florian Pelz)" <pelzflorian@pelzflorian.de> a écrit :

> On Tue, Sep 17, 2019 at 05:27:37PM +0200, Ludovic Courtès wrote:
> > As you’ve seen, the format I proposed does not rely on PO files and
> > gettext at all:
> > 
> >   https://issues.guix.gnu.org/issue/37413#5
> >  
> 
> I did not read carefully before, but even with this format, a Guile
> script could turn it into a POT file with multiple entries and another
> Guile script could add translations from a PO file into such a news
> file.
> 
> 
> 
> 
> > Are you suggesting that it should rely on PO files?  
> 
> 
> Yes.  The format need not be changed.  PO file conversion scripts can
> be added later, but should be a goal.
> 
> 
> 
> 
> > We can discuss it,
> > but that would be a significant change with technical challenges
> > compared to what I propose.
> > (Also keep in mind that the news mechanism
> > aims to be available to third-party channels as well.)
> >   
> 
> 3rd parties would not need to use the PO file conversion scripts.
> 
> 
> 
> > Also, why do you mention “the package info”?  There’s no notion of a
> > package here, so I wonder if there’s a misunderstanding.
> >   
> 
> Channels contain packages whose summaries and descriptions need
> translations as well, or do I misunderstand?  Admittedly, it will take
> some time until guix-packages is translated, but it should be a goal.
> 
> 
> 
> 
> > >> and (2) I think this is outside
> > >> the scope of the TP.
> > >> 
> > >> Does that clarify?
> > >> 
> > >> Ludo’.  
> > >
> > > I do not see why such translation should be outside the scope of
> > > the TP?  I can only think of the speed of POT file acceptance and
> > > PO file translators.  
> > 
> > Speed would be a problem (POT files have to be manually accepted by
> > Benno.)  But also, like I wrote earlier, (1) the TP is geared
> > towards translating releases of software packages, and (2) and
> > those news snippets could arrive anytime, not in sync with a
> > “release.” 
> > > I am unsure if setting up Guix’ own translation team would attract
> > > quicker translators than relying on the TP.  
> > 
> > Yeah, having a real translation team is best, but in this case I
> > don’t see how that could work.
> > 
> > Julien mentioned some time ago that we could run our own Pootle
> > instance.  Maybe that could be helpful in this case.
> > 
> > Thank you,
> > Ludo’.  
> 
> I do not know what Benno thinks about this.  I would prefer this to be
> handled by some Translation Project eventually.

I partially agree: on the one hand, having our own translation platform
only increases fragmentation of the free software translation teams,
and on the other hand, it means we can have a more reactive and
customized translation process. I also agree with Ludo that the TP is
probably not the right place for news translations: even if Benno
accepts the new translations super fast (and Benno is usually fast,
I just keep making mistakes when submitting a new version), it's
going to update all translations (guix, guix-packages and guix-manual),
and translators might focus on these instead of the news.

Using a PO file for news items seem a bit drastic too: a news item is
not supposed to change, except maybe for a typo.

Channel translation is another subject, but they could provide their
own PO file and have them translated separately from the official Guix
translation process. Guix pull would then concatenate po files from
every channel and install that. Does it sound good/feasible?

> 
> Regards,
> Florian

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [bug#37413] [PATCH 0/9] Channel news distribution mechanism
  2019-09-17 18:21                   ` Julien Lepiller
@ 2019-09-17 19:44                     ` pelzflorian (Florian Pelz)
  2019-09-17 22:02                       ` pelzflorian (Florian Pelz)
  2019-09-18 10:02                       ` Ludovic Courtès
  2019-09-18  9:12                     ` Ludovic Courtès
  1 sibling, 2 replies; 45+ messages in thread
From: pelzflorian (Florian Pelz) @ 2019-09-17 19:44 UTC (permalink / raw)
  To: Julien Lepiller; +Cc: 37413

I did not want to slow down these useful patches; the news file and
its translations can remain as proposed by Ludo.  How channel authors
translate their news file is their choice.

I thought we can suggest channel authors to use one gettext PO file
containing translations for both the news file and package information
and submit it to the TP.  We can provide scripts to convert to and
from a news file.  Writing such scripts is not hard because PO files
use a simple format.  Guix itself should use this process too.


On Tue, Sep 17, 2019 at 08:21:18PM +0200, Julien Lepiller wrote:
> […]
> Channel translation is another subject, but they could provide their
> own PO file and have them translated separately from the official Guix
> translation process. Guix pull would then concatenate po files from
> every channel and install that. Does it sound good/feasible?
>

I did not think about this before, but Julien’s idea how to process a
channel’s PO files with translations for package descriptions seems
good.  Just msgcat guix-packages and the other channels’ PO files to
the PO file for guix-packages and then run msgfmt on the result.


On Tue, Sep 17, 2019 at 08:21:18PM +0200, Julien Lepiller wrote:
> I partially agree: on the one hand, having our own translation platform
> only increases fragmentation of the free software translation teams,
> and on the other hand, it means we can have a more reactive and
> customized translation process. I also agree with Ludo that the TP is
> probably not the right place for news translations: even if Benno
> accepts the new translations super fast (and Benno is usually fast,
> I just keep making mistakes when submitting a new version), it's
> going to update all translations (guix, guix-packages and guix-manual),
> and translators might focus on these instead of the news.
>

How about adding a comment to the POT file that we wish for
translators to prioritize and urgently add translations for news?  Is
it really necessary to submit the entire Guix tarball to the TP for
updating news; I thought the tarball is just for convenience of
translators?  I wonder what Benno or others from the TP think.  I
think Guix’ goals align well with those of the TP.



> Using a PO file for news items seem a bit drastic too: a news item is
> not supposed to change, except maybe for a typo.
>

PO files are simple and what the Translation Project’s translators are
used to.  I do not understand how another format could be more
appropriate.

Regards,
Florian

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [bug#37413] [PATCH 0/9] Channel news distribution mechanism
  2019-09-17 19:44                     ` pelzflorian (Florian Pelz)
@ 2019-09-17 22:02                       ` pelzflorian (Florian Pelz)
  2019-09-18 10:02                       ` Ludovic Courtès
  1 sibling, 0 replies; 45+ messages in thread
From: pelzflorian (Florian Pelz) @ 2019-09-17 22:02 UTC (permalink / raw)
  To: Julien Lepiller; +Cc: 37413

On Tue, Sep 17, 2019 at 09:44:16PM +0200, pelzflorian (Florian Pelz) wrote:
> On Tue, Sep 17, 2019 at 08:21:18PM +0200, Julien Lepiller wrote:
> > […]
> > Channel translation is another subject, but they could provide their
> > own PO file and have them translated separately from the official Guix
> > translation process. Guix pull would then concatenate po files from
> > every channel and install that. Does it sound good/feasible?
> >
> 
> I did not think about this before, but Julien’s idea how to process a
> channel’s PO files with translations for package descriptions seems
> good.  Just msgcat guix-packages and the other channels’ PO files to
> the PO file for guix-packages and then run msgfmt on the result.
> 

No, concatenation via msgcat is not feasible.  It could fail if
translations clash, e.g. when channel A translates "Hello" as
"Bonjour" and channel B translates "Hello" as "Salut".  To handle this
edge case, PO files would need to remain separate and the textdomain
procedure would need to be called by Guix before looking up package
descriptions from this channel, I think.

Regards,
Florian






> 
> On Tue, Sep 17, 2019 at 08:21:18PM +0200, Julien Lepiller wrote:
> > I partially agree: on the one hand, having our own translation platform
> > only increases fragmentation of the free software translation teams,
> > and on the other hand, it means we can have a more reactive and
> > customized translation process. I also agree with Ludo that the TP is
> > probably not the right place for news translations: even if Benno
> > accepts the new translations super fast (and Benno is usually fast,
> > I just keep making mistakes when submitting a new version), it's
> > going to update all translations (guix, guix-packages and guix-manual),
> > and translators might focus on these instead of the news.
> >
> 
> How about adding a comment to the POT file that we wish for
> translators to prioritize and urgently add translations for news?  Is
> it really necessary to submit the entire Guix tarball to the TP for
> updating news; I thought the tarball is just for convenience of
> translators?  I wonder what Benno or others from the TP think.  I
> think Guix’ goals align well with those of the TP.
> 
> 
> 
> > Using a PO file for news items seem a bit drastic too: a news item is
> > not supposed to change, except maybe for a typo.
> >
> 
> PO files are simple and what the Translation Project’s translators are
> used to.  I do not understand how another format could be more
> appropriate.
> 
> Regards,
> Florian
> 
> 
> 

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [bug#37413] [PATCH 0/9] Channel news distribution mechanism
  2019-09-17 18:21                   ` Julien Lepiller
  2019-09-17 19:44                     ` pelzflorian (Florian Pelz)
@ 2019-09-18  9:12                     ` Ludovic Courtès
  1 sibling, 0 replies; 45+ messages in thread
From: Ludovic Courtès @ 2019-09-18  9:12 UTC (permalink / raw)
  To: Julien Lepiller; +Cc: 37413

Hello,

Julien Lepiller <julien@lepiller.eu> skribis:

>> I do not know what Benno thinks about this.  I would prefer this to be
>> handled by some Translation Project eventually.

You mean by some external project focused on translation, not
necessarily by the Translation Project, right?

> I partially agree: on the one hand, having our own translation platform
> only increases fragmentation of the free software translation teams,
> and on the other hand, it means we can have a more reactive and
> customized translation process. I also agree with Ludo that the TP is
> probably not the right place for news translations: even if Benno
> accepts the new translations super fast (and Benno is usually fast,
> I just keep making mistakes when submitting a new version), it's
> going to update all translations (guix, guix-packages and guix-manual),
> and translators might focus on these instead of the news.
>
> Using a PO file for news items seem a bit drastic too: a news item is
> not supposed to change, except maybe for a typo.

Yeah.  Another issue is that you don’t want translators to spend time
translating news from last year.  News translation is very much
now-or-never.

> Channel translation is another subject, but they could provide their
> own PO file and have them translated separately from the official Guix
> translation process. Guix pull would then concatenate po files from
> every channel and install that. Does it sound good/feasible?

This patch set adds a single mechanism to distribute news alongside a
channel, be it the 'guix channel or another channel, and I think that’s
a good thing.

If we want to preserve this property, any solution that special-cases
the 'guix channel won’t work.

Also, when I wrote that switching to PO files would introduce technical
challenges, I really mean it.  :-)  For example, how would
‘.guix-channel’ specify where PO files for its news are to be found?
Would ‘.guix-channel’ also have to specify a text domain?  How would we
compile PO files to .mo/.gmo such that gettext can actually find these
things at run time?  What if a channel specifies a text domain already
used for something else?  Do we really want to tell channel authors to
fiddle with a workflow as complex as gettext + TP?  And so on.

Thanks,
Ludo’.

PS: Please trim quotes a bit when replying.  :-)

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [bug#37413] [PATCH 0/9] Channel news distribution mechanism
  2019-09-17 19:44                     ` pelzflorian (Florian Pelz)
  2019-09-17 22:02                       ` pelzflorian (Florian Pelz)
@ 2019-09-18 10:02                       ` Ludovic Courtès
  2019-09-18 11:49                         ` pelzflorian (Florian Pelz)
  1 sibling, 1 reply; 45+ messages in thread
From: Ludovic Courtès @ 2019-09-18 10:02 UTC (permalink / raw)
  To: pelzflorian (Florian Pelz); +Cc: 37413

Hello,

"pelzflorian (Florian Pelz)" <pelzflorian@pelzflorian.de> skribis:

> I did not want to slow down these useful patches; the news file and
> its translations can remain as proposed by Ludo.  How channel authors
> translate their news file is their choice.
>
> I thought we can suggest channel authors to use one gettext PO file
> containing translations for both the news file and package information
> and submit it to the TP.  We can provide scripts to convert to and
> from a news file.  Writing such scripts is not hard because PO files
> use a simple format.  Guix itself should use this process too.

Ah OK.  Having an optional add-on process to work with PO files would be
nice too have.  My concerns were about having PO files at the core of
the news mechanism.

If you’re fine with the news format as it is in this patch series, then
I guess we can go ahead and push the patches?

> How about adding a comment to the POT file that we wish for
> translators to prioritize and urgently add translations for news?  Is
> it really necessary to submit the entire Guix tarball to the TP for
> updating news; I thought the tarball is just for convenience of
> translators?  I wonder what Benno or others from the TP think.

My feeling is that this use case doesn’t correspond to what the TP is
about, but yeah, you could ask them.

Also, “urgently” won’t work because they would have to be manual
intervention by at least two people before the info reaches translators:
someone on the Guix side who’d submit a tarball (?) to the TP, and the
TP coordinator who’d notify translation teams.  That’s another reason
why I’m skeptical about the applicability of this workflow for news.

>> Using a PO file for news items seem a bit drastic too: a news item is
>> not supposed to change, except maybe for a typo.
>>
>
> PO files are simple and what the Translation Project’s translators are
> used to.  I do not understand how another format could be more
> appropriate.

It’s not so much about the format, but rather about the tooling and
workflow.  I do not see how to integrate gettext and everything that
goes with it (PO files, compilation to .gmo, text domains, etc.)
directly into this design.

But like you wrote, we can add tools on top that would allow us to use
PO files if we want to.

Thanks,
Ludo’.

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [bug#37413] [PATCH 0/9] Channel news distribution mechanism
  2019-09-18 10:02                       ` Ludovic Courtès
@ 2019-09-18 11:49                         ` pelzflorian (Florian Pelz)
  2019-09-18 12:33                           ` Ludovic Courtès
  0 siblings, 1 reply; 45+ messages in thread
From: pelzflorian (Florian Pelz) @ 2019-09-18 11:49 UTC (permalink / raw)
  To: Ludovic Courtès; +Cc: 37413

On Wed, Sep 18, 2019 at 12:02:05PM +0200, Ludovic Courtès wrote:
> Ah OK.  Having an optional add-on process to work with PO files would be
> nice too have.  My concerns were about having PO files at the core of
> the news mechanism.
> 
> If you’re fine with the news format as it is in this patch series, then
> I guess we can go ahead and push the patches?
> 

Yes.



> My feeling is that this use case doesn’t correspond to what the TP is
> about, but yeah, you could ask them.
>

I will ask them, but later after Guix website translation is online.



> Also, “urgently” won’t work because they would have to be manual
> intervention by at least two people before the info reaches translators:
> someone on the Guix side who’d submit a tarball (?) to the TP, and the

The channel author would submit the tarball.  No Guix team involved.



> TP coordinator who’d notify translation teams.  That’s another reason
> why I’m skeptical about the applicability of this workflow for news.
> […]

On Wed, Sep 18, 2019 at 11:12:31AM +0200, Ludovic Courtès wrote:
> > [pelzflorian wrote:]
> >> I do not know what Benno thinks about this.  I would prefer this to be
> >> handled by some Translation Project eventually.
> 
> You mean by some external project focused on translation, not
> necessarily by the Translation Project, right?
> 

Yes, but I still prefer the Translation Project to be where
translators gather.

Regards,
Florian

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [bug#37413] [PATCH 0/9] Channel news distribution mechanism
  2019-09-18 11:49                         ` pelzflorian (Florian Pelz)
@ 2019-09-18 12:33                           ` Ludovic Courtès
  0 siblings, 0 replies; 45+ messages in thread
From: Ludovic Courtès @ 2019-09-18 12:33 UTC (permalink / raw)
  To: pelzflorian (Florian Pelz); +Cc: 37413

"pelzflorian (Florian Pelz)" <pelzflorian@pelzflorian.de> skribis:

>> My feeling is that this use case doesn’t correspond to what the TP is
>> about, but yeah, you could ask them.
>>
>
> I will ask them, but later after Guix website translation is online.

Oops, thanks for the reminder.  :-)

>> Also, “urgently” won’t work because they would have to be manual
>> intervention by at least two people before the info reaches translators:
>> someone on the Guix side who’d submit a tarball (?) to the TP, and the
>
> The channel author would submit the tarball.  No Guix team involved.

If it’s the 'guix channel, then the Guix team is definitely involved,
and typically there’s only a couple of people in the group “entitled” to
do that (so far it’s been Julien or myself.)

Sending a whole tarball seems overkill to me; currently we don’t even
send them tarballs in between releases, even though that’d be useful.

Anyway, let’s see what the TP folks think!

Ludo’.

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [bug#37413] [PATCH v2 00/11] Channel news distribution mechanism
  2019-09-16 21:25 ` Ludovic Courtès
  2019-09-16 21:49   ` Julien Lepiller
@ 2019-09-21 21:12   ` Ludovic Courtès
  2019-09-21 21:12     ` [bug#37413] [PATCH v2 01/11] pull: '--news' shows the list of channels added or removed Ludovic Courtès
                       ` (10 more replies)
  1 sibling, 11 replies; 45+ messages in thread
From: Ludovic Courtès @ 2019-09-21 21:12 UTC (permalink / raw)
  To: 37413

Hello!

Here’s an updated version that addresses some of the issues that
were raised and some of those that weren’t even raised.

Changes compared to v1:

  • If the news format version is incompatible, silently ignore it
    instead of raising an error.

  • Language tags in the news file are now symbols instead of
    strings.  Thus, xgettext can readily be used:

       xgettext -ken -l scheme etc/news.scm

  • News entries can refer to a tag instead of a commit, as suggested
    by Ricardo.

  • ‘guix pull’ shows news titles when there are news.  (In v1 you’d
    have to explicitly run ‘guix pull --news’ to see if there are
    news.)

Thoughts?

I’ll go with that if there are no objections.

Thanks,
Ludo’.

Ludovic Courtès (11):
  pull: '--news' shows the list of channels added or removed.
  git: 'update-cached-checkout' avoids network access when unnecessary.
  git: Add 'commit-difference'.
  channels: Add support for a news file.
  channels: Allow news entries to refer to a tag.
  ui: Add 'current-message-language'.
  pull: Display channel news.
  pull: '-l' displays channel news.
  pull: Display news titles directly upon 'pull'.
  Add '.guix-channel' file.
  DRAFT etc: Add channel news file.

 .dir-locals.el        |   1 +
 .guix-channel         |   5 ++
 Makefile.am           |   8 +-
 doc/guix.texi         |  73 ++++++++++++++++-
 etc/news.scm          |  23 ++++++
 guix/channels.scm     | 145 ++++++++++++++++++++++++++++++--
 guix/git.scm          |  58 ++++++++++++-
 guix/scripts/pull.scm | 186 +++++++++++++++++++++++++++++++++++++++---
 guix/tests/git.scm    | 105 ++++++++++++++++++++++++
 guix/ui.scm           |  18 ++++
 tests/channels.scm    | 104 +++++++++++++++++++++++
 tests/git.scm         |  99 ++++++++++++++++++++++
 12 files changed, 798 insertions(+), 27 deletions(-)
 create mode 100644 .guix-channel
 create mode 100644 etc/news.scm
 create mode 100644 guix/tests/git.scm
 create mode 100644 tests/git.scm

-- 
2.23.0

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [bug#37413] [PATCH v2 01/11] pull: '--news' shows the list of channels added or removed.
  2019-09-21 21:12   ` [bug#37413] [PATCH v2 00/11] " Ludovic Courtès
@ 2019-09-21 21:12     ` Ludovic Courtès
  2019-09-21 21:12     ` [bug#37413] [PATCH v2 02/11] git: 'update-cached-checkout' avoids network access when unnecessary Ludovic Courtès
                       ` (9 subsequent siblings)
  10 siblings, 0 replies; 45+ messages in thread
From: Ludovic Courtès @ 2019-09-21 21:12 UTC (permalink / raw)
  To: 37413

* guix/scripts/pull.scm (display-channel, channel=?)
(display-channel-news, display-news): New procedures.
(process-query): Call 'display-news' instead of 'display-profile-news'.
---
 guix/scripts/pull.scm | 61 ++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 57 insertions(+), 4 deletions(-)

diff --git a/guix/scripts/pull.scm b/guix/scripts/pull.scm
index c9835cef34..4091f926ac 100644
--- a/guix/scripts/pull.scm
+++ b/guix/scripts/pull.scm
@@ -213,6 +213,62 @@ newest generation of PROFILE."
                                         (G_ "New in this revision:\n")))))
     (_ #t)))
 
+(define (display-channel channel)
+  "Display information about CHANNEL."
+  (format (current-error-port)
+          ;; TRANSLATORS: This describes a "channel"; the first placeholder is
+          ;; the channel name (e.g., "guix") and the second placeholder is its
+          ;; URL.
+          (G_ "    ~a at ~a~%")
+          (channel-name channel)
+          (channel-url channel)))
+
+(define (channel=? channel1 channel2)
+  "Return true if CHANNEL1 and CHANNEL2 are the same for all practical
+purposes."
+  ;; Assume that the URL matters less than the name.
+  (eq? (channel-name channel1) (channel-name channel2)))
+
+(define (display-channel-news profile)
+  "Display new about the channels of PROFILE "
+  (define previous
+    (and=> (relative-generation profile -1)
+           (cut generation-file-name profile <>)))
+
+  (when previous
+    (let ((old-channels (profile-channels previous))
+          (new-channels (profile-channels profile)))
+      (and (pair? old-channels) (pair? new-channels)
+           (begin
+             (match (lset-difference channel=? new-channels old-channels)
+               (()
+                #t)
+               (new
+                (let ((count (length new)))
+                  (format (current-error-port)
+                          (N_ "  ~*One new channel:~%"
+                              "  ~a new channels:~%" count)
+                          count)
+                  (for-each display-channel new))))
+             (match (lset-difference channel=? old-channels new-channels)
+               (()
+                #t)
+               (removed
+                (let ((count (length removed)))
+                  (format (current-error-port)
+                          (N_ "  ~*One channel removed:~%"
+                              "  ~a channels removed:~%" count)
+                          count)
+                  (for-each display-channel removed)))))))))
+
+(define (display-news profile)
+  ;; Display profile news, with the understanding that this process represents
+  ;; the newest generation.
+  (display-profile-news profile
+                        #:current-is-newer? #t)
+
+  (display-channel-news profile))
+
 (define* (build-and-install instances profile
                             #:key use-substitutes? verbose? dry-run?)
   "Build the tool from SOURCE, and install it in PROFILE.  When DRY-RUN? is
@@ -521,10 +577,7 @@ list of package changes.")))))
                ((numbers ...)
                 (list-generations profile numbers)))))))
     (('display-news)
-     ;; Display profile news, with the understanding that this process
-     ;; represents the newest generation.
-     (display-profile-news profile
-                           #:current-is-newer? #t))))
+     (display-news profile))))
 
 (define (process-generation-change opts profile)
   "Process a request to change the current generation (roll-back, switch, delete)."
-- 
2.23.0

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [bug#37413] [PATCH v2 02/11] git: 'update-cached-checkout' avoids network access when unnecessary.
  2019-09-21 21:12   ` [bug#37413] [PATCH v2 00/11] " Ludovic Courtès
  2019-09-21 21:12     ` [bug#37413] [PATCH v2 01/11] pull: '--news' shows the list of channels added or removed Ludovic Courtès
@ 2019-09-21 21:12     ` Ludovic Courtès
  2019-09-21 21:12     ` [bug#37413] [PATCH v2 03/11] git: Add 'commit-difference' Ludovic Courtès
                       ` (8 subsequent siblings)
  10 siblings, 0 replies; 45+ messages in thread
From: Ludovic Courtès @ 2019-09-21 21:12 UTC (permalink / raw)
  To: 37413

* guix/git.scm (reference-available?): New procedure.
(update-cached-checkout): Avoid call to 'remote-fetch' when REPOSITORY
already contains REF.
---
 guix/git.scm | 18 +++++++++++++++++-
 1 file changed, 17 insertions(+), 1 deletion(-)

diff --git a/guix/git.scm b/guix/git.scm
index de98fed40c..92a7353b5a 100644
--- a/guix/git.scm
+++ b/guix/git.scm
@@ -220,6 +220,21 @@ dynamic extent of EXP."
               (G_ "Support for submodules is missing; \
 please upgrade Guile-Git.~%"))))
 
+(define (reference-available? repository ref)
+  "Return true if REF, a reference such as '(commit . \"cabba9e\"), is
+definitely available in REPOSITORY, false otherwise."
+  (match ref
+    (('commit . commit)
+     (catch 'git-error
+       (lambda ()
+         (->bool (commit-lookup repository (string->oid commit))))
+       (lambda (key error . rest)
+         (if (= GIT_ENOTFOUND (git-error-code error))
+             #f
+             (apply throw key error rest)))))
+    (_
+     #f)))
+
 (define* (update-cached-checkout url
                                  #:key
                                  (ref '(branch . "master"))
@@ -254,7 +269,8 @@ When RECURSIVE? is true, check out submodules as well, if any."
                              (repository-open cache-directory)
                              (clone* url cache-directory))))
      ;; Only fetch remote if it has not been cloned just before.
-     (when cache-exists?
+     (when (and cache-exists?
+                (not (reference-available? repository ref)))
        (remote-fetch (remote-lookup repository "origin")))
      (when recursive?
        (update-submodules repository #:log-port log-port))
-- 
2.23.0

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [bug#37413] [PATCH v2 03/11] git: Add 'commit-difference'.
  2019-09-21 21:12   ` [bug#37413] [PATCH v2 00/11] " Ludovic Courtès
  2019-09-21 21:12     ` [bug#37413] [PATCH v2 01/11] pull: '--news' shows the list of channels added or removed Ludovic Courtès
  2019-09-21 21:12     ` [bug#37413] [PATCH v2 02/11] git: 'update-cached-checkout' avoids network access when unnecessary Ludovic Courtès
@ 2019-09-21 21:12     ` Ludovic Courtès
  2019-09-21 21:12     ` [bug#37413] [PATCH v2 04/11] channels: Add support for a news file Ludovic Courtès
                       ` (7 subsequent siblings)
  10 siblings, 0 replies; 45+ messages in thread
From: Ludovic Courtès @ 2019-09-21 21:12 UTC (permalink / raw)
  To: 37413

* guix/git.scm (commit-closure, commit-difference): New procedures.
* guix/tests/git.scm, tests/git.scm: New files.
* Makefile.am (dist_noinst_DATA): Add guix/tests/git.scm.
(SCM_TESTS): Add tests/git.scm.
---
 .dir-locals.el     |  1 +
 Makefile.am        |  6 ++-
 guix/git.scm       | 40 +++++++++++++++++++
 guix/tests/git.scm | 97 +++++++++++++++++++++++++++++++++++++++++++++
 tests/git.scm      | 99 ++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 242 insertions(+), 1 deletion(-)
 create mode 100644 guix/tests/git.scm
 create mode 100644 tests/git.scm

diff --git a/.dir-locals.el b/.dir-locals.el
index 228685a69f..22aac2c402 100644
--- a/.dir-locals.el
+++ b/.dir-locals.el
@@ -90,6 +90,7 @@
    (eval . (put 'eventually 'scheme-indent-function 1))
 
    (eval . (put 'call-with-progress-reporter 'scheme-indent-function 1))
+   (eval . (put 'with-temporary-git-repository 'scheme-indent-function 2))
 
    ;; This notably allows '(' in Paredit to not insert a space when the
    ;; preceding symbol is one of these.
diff --git a/Makefile.am b/Makefile.am
index f71ea77671..658f03bd54 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -307,7 +307,10 @@ STORE_MODULES =					\
 MODULES += $(STORE_MODULES)
 
 # Internal modules with test suite support.
-dist_noinst_DATA = guix/tests.scm guix/tests/http.scm
+dist_noinst_DATA =				\
+  guix/tests.scm				\
+  guix/tests/http.scm				\
+  guix/tests/git.scm
 
 # Auxiliary files for packages.
 AUX_FILES =						\
@@ -391,6 +394,7 @@ SCM_TESTS =					\
   tests/file-systems.scm			\
   tests/gem.scm				\
   tests/gexp.scm				\
+  tests/git.scm					\
   tests/glob.scm				\
   tests/gnu-maintenance.scm			\
   tests/grafts.scm				\
diff --git a/guix/git.scm b/guix/git.scm
index 92a7353b5a..d7dddde3a7 100644
--- a/guix/git.scm
+++ b/guix/git.scm
@@ -28,6 +28,7 @@
   #:use-module (guix utils)
   #:use-module (guix records)
   #:use-module (guix gexp)
+  #:use-module (guix sets)
   #:use-module (rnrs bytevectors)
   #:use-module (ice-9 match)
   #:use-module (srfi srfi-1)
@@ -37,8 +38,10 @@
   #:export (%repository-cache-directory
             honor-system-x509-certificates!
 
+            with-repository
             update-cached-checkout
             latest-repository-commit
+            commit-difference
 
             git-checkout
             git-checkout?
@@ -339,6 +342,43 @@ Log progress and checkout info to LOG-PORT."
 
 (set-exception-printer! 'git-error print-git-error)
 
+\f
+;;;
+;;; Commit difference.
+;;;
+
+(define (commit-closure commit)
+  "Return the closure of COMMIT as a set."
+  (let loop ((commits (list commit))
+             (visited (setq)))
+    (match commits
+      (()
+       visited)
+      ((head . tail)
+       (if (set-contains? visited head)
+           (loop tail visited)
+           (loop (append (commit-parents head) tail)
+                 (set-insert head visited)))))))
+
+(define (commit-difference new old)
+  "Return the list of commits between NEW and OLD, where OLD is assumed to be
+an ancestor of NEW.
+
+Essentially, this computes the set difference between the closure of NEW and
+that of OLD."
+  (let loop ((commits (list new))
+             (result '())
+             (visited (commit-closure old)))
+    (match commits
+      (()
+       (reverse result))
+      ((head . tail)
+       (if (set-contains? visited head)
+           (loop tail result visited)
+           (loop (append (commit-parents head) tail)
+                 (cons head result)
+                 (set-insert head visited)))))))
+
 \f
 ;;;
 ;;; Checkouts.
diff --git a/guix/tests/git.scm b/guix/tests/git.scm
new file mode 100644
index 0000000000..52abe77c83
--- /dev/null
+++ b/guix/tests/git.scm
@@ -0,0 +1,97 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2019 Ludovic Courtès <ludo@gnu.org>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix 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.
+;;;
+;;; GNU Guix 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 GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (guix tests git)
+  #:use-module (git)
+  #:use-module (guix utils)
+  #:use-module (guix build utils)
+  #:use-module (ice-9 match)
+  #:use-module (ice-9 control)
+  #:export (git-command
+            with-temporary-git-repository
+            find-commit))
+
+(define git-command
+  (make-parameter "git"))
+
+(define (populate-git-repository directory directives)
+  "Initialize a new Git checkout and repository in DIRECTORY and apply
+DIRECTIVES.  Each element of DIRECTIVES is an sexp like:
+
+  (add \"foo.txt\" \"hi!\")
+
+Return DIRECTORY on success."
+
+  ;; Note: As of version 0.2.0, Guile-Git lacks the necessary bindings to do
+  ;; all this, so resort to the "git" command.
+  (define (git command . args)
+    (apply invoke (git-command) "-C" directory
+           command args))
+
+  (mkdir-p directory)
+  (git "init")
+
+  (let loop ((directives directives))
+    (match directives
+      (()
+       directory)
+      ((('add file contents) rest ...)
+       (let ((file (string-append directory "/" file)))
+         (mkdir-p (dirname file))
+         (call-with-output-file file
+           (lambda (port)
+             (display contents port)))
+         (git "add" file)
+         (loop rest)))
+      ((('commit text) rest ...)
+       (git "commit" "-m" text)
+       (loop rest))
+      ((('branch name) rest ...)
+       (git "branch" name)
+       (loop rest))
+      ((('checkout branch) rest ...)
+       (git "checkout" branch)
+       (loop rest))
+      ((('merge branch message) rest ...)
+       (git "merge" branch "-m" message)
+       (loop rest)))))
+
+(define (call-with-temporary-git-repository directives proc)
+  (call-with-temporary-directory
+   (lambda (directory)
+     (populate-git-repository directory directives)
+     (proc directory))))
+
+(define-syntax-rule (with-temporary-git-repository directory
+                                                   directives exp ...)
+  "Evaluate EXP in a context where DIRECTORY contains a checkout populated as
+per DIRECTIVES."
+  (call-with-temporary-git-repository directives
+                                      (lambda (directory)
+                                        exp ...)))
+
+(define (find-commit repository message)
+  "Return the commit in REPOSITORY whose message includes MESSAGE, a string."
+  (let/ec return
+    (fold-commits (lambda (commit _)
+                    (and (string-contains (commit-message commit)
+                                          message)
+                         (return commit)))
+                  #f
+                  repository)
+    (error "commit not found" message)))
diff --git a/tests/git.scm b/tests/git.scm
new file mode 100644
index 0000000000..8ba10ece51
--- /dev/null
+++ b/tests/git.scm
@@ -0,0 +1,99 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2019 Ludovic Courtès <ludo@gnu.org>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix 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.
+;;;
+;;; GNU Guix 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 GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (test-git)
+  #:use-module (git)
+  #:use-module (guix git)
+  #:use-module (guix tests git)
+  #:use-module (guix build utils)
+  #:use-module (srfi srfi-1)
+  #:use-module (srfi srfi-64))
+
+;; Test the (guix git) tools.
+
+(test-begin "git")
+
+;; 'with-temporary-git-repository' relies on the 'git' command.
+(unless (which (git-command)) (test-skip 1))
+(test-assert "commit-difference, linear history"
+  (with-temporary-git-repository directory
+      '((add "a.txt" "A")
+        (commit "first commit")
+        (add "b.txt" "B")
+        (commit "second commit")
+        (add "c.txt" "C")
+        (commit "third commit")
+        (add "d.txt" "D")
+        (commit "fourth commit"))
+    (with-repository directory repository
+      (let ((commit1 (find-commit repository "first"))
+            (commit2 (find-commit repository "second"))
+            (commit3 (find-commit repository "third"))
+            (commit4 (find-commit repository "fourth")))
+        (and (lset= eq? (commit-difference commit4 commit1)
+                    (list commit2 commit3 commit4))
+             (lset= eq? (commit-difference commit4 commit2)
+                    (list commit3 commit4))
+             (equal? (commit-difference commit3 commit2)
+                     (list commit3))
+
+             ;; COMMIT4 is not an ancestor of COMMIT1 so we should get the
+             ;; empty list.
+             (null? (commit-difference commit1 commit4)))))))
+
+(unless (which (git-command)) (test-skip 1))
+(test-assert "commit-difference, fork"
+  (with-temporary-git-repository directory
+      '((add "a.txt" "A")
+        (commit "first commit")
+        (branch "devel")
+        (checkout "devel")
+        (add "devel/1.txt" "1")
+        (commit "first devel commit")
+        (add "devel/2.txt" "2")
+        (commit "second devel commit")
+        (checkout "master")
+        (add "b.txt" "B")
+        (commit "second commit")
+        (add "c.txt" "C")
+        (commit "third commit")
+        (merge "devel" "merge")
+        (add "d.txt" "D")
+        (commit "fourth commit"))
+    (with-repository directory repository
+      (let ((master1 (find-commit repository "first commit"))
+            (master2 (find-commit repository "second commit"))
+            (master3 (find-commit repository "third commit"))
+            (master4 (find-commit repository "fourth commit"))
+            (devel1  (find-commit repository "first devel"))
+            (devel2  (find-commit repository "second devel"))
+            (merge   (find-commit repository "merge")))
+        (and (equal? (commit-difference master4 merge)
+                     (list master4))
+             (lset= eq? (commit-difference master3 master1)
+                    (list master3 master2))
+             (lset= eq? (commit-difference devel2 master1)
+                    (list devel2 devel1))
+
+             ;; The merge occurred between MASTER2 and MASTER4 so here we
+             ;; expect to see all the commits from the "devel" branch in
+             ;; addition to those on "master".
+             (lset= eq? (commit-difference master4 master2)
+                    (list master4 merge master3 devel1 devel2)))))))
+
+(test-end "git")
-- 
2.23.0

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [bug#37413] [PATCH v2 04/11] channels: Add support for a news file.
  2019-09-21 21:12   ` [bug#37413] [PATCH v2 00/11] " Ludovic Courtès
                       ` (2 preceding siblings ...)
  2019-09-21 21:12     ` [bug#37413] [PATCH v2 03/11] git: Add 'commit-difference' Ludovic Courtès
@ 2019-09-21 21:12     ` Ludovic Courtès
  2019-09-21 21:12     ` [bug#37413] [PATCH v2 05/11] channels: Allow news entries to refer to a tag Ludovic Courtès
                       ` (6 subsequent siblings)
  10 siblings, 0 replies; 45+ messages in thread
From: Ludovic Courtès @ 2019-09-21 21:12 UTC (permalink / raw)
  To: 37413

* guix/channels.scm (<channel-metadata>)[news-file]: New field.
(read-channel-metadata): Set the 'news-file' field.
(read-channel-metadata-from-source): Likewise.
(<channel-news>, <channel-news-entry>): New record types.
(sexp->channel-news-entry, read-channel-news)
(channel-news-for-commit): New procedures.
* guix/tests/git.scm (populate-git-repository): For 'add', allow
CONTENTS to be a procedure.
* tests/channels.scm ("channel-news, no news")
("channel-news, one entry"): New tests.
* doc/guix.texi (Channels): Document it.
---
 doc/guix.texi      |  62 +++++++++++++++++++++++
 guix/channels.scm  | 123 ++++++++++++++++++++++++++++++++++++++++++---
 guix/tests/git.scm |   7 ++-
 tests/channels.scm |  99 ++++++++++++++++++++++++++++++++++++
 4 files changed, 282 insertions(+), 9 deletions(-)

diff --git a/doc/guix.texi b/doc/guix.texi
index af1903f6ff..712c0811a5 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -3991,6 +3991,68 @@ add a meta-data file @file{.guix-channel} that contains:
   (directory "guix"))
 @end lisp
 
+@cindex news, for channels
+@subsection Writing Channel News
+
+Channel authors may occasionally want to communicate to their users
+information about important changes in the channel.  You'd send them all
+an email, but that's not convenient.
+
+Instead, channels can provide a @dfn{news file}; when the channel users
+run @command{guix pull}, that news file is automatically read and
+@command{guix pull --news} can display the announcements that correspond
+to the new commits that have been pulled, if any.
+
+To do that, channel authors must first declare the name of the news file
+in their @file{.guix-channel} file:
+
+@lisp
+(channel
+  (version 0)
+  (news-file "etc/news.txt"))
+@end lisp
+
+The news file itself, @file{etc/news.txt} in this example, must look
+something like this:
+
+@lisp
+(channel-news
+  (version 0)
+  (entry (commit "d894ab8e9bfabcefa6c49d9ba2e834dd5a73a300")
+         (title (en "Fixed terrible bug")
+                (fr "Oh la la"))
+         (body (en "@@emph@{Good news@}!  It's fixed!")
+               (eo "Certe ĝi pli bone funkcias nun!")))
+  (entry (commit "bdcabe815cd28144a2d2b4bc3c5057b051fa9906")
+         (title (en "Added a great package")
+                (ca "Què vol dir guix?"))
+         (body (en "Don't miss the @@code@{hello@} package!"))))
+@end lisp
+
+The file consists of a list of @dfn{news entries}.  Each entry is
+associated with a commit: it describes changes made in this commit,
+possibly in preceding commits as well.  Users see entries only the first
+time they obtain the commit the entry refers to.
+
+The @code{title} field should be a one-line summary while @code{body}
+can be arbitrary long, and both can contain Texinfo markup
+(@pxref{Overview,,, texinfo, GNU Texinfo}).  Both the title and body are
+a list of language tag/message tuples, which allows @command{guix pull}
+to display news in the language that corresponds to the user's locale.
+
+If you want to translate news using a gettext-based workflow, you can
+extract translatable strings with @command{xgettext} (@pxref{xgettext
+Invocation,,, gettext, GNU Gettext Utilities}).  For example, assuming
+you write news entries in English first, the command below creates a PO
+file containing the strings to translate:
+
+@example
+xgettext -o news.po -l scheme -ken etc/news.scm
+@end example
+
+To sum up, yes, you could use your channel as a blog.  But beware, this
+is @emph{not quite} what your users might expect.
+
 @subsection Replicating Guix
 
 @cindex pinning, channels
diff --git a/guix/channels.scm b/guix/channels.scm
index ebb2cacbc7..0dadba616f 100644
--- a/guix/channels.scm
+++ b/guix/channels.scm
@@ -19,6 +19,7 @@
 ;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
 
 (define-module (guix channels)
+  #:use-module (git)
   #:use-module (guix git)
   #:use-module (guix records)
   #:use-module (guix gexp)
@@ -29,6 +30,7 @@
   #:use-module (guix derivations)
   #:use-module (guix combinators)
   #:use-module (guix diagnostics)
+  #:use-module (guix sets)
   #:use-module (guix store)
   #:use-module (guix i18n)
   #:use-module ((guix utils)
@@ -67,7 +69,14 @@
             %channel-profile-hooks
             channel-instances->derivation
 
-            profile-channels))
+            profile-channels
+
+            channel-news-entry?
+            channel-news-entry-commit
+            channel-news-entry-title
+            channel-news-entry-body
+
+            channel-news-for-commit))
 
 ;;; Commentary:
 ;;;
@@ -110,10 +119,11 @@
   (checkout  channel-instance-checkout))
 
 (define-record-type <channel-metadata>
-  (channel-metadata directory dependencies)
+  (channel-metadata directory dependencies news-file)
   channel-metadata?
   (directory     channel-metadata-directory)      ;string with leading slash
-  (dependencies  channel-metadata-dependencies))  ;list of <channel>
+  (dependencies  channel-metadata-dependencies)   ;list of <channel>
+  (news-file     channel-metadata-news-file))     ;string | #f
 
 (define (channel-reference channel)
   "Return the \"reference\" for CHANNEL, an sexp suitable for
@@ -129,12 +139,13 @@ if valid metadata could not be read from PORT."
   (match (read port)
     (('channel ('version 0) properties ...)
      (let ((directory    (and=> (assoc-ref properties 'directory) first))
-           (dependencies (or (assoc-ref properties 'dependencies) '())))
+           (dependencies (or (assoc-ref properties 'dependencies) '()))
+           (news-file    (and=> (assoc-ref properties 'news-file) first)))
        (channel-metadata
-        (cond ((not directory) "/")
+        (cond ((not directory) "/")               ;directory
               ((string-prefix? "/" directory) directory)
               (else (string-append "/" directory)))
-        (map (lambda (item)
+        (map (lambda (item)                       ;dependencies
                (let ((get (lambda* (key #:optional default)
                             (or (and=> (assoc-ref item key) first) default))))
                  (and-let* ((name (get 'name))
@@ -145,7 +156,8 @@ if valid metadata could not be read from PORT."
                     (branch branch)
                     (url url)
                     (commit (get 'commit))))))
-             dependencies))))
+             dependencies)
+        news-file)))                              ;news-file
     ((and ('channel ('version version) _ ...) sexp)
      (raise (condition
              (&message (message "unsupported '.guix-channel' version"))
@@ -169,7 +181,7 @@ doesn't exist."
         read-channel-metadata))
     (lambda args
       (if (= ENOENT (system-error-errno args))
-          (channel-metadata "/" '())
+          (channel-metadata "/" '() #f)
           (apply throw args)))))
 
 (define (channel-instance-metadata instance)
@@ -560,3 +572,98 @@ PROFILE is not a profile created by 'guix pull', return the empty list."
               ;; Show most recently installed packages last.
               (reverse
                (manifest-entries (profile-manifest profile)))))
+
+\f
+;;;
+;;; News.
+;;;
+
+;; Channel news.
+(define-record-type <channel-news>
+  (channel-news entries)
+  channel-news?
+  (entries channel-news-entries))                 ;list of <channel-news-entry>
+
+;; News entry, associated with a specific commit of the channel.
+(define-record-type <channel-news-entry>
+  (channel-news-entry commit title body)
+  channel-news-entry?
+  (commit  channel-news-entry-commit)             ;hex string
+  (title   channel-news-entry-title)              ;list of language tag/string pairs
+  (body    channel-news-entry-body))              ;list of language tag/string pairs
+
+(define (sexp->channel-news-entry entry)
+  "Return the <channel-news-entry> record corresponding to ENTRY, an sexp."
+  (define (pair language message)
+    (cons (symbol->string language) message))
+
+  (match entry
+    (('entry ('commit commit)
+             ('title ((? symbol? title-tags) (? string? titles)) ...)
+             ('body ((? symbol? body-tags) (? string? bodies)) ...)
+             _ ...)
+     (channel-news-entry commit
+                         (map pair title-tags titles)
+                         (map pair body-tags bodies)))
+    (_
+     (raise (condition
+             (&message (message "invalid channel news entry"))
+             (&error-location
+              (location (source-properties->location
+                         (source-properties entry)))))))))
+
+(define (read-channel-news port)
+  "Read a channel news feed from PORT and return it as a <channel-news>
+record."
+  (match (false-if-exception (read port))
+    (('channel-news ('version 0) entries ...)
+     (channel-news (map sexp->channel-news-entry entries)))
+    (('channel-news ('version version) _ ...)
+     ;; This is an unsupported version from the future.  There's nothing wrong
+     ;; with that (the user may simply need to upgrade the 'guix' channel to
+     ;; be able to read it), so silently ignore it.
+     (channel-news '()))
+    (#f
+     (raise (condition
+             (&message (message "syntactically invalid channel news file")))))
+    (sexp
+     (raise (condition
+             (&message (message "invalid channel news file"))
+             (&error-location
+              (location (source-properties->location
+                         (source-properties sexp)))))))))
+
+(define* (channel-news-for-commit channel new #:optional old)
+  "Return a list of <channel-news-entry> for CHANNEL between commits OLD and
+NEW.  When OLD is omitted or is #f, return all the news entries of CHANNEL."
+  (catch 'git-error
+    (lambda ()
+      (let* ((checkout  (update-cached-checkout (channel-url channel)
+                                                #:ref `(commit . ,new)))
+             (metadata  (read-channel-metadata-from-source checkout))
+             (news-file (channel-metadata-news-file metadata))
+             (news-file (and news-file
+                             (string-append checkout "/" news-file))))
+        (if (and news-file (file-exists? news-file))
+            (let ((entries (channel-news-entries (call-with-input-file news-file
+                                                   read-channel-news))))
+              (if old
+                  (with-repository checkout repository
+                    (let* ((new     (commit-lookup repository (string->oid new)))
+                           (old     (commit-lookup repository (string->oid old)))
+                           (commits (list->set
+                                     (map (compose oid->string commit-id)
+                                          (commit-difference new old)))))
+                      (filter (lambda (entry)
+                                (set-contains? commits
+                                               (channel-news-entry-commit entry)))
+                              entries)))
+                  entries))
+            '())))
+    (lambda (key error . rest)
+      ;; If commit NEW or commit OLD cannot be found, then something must be
+      ;; wrong (for example, the history of CHANNEL was rewritten and these
+      ;; commits no longer exist upstream), so quietly return the empty list.
+      (if (= GIT_ENOTFOUND (git-error-code error))
+          '()
+          (apply throw key error rest)))))
diff --git a/guix/tests/git.scm b/guix/tests/git.scm
index 52abe77c83..9d5b1ae321 100644
--- a/guix/tests/git.scm
+++ b/guix/tests/git.scm
@@ -18,6 +18,7 @@
 
 (define-module (guix tests git)
   #:use-module (git)
+  #:use-module ((guix git) #:select (with-repository))
   #:use-module (guix utils)
   #:use-module (guix build utils)
   #:use-module (ice-9 match)
@@ -55,7 +56,11 @@ Return DIRECTORY on success."
          (mkdir-p (dirname file))
          (call-with-output-file file
            (lambda (port)
-             (display contents port)))
+             (display (if (string? contents)
+                          contents
+                          (with-repository directory repository
+                            (contents repository)))
+                      port)))
          (git "add" file)
          (loop rest)))
       ((('commit text) rest ...)
diff --git a/tests/channels.scm b/tests/channels.scm
index e83b5437d3..58101bcb72 100644
--- a/tests/channels.scm
+++ b/tests/channels.scm
@@ -28,6 +28,10 @@
   #:use-module (guix gexp)
   #:use-module ((guix utils)
                 #:select (error-location? error-location location-line))
+  #:use-module ((guix build utils) #:select (which))
+  #:use-module (git)
+  #:use-module (guix git)
+  #:use-module (guix tests git)
   #:use-module (srfi srfi-1)
   #:use-module (srfi srfi-26)
   #:use-module (srfi srfi-34)
@@ -246,4 +250,99 @@
                (depends? drv3
                          (list drv2 drv0) (list))))))))
 
+(unless (which (git-command)) (test-skip 1))
+(test-equal "channel-news, no news"
+  '()
+  (with-temporary-git-repository directory
+      '((add "a.txt" "A")
+        (commit "the commit"))
+    (with-repository directory repository
+      (let ((channel (channel (url (string-append "file://" directory))
+                              (name 'foo)))
+            (latest  (reference-name->oid repository "HEAD")))
+        (channel-news-for-commit channel (oid->string latest))))))
+
+(unless (which (git-command)) (test-skip 1))
+(test-assert "channel-news, one entry"
+  (with-temporary-git-repository directory
+      `((add ".guix-channel"
+             ,(object->string
+               '(channel (version 0)
+                         (news-file "news.scm"))))
+        (commit "first commit")
+        (add "src/a.txt" "A")
+        (commit "second commit")
+        (add "news.scm"
+             ,(lambda (repository)
+                (let ((previous
+                       (reference-name->oid repository "HEAD")))
+                  (object->string
+                   `(channel-news
+                     (version 0)
+                     (entry (commit ,(oid->string previous))
+                            (title (en "New file!")
+                                   (eo "Nova dosiero!"))
+                            (body (en "Yeah, a.txt."))))))))
+        (commit "third commit")
+        (add "src/b.txt" "B")
+        (commit "fourth commit")
+        (add "news.scm"
+             ,(lambda (repository)
+                (let ((second
+                       (commit-id
+                        (find-commit repository "second commit")))
+                      (previous
+                       (reference-name->oid repository "HEAD")))
+                  (object->string
+                   `(channel-news
+                     (version 0)
+                     (entry (commit ,(oid->string previous))
+                            (title (en "Another file!"))
+                            (body (en "Yeah, b.txt.")))
+                     (entry (commit ,(oid->string second))
+                            (title (en "Old news.")
+                                   (eo "Malnovaĵoj."))
+                            (body (en "For a.txt"))))))))
+        (commit "fifth commit"))
+    (with-repository directory repository
+      (define (find-commit* message)
+        (oid->string (commit-id (find-commit repository message))))
+
+      (let ((channel (channel (url (string-append "file://" directory))
+                              (name 'foo)))
+            (commit1 (find-commit* "first commit"))
+            (commit2 (find-commit* "second commit"))
+            (commit3 (find-commit* "third commit"))
+            (commit4 (find-commit* "fourth commit"))
+            (commit5 (find-commit* "fifth commit")))
+        ;; First try fetching all the news up to a given commit.
+        (and (null? (channel-news-for-commit channel commit2))
+             (lset= string=?
+                    (map channel-news-entry-commit
+                         (channel-news-for-commit channel commit5))
+                    (list commit2 commit4))
+             (lset= equal?
+                    (map channel-news-entry-title
+                         (channel-news-for-commit channel commit5))
+                    '((("en" . "Another file!"))
+                      (("en" . "Old news.") ("eo" . "Malnovaĵoj."))))
+             (lset= string=?
+                    (map channel-news-entry-commit
+                         (channel-news-for-commit channel commit3))
+                    (list commit2))
+
+             ;; Now fetch news entries that apply to a commit range.
+             (lset= string=?
+                    (map channel-news-entry-commit
+                         (channel-news-for-commit channel commit3 commit1))
+                    (list commit2))
+             (lset= string=?
+                    (map channel-news-entry-commit
+                         (channel-news-for-commit channel commit5 commit3))
+                    (list commit4))
+             (lset= string=?
+                    (map channel-news-entry-commit
+                         (channel-news-for-commit channel commit5 commit1))
+                    (list commit4 commit2)))))))
+
 (test-end "channels")
-- 
2.23.0

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [bug#37413] [PATCH v2 05/11] channels: Allow news entries to refer to a tag.
  2019-09-21 21:12   ` [bug#37413] [PATCH v2 00/11] " Ludovic Courtès
                       ` (3 preceding siblings ...)
  2019-09-21 21:12     ` [bug#37413] [PATCH v2 04/11] channels: Add support for a news file Ludovic Courtès
@ 2019-09-21 21:12     ` Ludovic Courtès
  2019-09-21 21:12     ` [bug#37413] [PATCH v2 06/11] ui: Add 'current-message-language' Ludovic Courtès
                       ` (5 subsequent siblings)
  10 siblings, 0 replies; 45+ messages in thread
From: Ludovic Courtès @ 2019-09-21 21:12 UTC (permalink / raw)
  To: 37413

Suggested by Ricardo Wurmus <rekado@elephly.net>.

* guix/channels.scm (<channel-news-entry>)[tag]: New field.
(sexp->channel-news-entry): Accept either 'commit' or 'tag' in 'entry'
forms.
(resolve-channel-news-entry-tag): New procedure.
(channel-news-for-commit): Move 'with-repository' form one level
higher.  Call 'resolve-channel-news-entry-tag' on all the news entries.
* guix/tests/git.scm (populate-git-repository): Add clause for 'tag'.
* tests/channels.scm ("channel-news, one entry"): Create a tag and add
an entry with a tag.  Check that the tag is resolved and also visible in
the <channel-news-entry> record.
* doc/guix.texi (Channels): Mention tags in news entries.
---
 doc/guix.texi      |  8 ++++----
 guix/channels.scm  | 42 ++++++++++++++++++++++++++++++++----------
 guix/tests/git.scm |  3 +++
 tests/channels.scm |  9 +++++++--
 4 files changed, 46 insertions(+), 16 deletions(-)

diff --git a/doc/guix.texi b/doc/guix.texi
index 712c0811a5..5addb1f5ee 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -4018,7 +4018,7 @@ something like this:
 @lisp
 (channel-news
   (version 0)
-  (entry (commit "d894ab8e9bfabcefa6c49d9ba2e834dd5a73a300")
+  (entry (tag "the-bug-fix")
          (title (en "Fixed terrible bug")
                 (fr "Oh la la"))
          (body (en "@@emph@{Good news@}!  It's fixed!")
@@ -4030,9 +4030,9 @@ something like this:
 @end lisp
 
 The file consists of a list of @dfn{news entries}.  Each entry is
-associated with a commit: it describes changes made in this commit,
-possibly in preceding commits as well.  Users see entries only the first
-time they obtain the commit the entry refers to.
+associated with a commit or tag: it describes changes made in this
+commit, possibly in preceding commits as well.  Users see entries only
+the first time they obtain the commit the entry refers to.
 
 The @code{title} field should be a one-line summary while @code{body}
 can be arbitrary long, and both can contain Texinfo markup
diff --git a/guix/channels.scm b/guix/channels.scm
index 0dadba616f..4e6e7090ac 100644
--- a/guix/channels.scm
+++ b/guix/channels.scm
@@ -40,6 +40,7 @@
   #:use-module (srfi srfi-2)
   #:use-module (srfi srfi-9)
   #:use-module (srfi srfi-11)
+  #:use-module (srfi srfi-26)
   #:use-module (srfi srfi-34)
   #:use-module (srfi srfi-35)
   #:autoload   (guix self) (whole-package make-config.scm)
@@ -73,6 +74,7 @@
 
             channel-news-entry?
             channel-news-entry-commit
+            channel-news-entry-tag
             channel-news-entry-title
             channel-news-entry-body
 
@@ -586,9 +588,10 @@ PROFILE is not a profile created by 'guix pull', return the empty list."
 
 ;; News entry, associated with a specific commit of the channel.
 (define-record-type <channel-news-entry>
-  (channel-news-entry commit title body)
+  (channel-news-entry commit tag title body)
   channel-news-entry?
-  (commit  channel-news-entry-commit)             ;hex string
+  (commit  channel-news-entry-commit)             ;hex string | #f
+  (tag     channel-news-entry-tag)                ;#f | string
   (title   channel-news-entry-title)              ;list of language tag/string pairs
   (body    channel-news-entry-body))              ;list of language tag/string pairs
 
@@ -598,11 +601,12 @@ PROFILE is not a profile created by 'guix pull', return the empty list."
     (cons (symbol->string language) message))
 
   (match entry
-    (('entry ('commit commit)
+    (('entry ((and (or 'commit 'tag) type) commit-or-tag)
              ('title ((? symbol? title-tags) (? string? titles)) ...)
              ('body ((? symbol? body-tags) (? string? bodies)) ...)
              _ ...)
-     (channel-news-entry commit
+     (channel-news-entry (and (eq? type 'commit) commit-or-tag)
+                         (and (eq? type 'tag) commit-or-tag)
                          (map pair title-tags titles)
                          (map pair body-tags bodies)))
     (_
@@ -633,6 +637,20 @@ record."
               (location (source-properties->location
                          (source-properties sexp)))))))))
 
+(define (resolve-channel-news-entry-tag repository entry)
+  "If ENTRY has its 'commit' field set, return ENTRY.  Otherwise, lookup
+ENTRY's 'tag' in REPOSITORY and return ENTRY with its 'commit' field set to
+the field its 'tag' refers to.  A 'git-error' exception is raised if the tag
+cannot be found."
+  (if (channel-news-entry-commit entry)
+      entry
+      (let* ((tag       (channel-news-entry-tag entry))
+             (reference (string-append "refs/tags/" tag))
+             (oid       (reference-name->oid repository reference)))
+        (channel-news-entry (oid->string oid) tag
+                            (channel-news-entry-title entry)
+                            (channel-news-entry-body entry)))))
+
 (define* (channel-news-for-commit channel new #:optional old)
   "Return a list of <channel-news-entry> for CHANNEL between commits OLD and
 NEW.  When OLD is omitted or is #f, return all the news entries of CHANNEL."
@@ -645,10 +663,14 @@ NEW.  When OLD is omitted or is #f, return all the news entries of CHANNEL."
              (news-file (and news-file
                              (string-append checkout "/" news-file))))
         (if (and news-file (file-exists? news-file))
-            (let ((entries (channel-news-entries (call-with-input-file news-file
-                                                   read-channel-news))))
-              (if old
-                  (with-repository checkout repository
+            (with-repository checkout repository
+              (let* ((news    (call-with-input-file news-file
+                                read-channel-news))
+                     (entries (map (lambda (entry)
+                                     (resolve-channel-news-entry-tag repository
+                                                                     entry))
+                                   (channel-news-entries news))))
+                (if old
                     (let* ((new     (commit-lookup repository (string->oid new)))
                            (old     (commit-lookup repository (string->oid old)))
                            (commits (list->set
@@ -657,8 +679,8 @@ NEW.  When OLD is omitted or is #f, return all the news entries of CHANNEL."
                       (filter (lambda (entry)
                                 (set-contains? commits
                                                (channel-news-entry-commit entry)))
-                              entries)))
-                  entries))
+                              entries))
+                    entries)))
             '())))
     (lambda (key error . rest)
       ;; If commit NEW or commit OLD cannot be found, then something must be
diff --git a/guix/tests/git.scm b/guix/tests/git.scm
index 9d5b1ae321..21573ac14e 100644
--- a/guix/tests/git.scm
+++ b/guix/tests/git.scm
@@ -66,6 +66,9 @@ Return DIRECTORY on success."
       ((('commit text) rest ...)
        (git "commit" "-m" text)
        (loop rest))
+      ((('tag name) rest ...)
+       (git "tag" name)
+       (loop rest))
       ((('branch name) rest ...)
        (git "branch" name)
        (loop rest))
diff --git a/tests/channels.scm b/tests/channels.scm
index 58101bcb72..f5a7955483 100644
--- a/tests/channels.scm
+++ b/tests/channels.scm
@@ -272,6 +272,7 @@
         (commit "first commit")
         (add "src/a.txt" "A")
         (commit "second commit")
+        (tag "tag-for-first-news-entry")
         (add "news.scm"
              ,(lambda (repository)
                 (let ((previous
@@ -299,7 +300,7 @@
                      (entry (commit ,(oid->string previous))
                             (title (en "Another file!"))
                             (body (en "Yeah, b.txt.")))
-                     (entry (commit ,(oid->string second))
+                     (entry (tag "tag-for-first-news-entry")
                             (title (en "Old news.")
                                    (eo "Malnovaĵoj."))
                             (body (en "For a.txt"))))))))
@@ -343,6 +344,10 @@
              (lset= string=?
                     (map channel-news-entry-commit
                          (channel-news-for-commit channel commit5 commit1))
-                    (list commit4 commit2)))))))
+                    (list commit4 commit2))
+             (lset= equal?
+                    (map channel-news-entry-tag
+                         (channel-news-for-commit channel commit5 commit1))
+                    '(#f "tag-for-first-news-entry")))))))
 
 (test-end "channels")
-- 
2.23.0

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [bug#37413] [PATCH v2 06/11] ui: Add 'current-message-language'.
  2019-09-21 21:12   ` [bug#37413] [PATCH v2 00/11] " Ludovic Courtès
                       ` (4 preceding siblings ...)
  2019-09-21 21:12     ` [bug#37413] [PATCH v2 05/11] channels: Allow news entries to refer to a tag Ludovic Courtès
@ 2019-09-21 21:12     ` Ludovic Courtès
  2019-09-21 21:12     ` [bug#37413] [PATCH v2 07/11] pull: Display channel news Ludovic Courtès
                       ` (4 subsequent siblings)
  10 siblings, 0 replies; 45+ messages in thread
From: Ludovic Courtès @ 2019-09-21 21:12 UTC (permalink / raw)
  To: 37413

* guix/ui.scm (%default-message-language): New variable.
(current-message-language): New procedure.
---
 guix/ui.scm | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/guix/ui.scm b/guix/ui.scm
index 4be31db047..069d542131 100644
--- a/guix/ui.scm
+++ b/guix/ui.scm
@@ -121,6 +121,10 @@
             roll-back*
             switch-to-generation*
             delete-generation*
+
+            %default-message-language
+            current-message-language
+
             run-guix-command
             run-guix
             guix-main))
@@ -428,6 +432,20 @@ exiting.  ARGS is the list of arguments received by the 'throw' handler."
 report them in a user-friendly way."
   (call-with-unbound-variable-handling (lambda () exp ...)))
 
+(define %default-message-language
+  ;; Default language to use for messages.
+  (make-parameter "en"))
+
+(define (current-message-language)
+  "Return the language used for messages according to the current locale.
+Return %DEFAULT-MESSAGE-LANGUAGE if that information could not be obtained.  The
+result is an ISO-639-2 language code such as \"ar\", without the territory
+part."
+  (let ((locale (setlocale LC_MESSAGES)))
+    (match (string-index locale #\_)
+      (#f    locale)
+      (index (string-take locale index)))))
+
 (define (install-locale)
   "Install the current locale settings."
   (catch 'system-error
-- 
2.23.0

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [bug#37413] [PATCH v2 07/11] pull: Display channel news.
  2019-09-21 21:12   ` [bug#37413] [PATCH v2 00/11] " Ludovic Courtès
                       ` (5 preceding siblings ...)
  2019-09-21 21:12     ` [bug#37413] [PATCH v2 06/11] ui: Add 'current-message-language' Ludovic Courtès
@ 2019-09-21 21:12     ` Ludovic Courtès
  2019-09-21 21:12     ` [bug#37413] [PATCH v2 08/11] pull: '-l' displays " Ludovic Courtès
                       ` (3 subsequent siblings)
  10 siblings, 0 replies; 45+ messages in thread
From: Ludovic Courtès @ 2019-09-21 21:12 UTC (permalink / raw)
  To: 37413

* guix/scripts/pull.scm (display-news-entry)
(display-channel-specific-news): New procedures.
(display-channel-news): Call it.
(display-new/upgraded-packages): Adjust hint message.
* doc/guix.texi (Invoking guix pull): Mention it.
---
 doc/guix.texi         | 11 +++++---
 guix/scripts/pull.scm | 61 ++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 65 insertions(+), 7 deletions(-)

diff --git a/doc/guix.texi b/doc/guix.texi
index 5addb1f5ee..50adce4dd3 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -3720,13 +3720,16 @@ Read the list of channels from @var{file} instead of
 evaluates to a list of channel objects.  @xref{Channels}, for more
 information.
 
+@cindex channel news
 @item --news
 @itemx -N
-Display the list of packages added or upgraded since the previous generation.
+Display the list of packages added or upgraded since the previous
+generation, as well as, occasionally, news written by channel authors
+for their users (@pxref{Channels, Writing Channel News}).
 
-This is the same information as displayed upon @command{guix pull} completion,
-but without ellipses; it is also similar to the output of @command{guix pull
--l} for the last generation (see below).
+The package information is the same as displayed upon @command{guix
+pull} completion, but without ellipses; it is also similar to the output
+of @command{guix pull -l} for the last generation (see below).
 
 @item --list-generations[=@var{pattern}]
 @itemx -l [@var{pattern}]
diff --git a/guix/scripts/pull.scm b/guix/scripts/pull.scm
index 4091f926ac..85291c3745 100644
--- a/guix/scripts/pull.scm
+++ b/guix/scripts/pull.scm
@@ -19,6 +19,7 @@
 
 (define-module (guix scripts pull)
   #:use-module (guix ui)
+  #:use-module (guix colors)
   #:use-module (guix utils)
   #:use-module ((guix status) #:select (with-status-verbosity))
   #:use-module (guix scripts)
@@ -229,6 +230,48 @@ purposes."
   ;; Assume that the URL matters less than the name.
   (eq? (channel-name channel1) (channel-name channel2)))
 
+(define (display-news-entry entry language port)
+  "Display ENTRY, a <channel-news-entry>, in LANGUAGE, a language code, to
+PORT."
+  (let ((title (channel-news-entry-title entry))
+        (body  (channel-news-entry-body entry)))
+    (format port "  ~a~%"
+            (highlight
+             (string-trim-right
+              (texi->plain-text (or (assoc-ref title language)
+                                    (assoc-ref title (%default-message-language))
+                                    "")))))
+    (format port (G_ "    commit ~a~%")
+            (channel-news-entry-commit entry))
+    (newline port)
+    (format port "    ~a~%"
+            (indented-string
+             (parameterize ((%text-width (- (%text-width) 4)))
+               (string-trim-right
+                (texi->plain-text (or (assoc-ref body language)
+                                      (assoc-ref body (%default-message-language))
+                                      ""))))
+             4))))
+
+(define* (display-channel-specific-news new old
+                                        #:key (port (current-output-port)))
+  "Display channel news applicable the commits between OLD and NEW, where OLD
+and NEW are <channel> records with a proper 'commit' field."
+  (let ((channel new)
+        (old     (channel-commit old))
+        (new     (channel-commit new)))
+    (when (and old new)
+      (let ((language (current-message-language)))
+        (match (channel-news-for-commit channel new old)
+          (()                                     ;no news is good news
+           #t)
+          ((entries ...)
+           (newline port)
+           (format port (G_ "News for channel '~a'~%")
+                   (channel-name channel))
+           (for-each (cut display-news-entry <> language port) entries)
+           (newline port)))))))
+
 (define (display-channel-news profile)
   "Display new about the channels of PROFILE "
   (define previous
@@ -259,7 +302,20 @@ purposes."
                           (N_ "  ~*One channel removed:~%"
                               "  ~a channels removed:~%" count)
                           count)
-                  (for-each display-channel removed)))))))))
+                  (for-each display-channel removed))))
+
+             ;; Display channel-specific news for those channels that were
+             ;; here before and are still around afterwards.
+             (for-each (match-lambda
+                         ((new old)
+                          (display-channel-specific-news new old)))
+                       (filter-map (lambda (new)
+                                     (define old
+                                       (find (cut channel=? new <>)
+                                             old-channels))
+
+                                     (and old (list new old)))
+                                   new-channels)))))))
 
 (define (display-news profile)
   ;; Display profile news, with the understanding that this process represents
@@ -534,8 +590,7 @@ display long package lists that would fill the user's screen."
     (when (and concise?
                (or (> new-count concise/max-item-count)
                    (> upgraded-count concise/max-item-count)))
-      (display-hint (G_ "Run @command{guix pull --news} to view the complete
-list of package changes.")))))
+      (display-hint (G_ "Run @command{guix pull --news} to read all the news.")))))
 
 (define (display-profile-content-diff profile gen1 gen2)
   "Display the changes in PROFILE GEN2 compared to generation GEN1."
-- 
2.23.0

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [bug#37413] [PATCH v2 08/11] pull: '-l' displays channel news.
  2019-09-21 21:12   ` [bug#37413] [PATCH v2 00/11] " Ludovic Courtès
                       ` (6 preceding siblings ...)
  2019-09-21 21:12     ` [bug#37413] [PATCH v2 07/11] pull: Display channel news Ludovic Courtès
@ 2019-09-21 21:12     ` Ludovic Courtès
  2019-09-21 21:12     ` [bug#37413] [PATCH v2 09/11] pull: Display news titles directly upon 'pull' Ludovic Courtès
                       ` (2 subsequent siblings)
  10 siblings, 0 replies; 45+ messages in thread
From: Ludovic Courtès @ 2019-09-21 21:12 UTC (permalink / raw)
  To: 37413

* guix/scripts/pull.scm (display-channel-news): Make 'previous' a
parameter.
(process-query)[list-generations]: Call 'display-channel-news'.
---
 guix/scripts/pull.scm | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/guix/scripts/pull.scm b/guix/scripts/pull.scm
index 85291c3745..4a4756dc6e 100644
--- a/guix/scripts/pull.scm
+++ b/guix/scripts/pull.scm
@@ -272,12 +272,12 @@ and NEW are <channel> records with a proper 'commit' field."
            (for-each (cut display-news-entry <> language port) entries)
            (newline port)))))))
 
-(define (display-channel-news profile)
-  "Display new about the channels of PROFILE "
-  (define previous
-    (and=> (relative-generation profile -1)
-           (cut generation-file-name profile <>)))
-
+(define* (display-channel-news profile
+                               #:optional
+                               (previous
+                                (and=> (relative-generation profile -1)
+                                       (cut generation-file-name profile <>))))
+  "Display news about the channels of PROFILE compared to PREVIOUS."
   (when previous
     (let ((old-channels (profile-channels previous))
           (new-channels (profile-channels profile)))
@@ -614,6 +614,8 @@ display long package lists that would fill the user's screen."
               ((first second rest ...)
                (display-profile-content-diff profile
                                              first second)
+               (display-channel-news (generation-file-name profile second)
+                                     (generation-file-name profile first))
                (loop (cons second rest)))
               ((_) #t)
               (()  #t))))))
-- 
2.23.0

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [bug#37413] [PATCH v2 09/11] pull: Display news titles directly upon 'pull'.
  2019-09-21 21:12   ` [bug#37413] [PATCH v2 00/11] " Ludovic Courtès
                       ` (7 preceding siblings ...)
  2019-09-21 21:12     ` [bug#37413] [PATCH v2 08/11] pull: '-l' displays " Ludovic Courtès
@ 2019-09-21 21:12     ` Ludovic Courtès
  2019-09-21 21:12     ` [bug#37413] [PATCH v2 10/11] Add '.guix-channel' file Ludovic Courtès
  2019-09-21 21:12     ` [bug#37413] [PATCH v2 11/11] DRAFT etc: Add channel news file Ludovic Courtès
  10 siblings, 0 replies; 45+ messages in thread
From: Ludovic Courtès @ 2019-09-21 21:12 UTC (permalink / raw)
  To: 37413

* guix/scripts/pull.scm (display-profile-news): Return true when there's
more to display.
(display-news-entry-title): New procedure.
(display-news-entry): Use it.
(display-channel-specific-news): Return true when there's more to
display.
(display-channel-news-headlines): New procedure.
(build-and-install): Call it.  When 'display-channel-news-headlines' or
'display-profile-news' returns #t, print a hint to run "pull --news".
(display-new/upgraded-packages): Return true when there's more to display.
---
 guix/scripts/pull.scm | 112 ++++++++++++++++++++++++++++++------------
 1 file changed, 81 insertions(+), 31 deletions(-)

diff --git a/guix/scripts/pull.scm b/guix/scripts/pull.scm
index 4a4756dc6e..a7fd36fffc 100644
--- a/guix/scripts/pull.scm
+++ b/guix/scripts/pull.scm
@@ -189,7 +189,7 @@ Download and deploy the latest version of Guix.\n"))
                                current-is-newer?)
   "Display what's up in PROFILE--new packages, and all that.  If
 CURRENT-IS-NEWER? is true, assume that the current process represents the
-newest generation of PROFILE."
+newest generation of PROFILE.  Return true when there's more info to display."
   (match (memv (generation-number profile)
                (reverse (profile-generations profile)))
     ((current previous _ ...)
@@ -212,7 +212,7 @@ newest generation of PROFILE."
                                         #:concise? concise?
                                         #:heading
                                         (G_ "New in this revision:\n")))))
-    (_ #t)))
+    (_ #f)))
 
 (define (display-channel channel)
   "Display information about CHANNEL."
@@ -230,33 +230,44 @@ purposes."
   ;; Assume that the URL matters less than the name.
   (eq? (channel-name channel1) (channel-name channel2)))
 
+(define (display-news-entry-title entry language port)
+  "Display the title of ENTRY, a news entry, to PORT."
+  (define title
+    (channel-news-entry-title entry))
+
+  (format port "  ~a~%"
+          (highlight
+           (string-trim-right
+            (texi->plain-text (or (assoc-ref title language)
+                                  (assoc-ref title (%default-message-language))
+                                  ""))))))
+
 (define (display-news-entry entry language port)
   "Display ENTRY, a <channel-news-entry>, in LANGUAGE, a language code, to
 PORT."
-  (let ((title (channel-news-entry-title entry))
-        (body  (channel-news-entry-body entry)))
-    (format port "  ~a~%"
-            (highlight
+  (define body
+    (channel-news-entry-body entry))
+
+  (display-news-entry-title entry language port)
+  (format port (G_ "    commit ~a~%")
+          (channel-news-entry-commit entry))
+  (newline port)
+  (format port "    ~a~%"
+          (indented-string
+           (parameterize ((%text-width (- (%text-width) 4)))
              (string-trim-right
-              (texi->plain-text (or (assoc-ref title language)
-                                    (assoc-ref title (%default-message-language))
-                                    "")))))
-    (format port (G_ "    commit ~a~%")
-            (channel-news-entry-commit entry))
-    (newline port)
-    (format port "    ~a~%"
-            (indented-string
-             (parameterize ((%text-width (- (%text-width) 4)))
-               (string-trim-right
-                (texi->plain-text (or (assoc-ref body language)
-                                      (assoc-ref body (%default-message-language))
-                                      ""))))
-             4))))
+              (texi->plain-text (or (assoc-ref body language)
+                                    (assoc-ref body (%default-message-language))
+                                    ""))))
+           4)))
 
 (define* (display-channel-specific-news new old
-                                        #:key (port (current-output-port)))
+                                        #:key (port (current-output-port))
+                                        concise?)
   "Display channel news applicable the commits between OLD and NEW, where OLD
-and NEW are <channel> records with a proper 'commit' field."
+and NEW are <channel> records with a proper 'commit' field.  When CONCISE? is
+true, display nothing but the news titles.  Return true if there are more news
+to display."
   (let ((channel new)
         (old     (channel-commit old))
         (new     (channel-commit new)))
@@ -264,13 +275,17 @@ and NEW are <channel> records with a proper 'commit' field."
       (let ((language (current-message-language)))
         (match (channel-news-for-commit channel new old)
           (()                                     ;no news is good news
-           #t)
+           #f)
           ((entries ...)
            (newline port)
            (format port (G_ "News for channel '~a'~%")
                    (channel-name channel))
-           (for-each (cut display-news-entry <> language port) entries)
-           (newline port)))))))
+           (for-each (if concise?
+                         (cut display-news-entry-title <> language port)
+                         (cut display-news-entry <> language port))
+                     entries)
+           (newline port)
+           #t))))))
 
 (define* (display-channel-news profile
                                #:optional
@@ -317,6 +332,35 @@ and NEW are <channel> records with a proper 'commit' field."
                                      (and old (list new old)))
                                    new-channels)))))))
 
+(define* (display-channel-news-headlines profile)
+  "Display the titles of news about the channels of PROFILE compared to its
+previous generation.  Return true if there are news to display."
+  (define previous
+    (and=> (relative-generation profile -1)
+           (cut generation-file-name profile <>)))
+
+  (when previous
+    (let ((old-channels (profile-channels previous))
+          (new-channels (profile-channels profile)))
+      ;; Find the channels present in both PROFILE and PREVIOUS, and print
+      ;; their news.
+      (and (pair? old-channels) (pair? new-channels)
+           (let ((channels (filter-map (lambda (new)
+                                         (define old
+                                           (find (cut channel=? new <>)
+                                                 old-channels))
+
+                                         (and old (list new old)))
+                                       new-channels)))
+             (define more?
+               (map (match-lambda
+                      ((new old)
+                       (display-channel-specific-news new old
+                                                      #:concise? #t)))
+                    channels))
+
+             (any ->bool more?))))))
+
 (define (display-news profile)
   ;; Display profile news, with the understanding that this process represents
   ;; the newest generation.
@@ -344,7 +388,12 @@ true, display what would be built without actually building it."
                       #:dry-run? dry-run?)
       (munless dry-run?
         (return (newline))
-        (return (display-profile-news profile #:concise? #t))
+        (return
+         (let ((more? (list (display-profile-news profile #:concise? #t)
+                            (display-channel-news-headlines profile))))
+           (when (any ->bool more?)
+             (display-hint
+              (G_ "Run @command{guix pull --news} to read all the news.")))))
         (if guix-command
             (let ((new (map (cut string-append <> "/bin/guix")
                             (list (user-friendly-profile profile)
@@ -544,7 +593,9 @@ it."
   "Given the two package name/version alists ALIST1 and ALIST2, display the
 list of new and upgraded packages going from ALIST1 to ALIST2.  When ALIST1
 and ALIST2 differ, display HEADING upfront.  When CONCISE? is true, do not
-display long package lists that would fill the user's screen."
+display long package lists that would fill the user's screen.
+
+Return true when there is more package info to display."
   (define (pretty str column)
     (indented-string (fill-paragraph str (- (%text-width) 4)
                                      column)
@@ -587,10 +638,9 @@ display long package lists that would fill the user's screen."
                (pretty (list->enumeration (sort upgraded string<?))
                        35))))
 
-    (when (and concise?
-               (or (> new-count concise/max-item-count)
-                   (> upgraded-count concise/max-item-count)))
-      (display-hint (G_ "Run @command{guix pull --news} to read all the news.")))))
+    (and concise?
+         (or (> new-count concise/max-item-count)
+             (> upgraded-count concise/max-item-count)))))
 
 (define (display-profile-content-diff profile gen1 gen2)
   "Display the changes in PROFILE GEN2 compared to generation GEN1."
-- 
2.23.0

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [bug#37413] [PATCH v2 10/11] Add '.guix-channel' file.
  2019-09-21 21:12   ` [bug#37413] [PATCH v2 00/11] " Ludovic Courtès
                       ` (8 preceding siblings ...)
  2019-09-21 21:12     ` [bug#37413] [PATCH v2 09/11] pull: Display news titles directly upon 'pull' Ludovic Courtès
@ 2019-09-21 21:12     ` Ludovic Courtès
  2019-09-21 21:12     ` [bug#37413] [PATCH v2 11/11] DRAFT etc: Add channel news file Ludovic Courtès
  10 siblings, 0 replies; 45+ messages in thread
From: Ludovic Courtès @ 2019-09-21 21:12 UTC (permalink / raw)
  To: 37413

* .guix-channel: New file.
* Makefile.am (EXTRA_DIST): Add it.
---
 .guix-channel | 5 +++++
 Makefile.am   | 1 +
 2 files changed, 6 insertions(+)
 create mode 100644 .guix-channel

diff --git a/.guix-channel b/.guix-channel
new file mode 100644
index 0000000000..3e618d79f8
--- /dev/null
+++ b/.guix-channel
@@ -0,0 +1,5 @@
+;; This is a Guix channel.
+
+(channel
+  (version 0)
+  (news-file "etc/news.scm"))
diff --git a/Makefile.am b/Makefile.am
index 658f03bd54..3c27d8ee10 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -533,6 +533,7 @@ EXTRA_DIST +=						\
   TODO							\
   CODE-OF-CONDUCT					\
   .dir-locals.el					\
+  .guix-channel						\
   scripts/guix.in					\
   etc/guix-install.sh					\
   build-aux/build-self.scm				\
-- 
2.23.0

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [bug#37413] [PATCH v2 11/11] DRAFT etc: Add channel news file.
  2019-09-21 21:12   ` [bug#37413] [PATCH v2 00/11] " Ludovic Courtès
                       ` (9 preceding siblings ...)
  2019-09-21 21:12     ` [bug#37413] [PATCH v2 10/11] Add '.guix-channel' file Ludovic Courtès
@ 2019-09-21 21:12     ` Ludovic Courtès
  2019-09-22 11:14       ` pelzflorian (Florian Pelz)
  10 siblings, 1 reply; 45+ messages in thread
From: Ludovic Courtès @ 2019-09-21 21:12 UTC (permalink / raw)
  To: 37413

DRAFT: Update commit ID before pushing.

* etc/news.scm: New file.
* Makefile.am (EXTRA_DIST): Add it.
---
 Makefile.am  |  1 +
 etc/news.scm | 23 +++++++++++++++++++++++
 2 files changed, 24 insertions(+)
 create mode 100644 etc/news.scm

diff --git a/Makefile.am b/Makefile.am
index 3c27d8ee10..6a4cfcd4a1 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -536,6 +536,7 @@ EXTRA_DIST +=						\
   .guix-channel						\
   scripts/guix.in					\
   etc/guix-install.sh					\
+  etc/news.scm						\
   build-aux/build-self.scm				\
   build-aux/compile-all.scm				\
   build-aux/hydra/evaluate.scm				\
diff --git a/etc/news.scm b/etc/news.scm
new file mode 100644
index 0000000000..05241b36e7
--- /dev/null
+++ b/etc/news.scm
@@ -0,0 +1,23 @@
+;; GNU Guix news, for use by 'guix pull'.
+;;
+;; Copyright © 2019 Ludovic Courtès <ludo@gnu.org>
+;;
+;; Copying and distribution of this file, with or without modification, are
+;; permitted in any medium without royalty provided the copyright notice and
+;; this notice are preserved.
+
+(channel-news
+ (version 0)
+ (entry (commit "d17f14c167791b640e49b2b6443d20f9534c62d7")
+        (title (en "New channel news mechanism")
+               (fr "Nouveau mécanisme d'information sur les canaux"))
+        (body
+         (en "You are reading this message through the new channel news
+mechanism, congratulations!  This mechanism allows channel authors to provide
+@dfn{news entries} that their users can view with @command{guix pull --news}.
+Run @command{info \"(guix) Invoking guix pull\"} for more info.")
+         (fr "Ce message t'arrive à travers le nouveau mécanisme d'information
+des canaux, bravo !  Ce mécanisme permet aux auteur·rice·s de canaux de
+fournir des informations qu'on peut visualiser avec @command{guix pull
+--news}.  Tape @command{info \"(guix.fr) Invoquer guix pull\"} pour plus de
+détails."))))
-- 
2.23.0

^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [bug#37413] [PATCH v2 11/11] DRAFT etc: Add channel news file.
  2019-09-21 21:12     ` [bug#37413] [PATCH v2 11/11] DRAFT etc: Add channel news file Ludovic Courtès
@ 2019-09-22 11:14       ` pelzflorian (Florian Pelz)
  2019-09-23  9:13         ` Ludovic Courtès
  0 siblings, 1 reply; 45+ messages in thread
From: pelzflorian (Florian Pelz) @ 2019-09-22 11:14 UTC (permalink / raw)
  To: Ludovic Courtès; +Cc: 37413

I can only make comments about the text. :D

Patch 1:

On Sat, Sep 21, 2019 at 11:12:18PM +0200, Ludovic Courtès wrote:
> +(define (display-channel-news profile)
> +  "Display new about the channels of PROFILE "

You fixed it in patch 8 anyway, so you can ignore it: Docstring should
be "Display news…" with s.

Patch 4:

On Sat, Sep 21, 2019 at 11:12:21PM +0200, Ludovic Courtès wrote:
> +The @code{title} field should be a one-line summary while @code{body}
> +can be arbitrary long, and both can contain Texinfo markup
> +(@pxref{Overview,,, texinfo, GNU Texinfo}).  Both the title and body are
> +a list of language tag/message tuples, which allows @command{guix pull}
> +to display news in the language that corresponds to the user's locale.

“arbitrarily” with -ly.

Patch 11:

On Sat, Sep 21, 2019 at 11:12:28PM +0200, Ludovic Courtès wrote:
> +(channel-news
> + (version 0)
> + (entry (commit "d17f14c167791b640e49b2b6443d20f9534c62d7")
> +        (title (en "New channel news mechanism")
> +               (fr "Nouveau mécanisme d'information sur les canaux"))

German would be:

(de "Neuer Mechanismus, um Neuigkeiten über Kanäle anzuzeigen.")



> +        (body
> +         (en "You are reading this message through the new channel news
> +mechanism, congratulations!  This mechanism allows channel authors to provide
> +@dfn{news entries} that their users can view with @command{guix pull --news}.
> +Run @command{info \"(guix) Invoking guix pull\"} for more info.")
> +         (fr "Ce message t'arrive à travers le nouveau mécanisme d'information
> +des canaux, bravo !  Ce mécanisme permet aux auteur·rice·s de canaux de
> +fournir des informations qu'on peut visualiser avec @command{guix pull
> +--news}.  Tape @command{info \"(guix.fr) Invoquer guix pull\"} pour plus de
> +détails."))))

German would be:

(de "Sie lesen diese Meldung mit Hilfe des neuen Mechanismus, um
Neuigkeiten über Kanäle anzuzeigen — Glückwunsch! Mit diesem
Mechanismus können Kanalautoren Ihren Nutzern @dfn{Einträge zu
Neuigkeiten} mitteilen, die diese sich mit @command{guix pull --news}
anzeigen lassen können. Führen Sie @command{info \"(guix.de) Aufruf
von guix pull\"} aus, um weitere Informationen zu erhalten.")

(Of course the German manual does not actually contain translated
information yet, but whatever.  It would be nice if the Translation
Project would accept new PO files without marking PO entries as fuzzy
that changed since the old version at the TP.)

Regards,
Florian

^ permalink raw reply	[flat|nested] 45+ messages in thread

* [bug#37413] [PATCH v2 11/11] DRAFT etc: Add channel news file.
  2019-09-22 11:14       ` pelzflorian (Florian Pelz)
@ 2019-09-23  9:13         ` Ludovic Courtès
  0 siblings, 0 replies; 45+ messages in thread
From: Ludovic Courtès @ 2019-09-23  9:13 UTC (permalink / raw)
  To: pelzflorian (Florian Pelz); +Cc: 37413, guix-i18n

Hi Florian,

Thanks for these comments!  I’ve fixed the issues you reported and push
the result as 90ca791ab082f0513cd5e8af7acfd8db63a6e73a.

So hopefully, next time you pull, ‘guix pull --news’ will show you the
very first news.

I’ve also created the guix-i18n@gnu.org alias (Cc’d), where I added
yourself, Julien, Miguel, Meiyo, and znavko (see
<https://issues.guix.gnu.org/issue/37413> for context.)

This alias should be low-traffic (one translation request per month
maybe?), but please let me know if you do not want to be there.

Florian, note that I’m still open to a PO-based translation workflow.  I
prefer to leave it to you translators to see whether/how this could be
set up, as discussed earlier.

Thanks,
Ludo’.

^ permalink raw reply	[flat|nested] 45+ messages in thread

end of thread, other threads:[~2019-09-23  9:14 UTC | newest]

Thread overview: 45+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2019-09-15 22:10 [bug#37413] [PATCH 0/9] Channel news distribution mechanism Ludovic Courtès
2019-09-15 22:20 ` [bug#37413] [PATCH 1/9] pull: '--news' shows the list of channels added or removed Ludovic Courtès
2019-09-15 22:20   ` [bug#37413] [PATCH 2/9] git: 'update-cached-checkout' avoids network access when unnecessary Ludovic Courtès
2019-09-15 22:21   ` [bug#37413] [PATCH 3/9] git: Add 'commit-difference' Ludovic Courtès
2019-09-15 22:21   ` [bug#37413] [PATCH 4/9] channels: Add support for a news file Ludovic Courtès
2019-09-15 22:21   ` [bug#37413] [PATCH 5/9] ui: Add 'current-message-language' Ludovic Courtès
2019-09-15 22:21   ` [bug#37413] [PATCH 6/9] pull: Display channel news Ludovic Courtès
2019-09-15 22:21   ` [bug#37413] [PATCH 7/9] pull: '-l' displays " Ludovic Courtès
2019-09-15 22:21   ` [bug#37413] [PATCH 8/9] Add '.guix-channel' file Ludovic Courtès
2019-09-15 22:21   ` [bug#37413] [PATCH 9/9] DRAFT etc: Add channel news file Ludovic Courtès
2019-09-16  9:31 ` [bug#37413] [PATCH 0/9] Channel news distribution mechanism Ricardo Wurmus
2019-09-16 12:59   ` Ludovic Courtès
2019-09-16 13:16     ` Ricardo Wurmus
2019-09-16 15:10       ` Ludovic Courtès
2019-09-16 17:16         ` Ricardo Wurmus
2019-09-16 21:25 ` Ludovic Courtès
2019-09-16 21:49   ` Julien Lepiller
2019-09-16 22:52     ` pelzflorian (Florian Pelz)
2019-09-17 12:44       ` Ludovic Courtès
2019-09-17 13:33         ` pelzflorian (Florian Pelz)
2019-09-17 13:39           ` Ludovic Courtès
2019-09-17 14:28             ` pelzflorian (Florian Pelz)
2019-09-17 15:27               ` Ludovic Courtès
2019-09-17 17:41                 ` pelzflorian (Florian Pelz)
2019-09-17 18:21                   ` Julien Lepiller
2019-09-17 19:44                     ` pelzflorian (Florian Pelz)
2019-09-17 22:02                       ` pelzflorian (Florian Pelz)
2019-09-18 10:02                       ` Ludovic Courtès
2019-09-18 11:49                         ` pelzflorian (Florian Pelz)
2019-09-18 12:33                           ` Ludovic Courtès
2019-09-18  9:12                     ` Ludovic Courtès
2019-09-21 21:12   ` [bug#37413] [PATCH v2 00/11] " Ludovic Courtès
2019-09-21 21:12     ` [bug#37413] [PATCH v2 01/11] pull: '--news' shows the list of channels added or removed Ludovic Courtès
2019-09-21 21:12     ` [bug#37413] [PATCH v2 02/11] git: 'update-cached-checkout' avoids network access when unnecessary Ludovic Courtès
2019-09-21 21:12     ` [bug#37413] [PATCH v2 03/11] git: Add 'commit-difference' Ludovic Courtès
2019-09-21 21:12     ` [bug#37413] [PATCH v2 04/11] channels: Add support for a news file Ludovic Courtès
2019-09-21 21:12     ` [bug#37413] [PATCH v2 05/11] channels: Allow news entries to refer to a tag Ludovic Courtès
2019-09-21 21:12     ` [bug#37413] [PATCH v2 06/11] ui: Add 'current-message-language' Ludovic Courtès
2019-09-21 21:12     ` [bug#37413] [PATCH v2 07/11] pull: Display channel news Ludovic Courtès
2019-09-21 21:12     ` [bug#37413] [PATCH v2 08/11] pull: '-l' displays " Ludovic Courtès
2019-09-21 21:12     ` [bug#37413] [PATCH v2 09/11] pull: Display news titles directly upon 'pull' Ludovic Courtès
2019-09-21 21:12     ` [bug#37413] [PATCH v2 10/11] Add '.guix-channel' file Ludovic Courtès
2019-09-21 21:12     ` [bug#37413] [PATCH v2 11/11] DRAFT etc: Add channel news file Ludovic Courtès
2019-09-22 11:14       ` pelzflorian (Florian Pelz)
2019-09-23  9:13         ` Ludovic Courtès

Code repositories for project(s) associated with this external index

	https://git.savannah.gnu.org/cgit/guix.git

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.