diff options
| author | Ludovic Courtès <ludo@gnu.org> | 2026-04-23 16:48:30 +0200 |
|---|---|---|
| committer | Ludovic Courtès <ludo@gnu.org> | 2026-05-10 17:01:08 +0200 |
| commit | f13901f6cbd663019090963664ace72345165461 (patch) | |
| tree | 294a2ea6f49725a6ac105bb99604d62d6d3dc0d4 | |
| parent | 4b81a4df19bc659915e156fed9b12fb9319b11fb (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.texi | 52 | ||||
| -rw-r--r-- | guix/scripts/pull.scm | 19 | ||||
| -rw-r--r-- | guix/swh.scm | 15 | ||||
| -rw-r--r-- | tests/guix-time-machine.sh | 16 |
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 |
