From bd7470185bae15e686c2b2a83d3f61807e6fa527 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludovic=20Court=C3=A8s?= Date: Mon, 3 Sep 2018 15:03:33 +0200 Subject: [PATCH] Add 'guix describe'. * guix/scripts/describe.scm: New file. * Makefile.am (MODULES): Add it. (SH_TESTS): Add tests/guix-describe.sh. * po/guix/POTFILES.in: Add it. * guix/scripts/pull.scm (display-profile-content): Export. * guix/describe.scm (current-profile, current-profile-entries): Export. * tests/guix-describe.sh: New file. * doc/guix.texi (Features): Mention 'guix pull' and provenance tracking. (Invoking guix pull): Link to 'guix describe'. (Channels): Likewise. (Invoking guix describe): New node. --- Makefile.am | 2 + doc/guix.texi | 94 +++++++++++++++++++++- guix/describe.scm | 4 +- guix/scripts/describe.scm | 160 ++++++++++++++++++++++++++++++++++++++ guix/scripts/pull.scm | 3 +- po/guix/POTFILES.in | 1 + tests/guix-describe.sh | 47 +++++++++++ 7 files changed, 308 insertions(+), 3 deletions(-) create mode 100644 guix/scripts/describe.scm create mode 100644 tests/guix-describe.sh diff --git a/Makefile.am b/Makefile.am index a3498460e0..5c8639d665 100644 --- a/Makefile.am +++ b/Makefile.am @@ -204,6 +204,7 @@ MODULES = \ guix/scripts/authenticate.scm \ guix/scripts/refresh.scm \ guix/scripts/repl.scm \ + guix/scripts/describe.scm \ guix/scripts/system.scm \ guix/scripts/system/search.scm \ guix/scripts/lint.scm \ @@ -409,6 +410,7 @@ SH_TESTS = \ tests/guix-environment.sh \ tests/guix-environment-container.sh \ tests/guix-graph.sh \ + tests/guix-describe.sh \ tests/guix-lint.sh TESTS = $(SCM_TESTS) $(SH_TESTS) diff --git a/doc/guix.texi b/doc/guix.texi index 30f1be2ab8..19a497c746 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -147,6 +147,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 describe:: Display information about your Guix revision. * Invoking guix pack:: Creating software bundles. * Invoking guix archive:: Exporting and importing store files. @@ -1698,6 +1699,7 @@ guix package -i 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 describe:: Display information about your Guix revision. * Invoking guix pack:: Creating software bundles. * Invoking guix archive:: Exporting and importing store files. @end menu @@ -1751,7 +1753,7 @@ collected. @cindex reproducibility @cindex reproducible builds -Finally, Guix takes a @dfn{purely functional} approach to package +Guix takes a @dfn{purely functional} approach to package management, as described in the introduction (@pxref{Introduction}). Each @file{/gnu/store} package directory name contains a hash of all the inputs that were used to build that package---compiler, libraries, build @@ -1779,6 +1781,15 @@ a package to quickly set up the right development environment for their package, without having to manually install the dependencies of the package into their profile (@pxref{Invoking guix environment}). +@cindex replication, of software environments +@cindex provenance tracking, of software artifacts +All of Guix and its package definitions is version-controlled, and +@command{guix pull} allows you to ``travel in time'' on the history of Guix +itself (@pxref{Invoking guix pull}). This makes it possible to replicate a +Guix instance on a different machine or at a later point in time, which in +turn allows you to @emph{replicate complete software environments}, while +retaining precise @dfn{provenance tracking} of the software. + @node Invoking guix package @section Invoking @command{guix package} @@ -2806,6 +2817,9 @@ Generation 3 Jun 13 2018 23:31:07 (current) 69 packages upgraded: borg@@1.1.6, cheese@@3.28.0, @dots{} @end example +@ref{Invoking guix describe, @command{guix describe}}, for other ways to +describe the current status of Guix. + This @code{~/.config/guix/current} profile works like any other profile created by @command{guix package} (@pxref{Invoking guix package}). That is, you can list generations, roll back to the previous @@ -2851,6 +2865,9 @@ is provided, the subset of generations that match @var{pattern}. The syntax of @var{pattern} is the same as with @code{guix package --list-generations} (@pxref{Invoking guix package}). +@ref{Invoking guix describe}, for a way to display information about the +current generation only. + @item --profile=@var{profile} @itemx -p @var{profile} Use @var{profile} instead of @file{~/.config/guix/current}. @@ -3023,6 +3040,9 @@ say, on another machine, by providing a channel specification in (branch "dd3df5e2c8818760a8fc0bd699e55d3b69fef2bb"))) @end lisp +The @command{guix describe --format=channels} command can even generate this +list of channels directly (@pxref{Invoking guix describe}). + 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 one machine will be exactly the same, bit for bit, as the output of the same @@ -3034,6 +3054,78 @@ This gives you super powers, allowing you to track the provenance of binary artifacts with very fine grain, and to reproduce software environments at will---some sort of ``meta reproducibility'' capabilities, if you will. +@node Invoking guix describe +@section Invoking @command{guix describe} + +@cindex reproducibility +@cindex replicating Guix +Often you may want to answer questions like: ``Which revision of Guix am I +using?'' or ``Which channels am I using?'' This is useful information in many +situations: if you want to @emph{replicate} an environment on a different +machine or user account, if you want to report a bug or to determine what +change in the channels you are using caused it, or if you want to record your +system state for reproducibility purposes. The @command{guix describe} +command answers these questions. + +When run from a @command{guix pull}ed @command{guix}, @command{guix describe} +displays the channel(s) that it was built from, including their repository URL +and commit IDs (@pxref{Channels}): + +@example +$ guix describe +Generation 10 Sep 03 2018 17:32:44 (current) + guix e0fa68c + repository URL: https://git.savannah.gnu.org/git/guix.git + branch: master + commit: e0fa68c7718fffd33d81af415279d6ddb518f727 +@end example + +If you're familiar with the Git version control system, this is similar in +spirit to @command{git describe}; the output is also similar to that of +@command{guix pull --list-generations}, but limited to the current generation +(@pxref{Invoking guix pull, the @option{--list-generations} option}). Because +the Git commit ID shown above unambiguously refers to a snapshot of Guix, this +information is all it takes to describe the revision of Guix you're using, and +also to replicate it. + +To make it easier to replicate Guix, @command{guix describe} can also be asked +to return a list of channels instead of the human-readable description above: + +@example +$ guix describe -f channels +(list (channel + (name 'guix) + (url "https://git.savannah.gnu.org/git/guix.git") + (commit + "e0fa68c7718fffd33d81af415279d6ddb518f727"))) +@end example + +@noindent +You can save this to a file and feed it to @command{guix pull -C} on some +other machine or at a later point in time, which will instantiate @emph{this +exact Guix revision} (@pxref{Invoking guix pull, the @option{-C} option}). +From there on, since you're able to deploy the same revision of Guix, you can +just as well @emph{replicate a complete software environment}. We humbly +think that this is @emph{awesome}, and we hope you'll like it too! + +The details of the options supported by @command{guix describe} are as +follows: + +@table @code +@item --format=@var{format} +@itemx -f @var{format} +Produce output in the specified @var{format}, one of: + +@table @code +@item human +produce human-readable output; +@item channels +produce a list of channel specifications that can be passed to @command{guix +pull -C} or installed as @file{~/.config/guix/channels.scm} (@pxref{Invoking +guix pull}). +@end table +@end table + @node Invoking guix pack @section Invoking @command{guix pack} diff --git a/guix/describe.scm b/guix/describe.scm index 3122a762fe..670db63ce7 100644 --- a/guix/describe.scm +++ b/guix/describe.scm @@ -21,7 +21,9 @@ #:use-module (guix profiles) #:use-module (srfi srfi-1) #:use-module (ice-9 match) - #:export (package-path-entries)) + #:export (current-profile + current-profile-entries + package-path-entries)) ;;; Commentary: ;;; diff --git a/guix/scripts/describe.scm b/guix/scripts/describe.scm new file mode 100644 index 0000000000..46feea2940 --- /dev/null +++ b/guix/scripts/describe.scm @@ -0,0 +1,160 @@ +;;; GNU Guix --- Functional package management for GNU +;;; Copyright © 2018 Ludovic Courtès +;;; +;;; 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 . + +(define-module (guix scripts describe) + #:use-module ((guix ui) #:hide (display-profile-content)) + #:use-module (guix scripts) + #:use-module (guix describe) + #:use-module (guix profiles) + #:use-module ((guix scripts pull) #:select (display-profile-content)) + #:use-module (git) + #:use-module (srfi srfi-1) + #:use-module (srfi srfi-37) + #:use-module (ice-9 match) + #:autoload (ice-9 pretty-print) (pretty-print) + #:export (guix-describe)) + + +;;; +;;; Command-line options. +;;; + +(define %options + ;; Specifications of the command-line options. + (list (option '(#\f "format") #t #f + (lambda (opt name arg result) + (unless (member arg '("human" "channels")) + (leave (G_ "~a: unsupported output format~%") arg)) + (alist-cons 'format 'channels result))) + (option '(#\h "help") #f #f + (lambda args + (show-help) + (exit 0))) + (option '(#\V "version") #f #f + (lambda args + (show-version-and-exit "guix describe"))))) + +(define %default-options + ;; Alist of default option values. + '((format . human))) + +(define (show-help) + (display (G_ "Usage: guix describe [OPTION]... +Display information about the channels currently in use.\n")) + (display (G_ " + -f, --format=FORMAT display information in the given FORMAT")) + (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 (display-package-search-path fmt) + "Display GUIX_PACKAGE_PATH, if it is set, according to FMT." + (match (getenv "GUIX_PACKAGE_PATH") + (#f #t) + (string + (match fmt + ('human + (format #t "~%GUIX_PACKAGE_PATH=\"~a\"~%" string)) + ('channels + (format #t (G_ "~%;; warning: GUIX_PACKAGE_PATH=\"~a\"~%") + string)))))) + +(define (display-checkout-info fmt) + "Display information about the current checkout according to FMT, a symbol +denoting the requested format. Exit if the current directory does not lie +within a Git checkout." + (let* ((program (car (command-line))) + (directory (catch 'git-error + (lambda () + (repository-discover (dirname program))) + (lambda (key err) + (leave (G_ "failed to determine origin~%"))))) + (repository (repository-open directory)) + (head (repository-head repository)) + (commit (oid->string (reference-target head)))) + (match fmt + ('human + (format #t (G_ "Git checkout:~%")) + (format #t (G_ " repository: ~a~%") (dirname directory)) + (format #t (G_ " branch: ~a~%") (reference-shorthand head)) + (format #t (G_ " commit: ~a~%") commit)) + ('channels + (pretty-print `(list (channel + (name 'guix) + (url ,(dirname directory)) + (commit ,commit)))))) + (display-package-search-path fmt))) + +(define (display-profile-info profile fmt) + "Display information about PROFILE, a profile as created by (guix channels), +in the format specified by FMT." + (define number + (match (profile-generations profile) + ((_ ... last) last))) + + (match fmt + ('human + (display-profile-content profile number)) + ('channels + (pretty-print + `(list ,@(map (lambda (entry) + (match (assq 'source (manifest-entry-properties entry)) + (('source ('repository ('version 0) + ('url url) + ('branch branch) + ('commit commit) + _ ...)) + `(channel (name ',(string->symbol + (manifest-entry-name entry))) + (url ,url) + (commit ,commit))) + + ;; Pre-0.15.0 Guix does not provide that information, + ;; so there's not much we can do in that case. + (_ '???))) + + ;; Show most recently installed packages last. + (reverse + (manifest-entries + (profile-manifest (generation-file-name profile + number))))))))) + (display-package-search-path fmt)) + + +;;; +;;; Entry point. +;;; + +(define (guix-describe . args) + (let* ((opts (args-fold* args %options + (lambda (opt name arg result) + (leave (G_ "~A: unrecognized option~%") + name)) + cons + %default-options)) + (format (assq-ref opts 'format))) + (with-error-handling + (match (current-profile) + (#f + (display-checkout-info format)) + (profile + (display-profile-info profile format)))))) diff --git a/guix/scripts/pull.scm b/guix/scripts/pull.scm index ebc5dc9b13..976e054a84 100644 --- a/guix/scripts/pull.scm +++ b/guix/scripts/pull.scm @@ -48,7 +48,8 @@ #:use-module (srfi srfi-37) #:use-module (ice-9 match) #:use-module (ice-9 vlist) - #:export (guix-pull)) + #:export (display-profile-content + guix-pull)) ;;; diff --git a/po/guix/POTFILES.in b/po/guix/POTFILES.in index 7f881355e7..2762ea078a 100644 --- a/po/guix/POTFILES.in +++ b/po/guix/POTFILES.in @@ -31,6 +31,7 @@ guix/scripts/challenge.scm guix/scripts/copy.scm guix/scripts/pack.scm guix/scripts/weather.scm +guix/scripts/describe.scm guix/gnu-maintenance.scm guix/scripts/container.scm guix/scripts/container/exec.scm diff --git a/tests/guix-describe.sh b/tests/guix-describe.sh new file mode 100644 index 0000000000..af523f0a0b --- /dev/null +++ b/tests/guix-describe.sh @@ -0,0 +1,47 @@ +# GNU Guix --- Functional package management for GNU +# Copyright © 2018 Ludovic Courtès +# +# 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 . + +# +# Test 'guix describe'. +# + +guix describe --version + +tmpfile="t-guix-describe-$$" +trap "rm -f $tmpfile" EXIT +rm -f "$tmpfile" + +if [ -d "$abs_top_srcdir/.git" ] +then + # Since we're in a Git checkout, we can at least check that these things + # work. + guix describe | grep -i "checkout" + if git --version > /dev/null 2>&1 + then + result="`guix describe | grep commit: | cut -d : -f 2-`" + commit="`git log | head -1 | cut -c 7-`" + test "x$result" = "x$commit" + fi + guix describe -f channels + case "`guix describe -f channels | grep url`" in + *"(url \"$abs_top_srcdir\")") true;; + *) false;; + esac +else + exit 77 +fi