Add 'guix time-machine'.

* guix/scripts/time-machine.scm: New file.
* Makefile.am: (MODULES): Add it.
* guix/scripts/pull.scm (channel-list): Export.
* guix/inferior.scm (cached-channel-instance): New procedure.
(inferior-for-channels): Use it.
* doc/guix.texi (Invoking guix time-machine): New section.
(Channels): Cross-reference it.

Signed-off-by: Ludovic Courtès <ludo@gnu.org>
This commit is contained in:
Konrad Hinsen 2019-10-25 17:42:21 +02:00 committed by Ludovic Courtès
parent 1edcfda81b
commit f675f8dec7
No known key found for this signature in database
GPG key ID: 090B11993D9AEBB5
5 changed files with 187 additions and 14 deletions

View file

@ -278,6 +278,7 @@ MODULES = \
guix/scripts/container.scm \
guix/scripts/container/exec.scm \
guix/scripts/deploy.scm \
guix/scripts/time-machine.scm \
guix.scm \
$(GNU_SYSTEM_MODULES)

View file

@ -198,6 +198,7 @@ Package Management
* Invoking guix gc:: Running the garbage collector.
* Invoking guix pull:: Fetching the latest Guix and distribution.
* Channels:: Customizing the package collection.
* Invoking guix time-machine:: Running an older revision of Guix.
* Inferiors:: Interacting with another revision of Guix.
* Invoking guix describe:: Display information about your Guix revision.
* Invoking guix archive:: Exporting and importing store files.
@ -2550,6 +2551,7 @@ guix install emacs-guix
* Invoking guix gc:: Running the garbage collector.
* Invoking guix pull:: Fetching the latest Guix and distribution.
* Channels:: Customizing the package collection.
* Invoking guix time-machine:: Running an older revision of Guix.
* Inferiors:: Interacting with another revision of Guix.
* Invoking guix describe:: Display information about your Guix revision.
* Invoking guix archive:: Exporting and importing store files.
@ -4152,7 +4154,10 @@ say, on another machine, by providing a channel specification in
@end lisp
The @command{guix describe --format=channels} command can even generate this
list of channels directly (@pxref{Invoking guix describe}).
list of channels directly (@pxref{Invoking guix describe}). The resulting
file can be used with the -C options of @command{guix pull}
(@pxref{Invoking guix pull}) or @command{guix time-machine}
(@pxref{Invoking guix time-machine}).
At this point the two machines run the @emph{exact same Guix}, with access to
the @emph{exact same packages}. The output of @command{guix build gimp} on
@ -4166,6 +4171,57 @@ artifacts with very fine grain, and to reproduce software environments at
will---some sort of ``meta reproducibility'' capabilities, if you will.
@xref{Inferiors}, for another way to take advantage of these super powers.
@node Invoking guix time-machine
@section Invoking @command{guix time-machine}
@cindex @command{guix time-machine}
@cindex pinning, channels
@cindex replicating Guix
@cindex reproducibility, of Guix
The @command{guix time-machine} command provides access to other
revisions of Guix, for example to install older versions of packages,
or to reproduce a computation in an identical environment. The revision
of Guix to be used is defined by a commit or by a channel
description file created by @command{guix describe}
(@pxref{Invoking guix describe}).
The general syntax is:
@example
guix time-machine @var{options}@dots{} -- @var{command} @var {arg}@dots{}
@end example
where @var{command} and @var{arg}@dots{} are passed unmodified to the
@command{guix} command if the specified revision. The @var{options} that define
this revision are the same as for @command{guix pull} (@pxref{Invoking guix pull}):
@table @code
@item --url=@var{url}
@itemx --commit=@var{commit}
@itemx --branch=@var{branch}
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 @var{branch}.
@item --channels=@var{file}
@itemx -C @var{file}
Read the list of channels from @var{file}. @var{file} must contain
Scheme code that evaluates to a list of channel objects.
@xref{Channels} for more information.
@end table
As for @command{guix pull}, the absence of any options means that the
the latest commit on the master branch will be used. The command
@example
guix time-machine -- build hello
@end example
will thus build the package @code{hello} as defined in the master branch,
which is in general a newer revison of Guix than you have installed.
Time travel works in both directions!
@node Inferiors
@section Inferiors
@ -10589,7 +10645,6 @@ ClientPID: 19419
ClientCommand: cuirass --cache-directory /var/cache/cuirass @dots{}
@end example
@node System Configuration
@chapter System Configuration

View file

