summaryrefslogtreecommitdiff
path: root/guix/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'guix/scripts')
-rw-r--r--guix/scripts/describe.scm56
-rw-r--r--guix/scripts/git.scm63
-rw-r--r--guix/scripts/git/authenticate.scm179
-rw-r--r--guix/scripts/graph.scm1
-rw-r--r--guix/scripts/pack.scm29
-rw-r--r--guix/scripts/processes.scm11
-rw-r--r--guix/scripts/pull.scm18
-rw-r--r--guix/scripts/system.scm67
-rw-r--r--guix/scripts/system/reconfigure.scm97
-rw-r--r--guix/scripts/system/search.scm1
-rw-r--r--guix/scripts/time-machine.scm4
-rw-r--r--guix/scripts/weather.scm2
12 files changed, 455 insertions, 73 deletions
diff --git a/guix/scripts/describe.scm b/guix/scripts/describe.scm
index ea982955da..bc868ffbbf 100644
--- a/guix/scripts/describe.scm
+++ b/guix/scripts/describe.scm
@@ -26,9 +26,11 @@
#:use-module (guix scripts)
#:use-module (guix describe)
#:use-module (guix profiles)
+ #:autoload (guix openpgp) (openpgp-format-fingerprint)
#:use-module (git)
#:use-module (json)
#:use-module (srfi srfi-1)
+ #:use-module (srfi srfi-26)
#:use-module (srfi srfi-37)
#:use-module (ice-9 match)
#:use-module (ice-9 format)
@@ -43,7 +45,8 @@
;;;
;;; Command-line options.
;;;
-(define %available-formats '("human" "channels" "json" "recutils"))
+(define %available-formats
+ '("human" "channels" "channels-sans-intro" "json" "recutils"))
(define (list-formats)
(display (G_ "The available formats are:\n"))
@@ -110,21 +113,50 @@ Display information about the channels currently in use.\n"))
(_
(warning (G_ "'GUIX_PACKAGE_PATH' is set but it is not captured~%")))))))
-(define (channel->sexp channel)
- `(channel
- (name ',(channel-name channel))
- (url ,(channel-url channel))
- (commit ,(channel-commit channel))))
+(define* (channel->sexp channel #:key (include-introduction? #t))
+ (let ((intro (and include-introduction?
+ (channel-introduction channel))))
+ `(channel
+ (name ',(channel-name channel))
+ (url ,(channel-url channel))
+ (commit ,(channel-commit channel))
+ ,@(if intro
+ `((introduction (make-channel-introduction
+ ,(channel-introduction-first-signed-commit intro)
+ (openpgp-fingerprint
+ ,(openpgp-format-fingerprint
+ (channel-introduction-first-commit-signer
+ intro))))))
+ '()))))
(define (channel->json channel)
- (scm->json-string `((name . ,(channel-name channel))
- (url . ,(channel-url channel))
- (commit . ,(channel-commit channel)))))
+ (scm->json-string
+ (let ((intro (channel-introduction channel)))
+ `((name . ,(channel-name channel))
+ (url . ,(channel-url channel))
+ (commit . ,(channel-commit channel))
+ ,@(if intro
+ `((introduction
+ . ((commit . ,(channel-introduction-first-signed-commit
+ intro))
+ (signer . ,(openpgp-format-fingerprint
+ (channel-introduction-first-commit-signer
+ intro))))))
+ '())))))
(define (channel->recutils channel port)
+ (define intro
+ (channel-introduction channel))
+
(format port "name: ~a~%" (channel-name channel))
(format port "url: ~a~%" (channel-url channel))
- (format port "commit: ~a~%" (channel-commit channel)))
+ (format port "commit: ~a~%" (channel-commit channel))
+ (when intro
+ (format port "introductioncommit: ~a~%"
+ (channel-introduction-first-signed-commit intro))
+ (format port "introductionsigner: ~a~%"
+ (openpgp-format-fingerprint
+ (channel-introduction-first-commit-signer intro)))))
(define (display-checkout-info fmt)
"Display information about the current checkout according to FMT, a symbol
@@ -182,6 +214,10 @@ in the format specified by FMT."
(display-profile-content profile number))
('channels
(pretty-print `(list ,@(map channel->sexp channels))))
+ ('channels-sans-intro
+ (pretty-print `(list ,@(map (cut channel->sexp <>
+ #:include-introduction? #f)
+ channels))))
('json
(format #t "[~a]~%" (string-join (map channel->json channels) ",")))
('recutils
diff --git a/guix/scripts/git.scm b/guix/scripts/git.scm
new file mode 100644
index 0000000000..bc829cbe99
--- /dev/null
+++ b/guix/scripts/git.scm
@@ -0,0 +1,63 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2020 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 scripts git)
+ #:use-module (ice-9 match)
+ #:use-module (guix ui)
+ #:export (guix-git))
+
+(define (show-help)
+ (display (G_ "Usage: guix git COMMAND ARGS...
+Operate on Git repositories.\n"))
+ (newline)
+ (display (G_ "The valid values for ACTION are:\n"))
+ (newline)
+ (display (G_ "\
+ authenticate verify commit signatures and authorizations\n"))
+ (newline)
+ (display (G_ "
+ -h, --help display this help and exit"))
+ (display (G_ "
+ -V, --version display version information and exit"))
+ (newline)
+ (show-bug-report-information))
+
+(define %sub-commands '("authenticate"))
+
+(define (resolve-sub-command name)
+ (let ((module (resolve-interface
+ `(guix scripts git ,(string->symbol name))))
+ (proc (string->symbol (string-append "guix-git-" name))))
+ (module-ref module proc)))
+
+(define (guix-git . args)
+ (with-error-handling
+ (match args
+ (()
+ (format (current-error-port)
+ (G_ "guix git: missing sub-command~%")))
+ ((or ("-h") ("--help"))
+ (show-help)
+ (exit 0))
+ ((or ("-V") ("--version"))
+ (show-version-and-exit "guix git"))
+ ((sub-command args ...)
+ (if (member sub-command %sub-commands)
+ (apply (resolve-sub-command sub-command) args)
+ (format (current-error-port)
+ (G_ "guix git: invalid sub-command~%")))))))
diff --git a/guix/scripts/git/authenticate.scm b/guix/scripts/git/authenticate.scm
new file mode 100644
index 0000000000..5f5d423f28
--- /dev/null
+++ b/guix/scripts/git/authenticate.scm
@@ -0,0 +1,179 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2020 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 scripts git authenticate)
+ #:use-module (git)
+ #:use-module (guix ui)
+ #:use-module (guix scripts)
+ #:use-module (guix git-authenticate)
+ #:autoload (guix openpgp) (openpgp-format-fingerprint
+ openpgp-public-key-fingerprint)
+ #:use-module ((guix channels) #:select (openpgp-fingerprint))
+ #:use-module ((guix git) #:select (with-git-error-handling))
+ #:use-module (guix progress)
+ #:use-module (guix base64)
+ #:use-module (srfi srfi-1)
+ #:use-module (srfi srfi-26)
+ #:use-module (srfi srfi-37)
+ #:use-module (ice-9 format)
+ #:use-module (ice-9 match)
+ #:export (guix-git-authenticate))
+
+;;; Commentary:
+;;;
+;;; Authenticate a Git checkout by reading '.guix-authorizations' files and
+;;; following the "authorizations invariant" also used by (guix channels).
+;;;
+;;; Code:
+
+(define %options
+ ;; Specifications of the command-line options.
+ (list (option '(#\h "help") #f #f
+ (lambda args
+ (show-help)
+ (exit 0)))
+ (option '(#\V "version") #f #f
+ (lambda args
+ (show-version-and-exit "guix git authenticate")))
+
+ (option '(#\r "repository") #t #f
+ (lambda (opt name arg result)
+ (alist-cons 'directory arg result)))
+ (option '(#\e "end") #t #f
+ (lambda (opt name arg result)
+ (alist-cons 'end-commit (string->oid arg) result)))
+ (option '(#\k "keyring") #t #f
+ (lambda (opt name arg result)
+ (alist-cons 'keyring-reference arg result)))
+ (option '("cache-key") #t #f
+ (lambda (opt name arg result)
+ (alist-cons 'cache-key arg result)))
+ (option '("historical-authorizations") #t #f
+ (lambda (opt name arg result)
+ (alist-cons 'historical-authorizations arg
+ result)))
+ (option '("stats") #f #f
+ (lambda (opt name arg result)
+ (alist-cons 'show-stats? #t result)))))
+
+(define %default-options
+ '((directory . ".")
+ (keyring-reference . "keyring")))
+
+(define (show-stats stats)
+ "Display STATS, an alist containing commit signing stats as returned by
+'authenticate-repository'."
+ (format #t (G_ "Signing statistics:~%"))
+ (for-each (match-lambda
+ ((signer . count)
+ (format #t " ~a ~10d~%"
+ (openpgp-format-fingerprint
+ (openpgp-public-key-fingerprint signer))
+ count)))
+ (sort stats
+ (match-lambda*
+ (((_ . count1) (_ . count2))
+ (> count1 count2))))))
+
+(define (show-help)
+ (display (G_ "Usage: guix git authenticate COMMIT SIGNER [OPTIONS...]
+Authenticate the given Git checkout using COMMIT/SIGNER as its introduction.\n"))
+ (display (G_ "
+ -r, --repository=DIRECTORY
+ open the Git repository at DIRECTORY"))
+ (display (G_ "
+ -k, --keyring=REFERENCE
+ load keyring from REFERENCE, a Git branch"))
+ (display (G_ "
+ --stats display commit signing statistics upon completion"))
+ (display (G_ "
+ --cache-key=KEY cache authenticated commits under KEY"))
+ (display (G_ "
+ --historical-authorizations=FILE
+ read historical authorizations from FILE"))
+ (newline)
+ (display (G_ "
+ -h, --help display this help and exit"))
+ (display (G_ "
+ -V, --version display version information and exit"))
+ (newline)
+ (show-bug-report-information))
+
+
+;;;
+;;; Entry point.
+;;;
+
+(define (guix-git-authenticate . args)
+ (define options
+ (parse-command-line args %options (list %default-options)
+ #:build-options? #f))
+
+ (define (command-line-arguments lst)
+ (reverse (filter-map (match-lambda
+ (('argument . arg) arg)
+ (_ #f))
+ lst)))
+
+ (define commit-short-id
+ (compose (cut string-take <> 7) oid->string commit-id))
+
+ (define (make-reporter start-commit end-commit commits)
+ (format (current-error-port)
+ (G_ "Authenticating commits ~a to ~a (~h new \
+commits)...~%")
+ (commit-short-id start-commit)
+ (commit-short-id end-commit)
+ (length commits))
+
+ (if (isatty? (current-error-port))
+ (progress-reporter/bar (length commits))
+ progress-reporter/silent))
+
+ (with-error-handling
+ (with-git-error-handling
+ (match (command-line-arguments options)
+ ((commit signer)
+ (let* ((directory (assoc-ref options 'directory))
+ (show-stats? (assoc-ref options 'show-stats?))
+ (keyring (assoc-ref options 'keyring-reference))
+ (repository (repository-open directory))
+ (end (match (assoc-ref options 'end-commit)
+ (#f (reference-target
+ (repository-head repository)))
+ (oid oid)))
+ (history (match (assoc-ref options 'historical-authorizations)
+ (#f '())
+ (file (call-with-input-file file
+ read-authorizations))))
+ (cache-key (or (assoc-ref options 'cache-key)
+ (repository-cache-key repository))))
+ (define stats
+ (authenticate-repository repository (string->oid commit)
+ (openpgp-fingerprint signer)
+ #:end end
+ #:keyring-reference keyring
+ #:historical-authorizations history
+ #:cache-key cache-key
+ #:make-reporter make-reporter))
+
+ (when (and show-stats? (not (null? stats)))
+ (show-stats stats))))
+ (_
+ (leave (G_ "wrong number of arguments; \
+expected COMMIT and SIGNER~%")))))))
diff --git a/guix/scripts/graph.scm b/guix/scripts/graph.scm
index 1d5db3b3cb..489931d5bb 100644
--- a/guix/scripts/graph.scm
+++ b/guix/scripts/graph.scm
@@ -43,6 +43,7 @@
#:use-module (srfi srfi-34)
#:use-module (srfi srfi-35)
#:use-module (srfi srfi-37)
+ #:use-module (ice-9 format)
#:use-module (ice-9 match)
#:export (%package-node-type
%reverse-package-node-type
diff --git a/guix/scripts/pack.scm b/guix/scripts/pack.scm
index e0f9cc1a12..5fb6aaae0c 100644
--- a/guix/scripts/pack.scm
+++ b/guix/scripts/pack.scm
@@ -149,6 +149,11 @@ dependencies are registered."
(define db-file
(store-database-file #:state-directory #$output))
+ ;; Make sure non-ASCII file names are properly handled.
+ (setenv "GUIX_LOCPATH"
+ #+(file-append glibc-utf8-locales "/lib/locale"))
+ (setlocale LC_ALL "en_US.utf8")
+
(sql-schema #$schema)
(let ((items (append-map read-closure '#$labels)))
(with-database db-file db
@@ -181,6 +186,15 @@ added to the pack."
(file-append (store-database (list profile))
"/db/db.sqlite")))
+ (define set-utf8-locale
+ ;; Arrange to not depend on 'glibc-utf8-locales' when using '--bootstrap'.
+ (and (or (not (profile? profile))
+ (profile-locales? profile))
+ #~(begin
+ (setenv "GUIX_LOCPATH"
+ #+(file-append glibc-utf8-locales "/lib/locale"))
+ (setlocale LC_ALL "en_US.utf8"))))
+
(define build
(with-imported-modules (source-module-closure
`((guix build utils)
@@ -226,6 +240,9 @@ added to the pack."
"cf" "/dev/null" "--files-from=/dev/null"
"--sort=name")))
+ ;; Make sure non-ASCII file names are properly handled.
+ #+set-utf8-locale
+
;; Add 'tar' to the search path.
(setenv "PATH" #+(file-append archiver "/bin"))
@@ -836,9 +853,10 @@ last resort for relocation."
(scandir input))
(for-each build-wrapper
- (append (find-files (string-append input "/bin"))
- (find-files (string-append input "/sbin"))
- (find-files (string-append input "/libexec")))))))
+ ;; Note: Trailing slash in case these are symlinks.
+ (append (find-files (string-append input "/bin/"))
+ (find-files (string-append input "/sbin/"))
+ (find-files (string-append input "/libexec/")))))))
(computed-file (string-append
(cond ((package? package)
@@ -857,7 +875,10 @@ last resort for relocation."
(item (apply wrapped-package
(manifest-entry-item entry)
(manifest-entry-output entry)
- args))))
+ args))
+ (dependencies (map (lambda (entry)
+ (apply wrapped-manifest-entry entry args))
+ (manifest-entry-dependencies entry)))))
;;;
diff --git a/guix/scripts/processes.scm b/guix/scripts/processes.scm
index a2ab017490..01f7213e8c 100644
--- a/guix/scripts/processes.scm
+++ b/guix/scripts/processes.scm
@@ -1,5 +1,5 @@
;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2018, 2019 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2018, 2019, 2020 Ludovic Courtès <ludo@gnu.org>
;;;
;;; This file is part of GNU Guix.
;;;
@@ -231,7 +231,8 @@ List the current Guix sessions and their processes."))
cons
'()))
- (for-each (lambda (session)
- (daemon-session->recutils session (current-output-port))
- (newline))
- (daemon-sessions)))
+ (with-paginated-output-port port
+ (for-each (lambda (session)
+ (daemon-session->recutils session port)
+ (newline port))
+ (daemon-sessions))))
diff --git a/guix/scripts/pull.scm b/guix/scripts/pull.scm
index f953957161..807daec593 100644
--- a/guix/scripts/pull.scm
+++ b/guix/scripts/pull.scm
@@ -63,7 +63,6 @@
#:re-export (display-profile-content
channel-commit-hyperlink)
#:export (channel-list
- with-git-error-handling
guix-pull))
@@ -464,23 +463,6 @@ true, display what would be built without actually building it."
(unless (honor-system-x509-certificates!)
(honor-lets-encrypt-certificates! store)))
-(define (report-git-error error)
- "Report the given Guile-Git error."
- ;; Prior to Guile-Git commit b6b2760c2fd6dfaa5c0fedb43eeaff06166b3134,
- ;; errors would be represented by integers.
- (match error
- ((? integer? error) ;old Guile-Git
- (leave (G_ "Git error ~a~%") error))
- ((? git-error? error) ;new Guile-Git
- (leave (G_ "Git error: ~a~%") (git-error-message error)))))
-
-(define-syntax-rule (with-git-error-handling body ...)
- (catch 'git-error
- (lambda ()
- body ...)
- (lambda (key err)
- (report-git-error err))))
-
;;;
;;; Profile.
diff --git a/guix/scripts/system.scm b/guix/scripts/system.scm
index d9cf45da23..79bfcd7db2 100644
--- a/guix/scripts/system.scm
+++ b/guix/scripts/system.scm
@@ -446,17 +446,6 @@ list of services."
;;; Generations.
;;;
-(define (sexp->channel sexp)
- "Return the channel corresponding to SEXP, an sexp as found in the
-\"provenance\" file produced by 'provenance-service-type'."
- (match sexp
- (('channel ('name name)
- ('url url)
- ('branch branch)
- ('commit commit))
- (channel (name name) (url url)
- (branch branch) (commit commit)))))
-
(define* (display-system-generation number
#:optional (profile %system-profile))
"Display a summary of system generation NUMBER in a human-readable format."
@@ -480,12 +469,10 @@ list of services."
(uuid->string root)
root))
(kernel (boot-parameters-kernel params))
- (provenance (catch 'system-error
- (lambda ()
- (call-with-input-file
- (string-append generation "/provenance")
- read))
- (const #f))))
+ (multiboot-modules (boot-parameters-multiboot-modules params)))
+ (define-values (channels config-file)
+ (system-provenance generation))
+
(display-generation profile number)
(format #t (G_ " file name: ~a~%") generation)
(format #t (G_ " canonical file name: ~a~%") (readlink* generation))
@@ -509,21 +496,22 @@ list of services."
(format #t (G_ " kernel: ~a~%") kernel)
- (match provenance
- (#f #t)
- (('provenance ('version 0)
- ('channels channels ...)
- ('configuration-file config-file))
- (unless (null? channels)
- ;; TRANSLATORS: Here "channel" is the same terminology as used in
- ;; "guix describe" and "guix pull --channels".
- (format #t (G_ " channels:~%"))
- (for-each display-channel (map sexp->channel channels)))
- (when config-file
- (format #t (G_ " configuration file: ~a~%")
- (if (supports-hyperlinks?)
- (file-hyperlink config-file)
- config-file))))))))
+ (match multiboot-modules
+ (() #f)
+ (((modules . _) ...)
+ (format #t (G_ " multiboot: ~a~%")
+ (string-join modules "\n "))))
+
+ (unless (null? channels)
+ ;; TRANSLATORS: Here "channel" is the same terminology as used in
+ ;; "guix describe" and "guix pull --channels".
+ (format #t (G_ " channels:~%"))
+ (for-each display-channel channels))
+ (when config-file
+ (format #t (G_ " configuration file: ~a~%")
+ (if (supports-hyperlinks?)
+ (file-hyperlink config-file)
+ config-file))))))
(define* (list-generations pattern #:optional (profile %system-profile))
"Display in a human-readable format all the system generations matching
@@ -748,6 +736,7 @@ and TARGET arguments."
(define* (perform-action action os
#:key
+ (validate-reconfigure ensure-forward-reconfigure)
save-provenance?
skip-safety-checks?
install-bootloader?
@@ -790,7 +779,8 @@ static checks."
(operating-system-bootcfg os menu-entries)))
(when (eq? action 'reconfigure)
- (maybe-suggest-running-guix-pull))
+ (maybe-suggest-running-guix-pull)
+ (check-forward-update validate-reconfigure))
;; Check whether the declared file systems exist. This is better than
;; instantiating a broken configuration. Assume that we can only check if
@@ -939,6 +929,9 @@ Some ACTIONS support additional ARGS.\n"))
-e, --expression=EXPR consider the operating-system EXPR evaluates to
instead of reading FILE, when applicable"))
(display (G_ "
+ --allow-downgrades for 'reconfigure', allow downgrades to earlier
+ channel revisions"))
+ (display (G_ "
--on-error=STRATEGY
apply STRATEGY (one of nothing-special, backtrace,
or debug) when an error occurs while reading FILE"))
@@ -993,6 +986,11 @@ Some ACTIONS support additional ARGS.\n"))
(option '(#\d "derivation") #f #f
(lambda (opt name arg result)
(alist-cons 'derivations-only? #t result)))
+ (option '("allow-downgrades") #f #f
+ (lambda (opt name arg result)
+ (alist-cons 'validate-reconfigure
+ warn-about-backward-reconfigure
+ result)))
(option '("on-error") #t #f
(lambda (opt name arg result)
(alist-cons 'on-error (string->symbol arg)
@@ -1065,6 +1063,7 @@ Some ACTIONS support additional ARGS.\n"))
(graft? . #t)
(debug . 0)
(verbosity . #f) ;default
+ (validate-reconfigure . ,ensure-forward-reconfigure)
(file-system-type . "ext4")
(image-size . guess)
(install-bootloader? . #t)))
@@ -1150,6 +1149,8 @@ resulting from command-line parsing."
#:use-substitutes? (assoc-ref opts 'substitutes?)
#:skip-safety-checks?
(assoc-ref opts 'skip-safety-checks?)
+ #:validate-reconfigure
+ (assoc-ref opts 'validate-reconfigure)
#:file-system-type (assoc-ref opts 'file-system-type)
#:image-size (assoc-ref opts 'image-size)
#:full-boot? (assoc-ref opts 'full-boot?)
diff --git a/guix/scripts/system/reconfigure.scm b/guix/scripts/system/reconfigure.scm
index 7885c33457..9013e035f7 100644
--- a/guix/scripts/system/reconfigure.scm
+++ b/guix/scripts/system/reconfigure.scm
@@ -34,9 +34,18 @@
#:use-module (guix monads)
#:use-module (guix store)
#:use-module ((guix self) #:select (make-config.scm))
+ #:autoload (guix describe) (current-profile)
+ #:use-module (guix channels)
+ #:autoload (guix git) (update-cached-checkout)
+ #:use-module (guix i18n)
+ #:use-module (guix diagnostics)
+ #:use-module ((guix utils) #:select (&fix-hint))
#:use-module (ice-9 match)
#:use-module (srfi srfi-1)
#:use-module (srfi srfi-11)
+ #:use-module (srfi srfi-34)
+ #:use-module (srfi srfi-35)
+ #:use-module ((guix config) #:select (%guix-package-name))
#:export (switch-system-program
switch-to-system
@@ -44,7 +53,11 @@
upgrade-shepherd-services
install-bootloader-program
- install-bootloader))
+ install-bootloader
+
+ check-forward-update
+ ensure-forward-reconfigure
+ warn-about-backward-reconfigure))
;;; Commentary:
;;;
@@ -266,3 +279,85 @@ additional configurations specified by MENU-ENTRIES can be selected."
bootcfg-file
device
target))))))
+
+
+;;;
+;;; Downgrade detection.
+;;;
+
+(define (ensure-forward-reconfigure channel start commit relation)
+ "Raise an error if RELATION is not 'ancestor, meaning that START is not an
+ancestor of COMMIT, unless CHANNEL specifies a commit."
+ (match relation
+ ('ancestor #t)
+ ('self #t)
+ (_
+ (raise (make-compound-condition
+ (condition
+ (&message (message
+ (format #f (G_ "\
+aborting reconfiguration because commit ~a of channel '~a' is not a descendant of ~a")
+ commit (channel-name channel)
+ start)))
+ (&fix-hint
+ (hint (G_ "Use @option{--allow-downgrades} to force
+this downgrade.")))))))))
+
+(define (warn-about-backward-reconfigure channel start commit relation)
+ "Warn about non-forward updates of CHANNEL from START to COMMIT, without
+aborting."
+ (match relation
+ ((or 'ancestor 'self)
+ #t)
+ ('descendant
+ (warning (G_ "rolling back channel '~a' from ~a to ~a~%")
+ (channel-name channel) start commit))
+ ('unrelated
+ (warning (G_ "moving channel '~a' from ~a to unrelated commit ~a~%")
+ (channel-name channel) start commit))))
+
+(define (channel-relations old new)
+ "Return a list of channel/relation pairs, where each relation is a symbol as
+returned by 'commit-relation' denoting how commits of channels in OLD relate
+to commits of channels in NEW."
+ (filter-map (lambda (old)
+ (let ((new (find (lambda (channel)
+ (eq? (channel-name channel)
+ (channel-name old)))
+ new)))
+ (and new
+ (let-values (((checkout commit relation)
+ (update-cached-checkout
+ (channel-url new)
+ #:ref
+ `(commit . ,(channel-commit new))
+ #:starting-commit
+ (channel-commit old)
+ #:check-out? #f)))
+ (list new
+ (channel-commit old) (channel-commit new)
+ relation)))))
+ old))
+
+(define* (check-forward-update #:optional
+ (validate-reconfigure ensure-forward-reconfigure))
+ "Call VALIDATE-RECONFIGURE passing it, for each channel, the channel, the
+currently-deployed commit (as returned by 'guix system describe') and the
+target commit (as returned by 'guix describe')."
+ ;; TODO: Make that functionality available to 'guix deploy'.
+ (define new
+ (or (and=> (current-profile) profile-channels)
+ '()))
+
+ (define old
+ (system-provenance "/run/current-system"))
+
+ (when (null? old)
+ (warning (G_ "cannot determine provenance for /run/current-system~%")))
+ (when (and (null? new) (not (getenv "GUIX_UNINSTALLED")))
+ (warning (G_ "cannot determine provenance of ~a~%") %guix-package-name))
+
+ (for-each (match-lambda
+ ((channel old new relation)
+ (validate-reconfigure channel old new relation)))
+ (channel-relations old new)))
diff --git a/guix/scripts/system/search.scm b/guix/scripts/system/search.scm
index d2eac06cca..bf49ea2341 100644
--- a/guix/scripts/system/search.scm
+++ b/guix/scripts/system/search.scm
@@ -26,6 +26,7 @@
#:use-module (srfi srfi-11)
#:use-module (srfi srfi-26)
#:use-module (srfi srfi-34)
+ #:use-module (ice-9 format)
#:use-module (ice-9 regex)
#:use-module (ice-9 match)
#:export (service-type->recutils
diff --git a/guix/scripts/time-machine.scm b/guix/scripts/time-machine.scm
index f9bcec651a..441673b780 100644
--- a/guix/scripts/time-machine.scm
+++ b/guix/scripts/time-machine.scm
@@ -24,10 +24,12 @@
#:use-module (guix channels)
#:use-module (guix store)
#:use-module (guix status)
+ #:use-module ((guix git)
+ #:select (with-git-error-handling))
#:use-module ((guix utils)
#:select (%current-system))
#:use-module ((guix scripts pull)
- #:select (with-git-error-handling channel-list))
+ #:select (channel-list))
#:use-module ((guix scripts build)
#:select (%standard-build-options
show-build-options-help
diff --git a/guix/scripts/weather.scm b/guix/scripts/weather.scm
index 475d989357..3035ff6ca8 100644
--- a/guix/scripts/weather.scm
+++ b/guix/scripts/weather.scm
@@ -190,7 +190,7 @@ Return the coverage ratio, an exact number between 0 and 1."
narinfos))
(time (+ (time-second time)
(/ (time-nanosecond time) 1e9))))
- (format #t (G_ " ~2,1f% substitutes available (~h out of ~h)~%")
+ (format #t (G_ " ~,1f% substitutes available (~h out of ~h)~%")
(* 100. (/ obtained requested 1.))
obtained requested)
(let ((total (/ (reduce + 0 sizes) MiB)))