summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLudovic Courtès <ludo@gnu.org>2026-04-23 16:48:30 +0200
committerLudovic Courtès <ludo@gnu.org>2026-05-10 17:01:08 +0200
commitf13901f6cbd663019090963664ace72345165461 (patch)
tree294a2ea6f49725a6ac105bb99604d62d6d3dc0d4
parent4b81a4df19bc659915e156fed9b12fb9319b11fb (diff)
pull, time-machine: Accept content SWHIDs as arguments to ‘--channels’.
* guix/swh.scm (swhid-content-data): New procedure. (call): Do not close ‘port’ when ‘result’ is the same as ‘port’. * guix/scripts/pull.scm (swhid-content-data*): New procedure. (channel-list): Accept ‘file’ as a SWHID. * tests/guix-time-machine.sh: Add test. * doc/guix.texi (Invoking guix pull): Document it. (Invoking guix time-machine): Likewise. Change-Id: I8145cd8685fe2926b1548d4a2dcd54804d89228a Signed-off-by: Ludovic Courtès <ludo@gnu.org>
-rw-r--r--doc/guix.texi52
-rw-r--r--guix/scripts/pull.scm19
-rw-r--r--guix/swh.scm15
-rw-r--r--tests/guix-time-machine.sh16
4 files changed, 84 insertions, 18 deletions
diff --git a/doc/guix.texi b/doc/guix.texi
index 04cf049e62..a8af7659b9 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -4780,15 +4780,15 @@ These options are provided for convenience, but you can also specify your
configuration in the @file{~/.config/guix/channels.scm} file or using the
@option{--channels} option (see below).
-@item --channels=@var{file-or-url}
-@itemx -C @var{file-or-url}
-Read the list of channels from @var{file-or-url} instead of
+@item --channels=@var{file-or-uri}
+@itemx -C @var{file-or-uri}
+Read the list of channels from @var{file-or-uri} instead of
@file{~/.config/guix/channels.scm} or @file{/etc/guix/channels.scm}.
-@var{file-or-url} must contain Scheme code that
+@var{file-or-uri} must contain Scheme code that
evaluates to a list of channel objects. @xref{Channels}, for more
information.
-As you can guess, @var{file-or-url} can be a URL, in which case the
+As you can guess, @var{file-or-uri} can be a URL, in which case the
given channels file is transparently downloaded. In the example below,
we pull from the latest revision of Guix that was successfully
evaluated:
@@ -4798,10 +4798,24 @@ guix pull \
-C https://ci.guix.gnu.org/eval/latest/channels.scm?spec=master
@end example
+@cindex SWHID, for channel files
+Alternatively, you can refer to a channel file by its
+@uref{https://swhid.org,Software Hash Identifier}, or SWHID, which
+uniquely and unambiguously identifies contents, as in this example:
+
+@example
+guix pull \
+ -C swh:1:cnt:ae02d8ba3538a385ee799e61cdd0dfc5e14a8d1b
+@end example
+
+This command downloads the data designated by the given identifier from
+@uref{https://archive.softwareheritage.org, the Software Heritage
+archive}.
+
@anchor{trusted-channels}
@cindex trusted channels, for downloaded channel files
-This command errors out if the returned channels are not among the
-user's @dfn{trusted channels}, defined as:
+In both cases, the command errors out if the returned channels are not
+among the user's @dfn{trusted channels}, defined as:
@itemize
@item
@@ -5089,13 +5103,13 @@ Use the @code{guix} channel from the specified @var{url}, at the
given @var{commit} (a valid Git commit ID represented as a hexadecimal
string or the name of a tag), or @var{branch}.
-@item --channels=@var{file-or-url}
-@itemx -C @var{file-or-url}
-Read the list of channels from @var{file-or-url}. @var{file-or-url}
+@item --channels=@var{file-or-uri}
+@itemx -C @var{file-or-uri}
+Read the list of channels from @var{file-or-uri}. @var{file-or-uri}
must contain Scheme code that evaluates to a list of channel objects.
@xref{Channels} for more information.
-When @var{file-or-url} is a URL, channels are transparently downloaded.
+When @var{file-or-uri} is a URL, channels are transparently downloaded.
In the example below, we pull from the latest revision of Guix that was
successfully evaluated:
@@ -5105,10 +5119,24 @@ guix time-machine \
-- describe
@end example
+@cindex SWHID, for channel files
Note that downloading a channel file potentially harms reproducibility:
the file might vanish or be modified on the server behind your back.
From a reproducibility viewpoint the safe approach is to keep channel
-files under version control.
+files under version control, or to refer to them by their
+@uref{https://swhid.org,Software Hash Identifier}, or SWHID, which
+uniquely and unambiguously identifies contents. Here is an example:
+
+@example
+guix time-machine \
+ -C swh:1:cnt:ae02d8ba3538a385ee799e61cdd0dfc5e14a8d1b \
+ -- @dots{}
+@end example
+
+This command downloads the data designated by the identifier from
+@uref{https://archive.softwareheritage.org, the Software Heritage
+archive}; it is fully reproducible because the data it refers to is
+immutable.
@xref{Invoking guix pull}, for more information.
diff --git a/guix/scripts/pull.scm b/guix/scripts/pull.scm
index fa04702587..8474f28ea6 100644
--- a/guix/scripts/pull.scm
+++ b/guix/scripts/pull.scm
@@ -33,6 +33,7 @@
#:use-module (guix memoization)
#:use-module (guix monads)
#:use-module (guix channels)
+ #:autoload (guix swh) (swhid-content-data)
#:autoload (guix inferior) (open-inferior
inferior-available-packages
close-inferior)
@@ -792,6 +793,13 @@ authenticated---i.e., have a @code{introduction} field---may be listed in that
file."))
(exit EXIT_FAILURE)))
+(define (swhid-content-data* swhid)
+ "Like 'swhid-content-data' but error out of SWHID is not a valid content
+identifier."
+ (match (swhid-content-data swhid)
+ (#f (leave (G_ "~a: invalid content SWHID~%") swhid))
+ (port port)))
+
(define (channel-list opts)
"Return the list of channels to use. If OPTS specify a channel file,
channels are read from there; otherwise, if ~/.config/guix/channels.scm
@@ -819,9 +827,11 @@ transformations specified in OPTS (resulting from '--url', '--commit', or
(define (load-channels file)
(let* ((url? (or (string-prefix? "https://" file)
(string-prefix? "http://" file)))
- (result (load* (if url?
- (http-fetch file #:timeout 10)
- file)
+ (swhid? (string-prefix? "swh:" file))
+ (result (load* (cond
+ (url? (http-fetch file #:timeout 10))
+ (swhid? (swhid-content-data* file))
+ (else file))
%safe-channel-bindings
#:isolated? isolated?)))
(if (and (list? result) (every channel? result))
@@ -829,7 +839,8 @@ transformations specified in OPTS (resulting from '--url', '--commit', or
;; When downloading channels, keep going if and only if these are
;; channels the user trusts.
(check-trusted-channels result
- (if (and url? require-trusted-channels?)
+ (if (and (or url? swhid?)
+ require-trusted-channels?)
'error
'warning))
result)
diff --git a/guix/swh.scm b/guix/swh.scm
index 038895904a..0999314707 100644
--- a/guix/swh.scm
+++ b/guix/swh.scm
@@ -92,6 +92,7 @@
content-data-url
content-length
lookup-content
+ swhid-content-data
directory-entry?
directory-entry-name
@@ -285,7 +286,8 @@ FALSE-IF-404? is true, return #f upon 404 responses."
(cond ((= 200 (response-code response))
(let ((result (decode port)))
- (close-port port)
+ (unless (eq? result port)
+ (close-port port))
result))
((and false-if-404?
(= 404 (response-code response)))
@@ -448,6 +450,17 @@ FALSE-IF-404? is true, return #f upon 404 responses."
(bytevector->base16-string hash)))
json->content)
+(define (swhid-content-data swhid)
+ "If SWHID is a content identifier (starting with \"swh:1:cnt:\"), return an
+input port from which its data can be read. Otherwise return #f."
+ (define prefix
+ "swh:1:cnt:")
+ (and (string-prefix? prefix swhid)
+ (call (swh-url "/api/1/content"
+ (string-append "sha1_git:" (string-drop swhid (string-length prefix)))
+ "raw")
+ identity))) ;return the port as is
+
(define-query (lookup-revision id)
"Return the revision with the given ID, typically a Git commit SHA1."
(path "/api/1/revision" id)
diff --git a/tests/guix-time-machine.sh b/tests/guix-time-machine.sh
index 35b186ad6d..262e3d5fef 100644
--- a/tests/guix-time-machine.sh
+++ b/tests/guix-time-machine.sh
@@ -22,7 +22,8 @@
#
channels_file="channels-test-$$.scm"
-trap "rm -f $channels_file" EXIT
+log_file="log-test-$$.log"
+trap "rm -f $channels_file $log_file" EXIT
cat > "$channels_file" <<EOF
(system "echo rm -rf /")
@@ -73,6 +74,19 @@ cat > "$channels_file" <<EOF
EOF
guix repl -- "$channels_file"
+if guile -c '(getaddrinfo "www.gnu.org" "80" AI_NUMERICSERV)' 2> /dev/null
+then
+ # Ignore the user's ~/.config/guix/trusted-channels.scm. Under
+ # ./pre-inst-env zero channels are trusted by default so the following
+ # command must fail.
+ XDG_CONFIG_HOME="$XDG_CACHE_HOME/dot-config" \
+ guix time-machine \
+ -C swh:1:cnt:003e1e0c1b9b358082201332c926ae54e9549002 \
+ 2> "$log_file" \
+ && false
+ grep "'guix' is not trusted" "$log_file"
+fi
+
if [ -d "$abs_top_srcdir/.git" ] \
|| guile -c '(getaddrinfo "www.gnu.org" "80" AI_NUMERICSERV)' 2> /dev/null
then