@ -89,6 +89,7 @@ (define-module (guix inferior)
gexp->derivation-in-inferior
%inferior-cache-directory
cached-channel-instance
inferior-for-channels))
;;; Commentary:
@ -635,16 +636,13 @@ (define %inferior-cache-directory
(make-parameter (string-append (cache-directory #:ensure? #f)
"/inferiors")))
(define* (inferior-for-channels channels
#:key
(cache-directory (%inferior-cache-directory))
(ttl (* 3600 24 30)))
"Return an inferior for CHANNELS, a list of channels. Use the cache at
CACHE-DIRECTORY, where entries can be reclaimed after TTL seconds. This
procedure opens a new connection to the build daemon.
This is a convenience procedure that people may use in manifests passed to
'guix package -m', for instance."
(define* (cached-channel-instance channels
#:key
(cache-directory (%inferior-cache-directory))
(ttl (* 3600 24 30)))
"Return a directory containing a guix filetree defined by CHANNELS, a list of channels.
The directory is a subdirectory of CACHE-DIRECTORY, where entries can be reclaimed after TTL seconds.
This procedure opens a new connection to the build daemon."
(with-store store
(let ()
(define instances
@ -680,7 +678,7 @@ (define add-indirect-root*
(file-expiration-time ttl))
(if (file-exists? cached)
(open-inferior cached)
cached
(run-with-store store
(mlet %store-monad ((profile
(channel-instances->derivation instances)))
@ -689,4 +687,20 @@ (define add-indirect-root*
(built-derivations (list profile))
(symlink* (derivation->output-path profile) cached)
(add-indirect-root* cached)
(return (open-inferior cached)))))))))
(return cached))))))))
(define* (inferior-for-channels channels
#:key
(cache-directory (%inferior-cache-directory))
(ttl (* 3600 24 30)))
"Return an inferior for CHANNELS, a list of channels. Use the cache at
CACHE-DIRECTORY, where entries can be reclaimed after TTL seconds. This
procedure opens a new connection to the build daemon.
This is a convenience procedure that people may use in manifests passed to
'guix package -m', for instance."
(define cached
(cached-channel-instance channels
#:cache-directory cache-directory
#:ttl ttl))
(open-inferior cached))

View file

@ -56,6 +56,7 @@ (define-module (guix scripts pull)
#:use-module (ice-9 vlist)
#:use-module (ice-9 format)
#:export (display-profile-content
channel-list
guix-pull))

View file

@ -0,0 +1,102 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2019 Konrad Hinsen <konrad.hinsen@fastmail.net>
;;;
;;; 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 time-machine)
#:use-module (guix ui)
#:use-module (guix scripts)
#:use-module (guix inferior)
#:use-module (guix channels)
#:use-module ((guix scripts pull) #:select (channel-list))
#:use-module (ice-9 match)
#:use-module (srfi srfi-1)
#:use-module (srfi srfi-11)
#:use-module (srfi srfi-26)
#:use-module (srfi srfi-37)
#:export (guix-time-machine))
;;;
;;; Command-line options.
;;;
(define (show-help)
(display (G_ "Usage: guix time-machine [OPTION] -- COMMAND ARGS...
Execute COMMAND ARGS... in an older version of Guix.\n"))
(display (G_ "
-C, --channels=FILE deploy the channels defined in FILE"))
(display (G_ "
--url=URL use the Git repository at URL"))
(display (G_ "
--commit=COMMIT use the specified COMMIT"))
(display (G_ "
--branch=BRANCH use the tip of the specified BRANCH"))
(display (G_ "
-h, --help display this help and exit"))
(display (G_ "
-V, --version display version information and exit"))
(newline)
(show-bug-report-information))
(define %options
;; Specifications of the command-line options.
(list (option '(#\C "channels") #t #f
(lambda (opt name arg result)
(alist-cons 'channel-file arg result)))
(option '("url") #t #f
(lambda (opt name arg result)
(alist-cons 'repository-url arg
(alist-delete 'repository-url result))))
(option '("commit") #t #f
(lambda (opt name arg result)
(alist-cons 'ref `(commit . ,arg) result)))
(option '("branch") #t #f
(lambda (opt name arg result)
(alist-cons 'ref `(branch . ,arg) result)))
(option '(#\h "help") #f #f
(lambda args
(show-help)
(exit 0)))
(option '(#\V "version") #f #f
(lambda args
(show-version-and-exit "guix time-machine")))))
(define (parse-args args)
"Parse the list of command line arguments ARGS."
;; The '--' token is used to separate the command to run from the rest of
;; the operands.
(let-values (((args command) (break (cut string=? "--" <>) args)))
(let ((opts (parse-command-line args %options '(()) #:build-options? #f)))
(match command
(() opts)
(("--") opts)
(("--" command ...) (alist-cons 'exec command opts))))))
;;;
;;; Entry point.
;;;
(define (guix-time-machine . args)
(with-error-handling
(let* ((opts (parse-args args))
(channels (channel-list opts))
(command-line (assoc-ref opts 'exec)))
(when command-line
(let* ((directory (cached-channel-instance channels))
(executable (string-append directory "/bin/guix")))
(apply execl (cons* executable executable command-line)))))))