provenance.scm
1 | ;;; Guix Home Manager. |
2 | ;;; |
3 | ;;; Copyright © 2015, 2016, 2017, 2018, 2019, 2020 Ludovic Courtès <ludo@gnu.org> |
4 | ;;; Copyright © 2016 Chris Marusich <cmmarusich@gmail.com> |
5 | ;;; Copyright © 2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org> |
6 | ;;; Copyright © 2020 Jelle Licht <jlicht@fsfe.org> |
7 | ;;; |
8 | ;;; This file is based on work found in GNU Guix. |
9 | ;;; |
10 | ;;; This program is free software: you can redistribute it and/or modify |
11 | ;;; it under the terms of the GNU General Public License as published by |
12 | ;;; the Free Software Foundation, either version 3 of the License, or |
13 | ;;; (at your option) any later version. |
14 | ;;; |
15 | ;;; This program is distributed in the hope that it will be useful, |
16 | ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
18 | ;;; GNU General Public License for more details. |
19 | ;;; |
20 | ;;; You should have received a copy of the GNU General Public License |
21 | ;;; along with this program. If not, see <http://www.gnu.org/licenses/>. |
22 | |
23 | (define-module (home provenance) |
24 | #:use-module (gnu services) |
25 | #:use-module (guix channels) |
26 | #:use-module (guix describe) |
27 | #:use-module (guix diagnostics) |
28 | #:autoload (guix openpgp) (openpgp-format-fingerprint) |
29 | #:use-module (guix gexp) |
30 | #:use-module (guix monads) |
31 | #:use-module (guix profiles) |
32 | #:use-module (guix records) |
33 | #:use-module (guix store) |
34 | #:use-module (home) |
35 | #:autoload (ice-9 pretty-print) (pretty-print) |
36 | #:export (provenance-home-type |
37 | home-with-provenance)) |
38 | |
39 | (define (object->pretty-string obj) |
40 | "Like 'object->string', but using 'pretty-print'." |
41 | (call-with-output-string |
42 | (lambda (port) |
43 | (pretty-print obj port)))) |
44 | |
45 | (define (channel->code channel) |
46 | "Return code to build CHANNEL, ready to be dropped in a 'channels.scm' |
47 | file." |
48 | ;; Since the 'introduction' field is backward-incompatible, and since it's |
49 | ;; optional when using the "official" 'guix channel, include it if and only |
50 | ;; if we're referring to a different channel. |
51 | (let ((intro (and (not (equal? (list channel) %default-channels)) |
52 | (channel-introduction channel)))) |
53 | `(channel (name ',(channel-name channel)) |
54 | (url ,(channel-url channel)) |
55 | (branch ,(channel-branch channel)) |
56 | (commit ,(channel-commit channel)) |
57 | ,@(if intro |
58 | `((introduction |
59 | (make-channel-introduction |
60 | ,(channel-introduction-first-signed-commit intro) |
61 | (openpgp-fingerprint |
62 | ,(openpgp-format-fingerprint |
63 | (channel-introduction-first-commit-signer |
64 | intro)))))) |
65 | '())))) |
66 | |
67 | (define (channel->sexp channel) |
68 | "Return an sexp describing CHANNEL. The sexp is _not_ code and is meant to |
69 | be parsed by tools; it's potentially more future-proof than code." |
70 | ;; TODO: Add CHANNEL's introduction. Currently we can't do that because |
71 | ;; older 'guix system describe' expect exactly name/url/branch/commit |
72 | ;; without any additional fields. |
73 | `(channel (name ,(channel-name channel)) |
74 | (url ,(channel-url channel)) |
75 | (branch ,(channel-branch channel)) |
76 | (commit ,(channel-commit channel)))) |
77 | |
78 | (define (provenance-file channels configuration-file) |
79 | "Return a 'provenance' file describing CHANNELS, a list of channels, and |
80 | CONFIGURATION-FILE, which can be either #f or a <local-file> containing the OS |
81 | configuration being used." |
82 | (scheme-file "provenance" |
83 | #~(provenance |
84 | (version 0) |
85 | (channels #+@(if channels |
86 | (map channel->sexp channels) |
87 | '())) |
88 | (configuration-file #+configuration-file)))) |
89 | |
90 | (define (provenance-entry config-file) |
91 | "Return system entries describing the operating system provenance: the |
92 | channels in use and CONFIG-FILE, if it is true." |
93 | (define profile |
94 | (current-profile)) |
95 | |
96 | (define channels |
97 | (and=> profile profile-channels)) |
98 | |
99 | (let ((config-file (cond ((string? config-file) |
100 | (local-file config-file "home.scm")) |
101 | ((not config-file) |
102 | #f) |
103 | (else |
104 | config-file)))) |
105 | `((".provenance" ,(provenance-file channels config-file)) |
106 | ,@(if channels |
107 | `((".channels.scm" |
108 | ,(plain-file "channels.scm" |
109 | (object->pretty-string |
110 | `(list |
111 | ,@(map channel->code channels)))))) |
112 | '()) |
113 | ,@(if config-file |
114 | `((".home.scm" ,config-file)) |
115 | '())))) |
116 | |
117 | (define provenance-home-type |
118 | (home-type |
119 | (name 'provenance) |
120 | (extensions |
121 | (list |
122 | (home-extension |
123 | (target root-home-type) |
124 | (compute provenance-entry)))) |
125 | (description "The provenance home type"))) |
126 | |
127 | (define (home-configuration-file h) |
128 | "Return the configuration file of H, based on its 'location' field, or #f |
129 | if it could not be determined." |
130 | (let ((file (and=> (home-location h) |
131 | location-file))) |
132 | (and file |
133 | (or (and (string-prefix? "/" file) file) |
134 | (search-path %load-path file))))) |
135 | |
136 | (define* (home-with-provenance h |
137 | #:optional |
138 | (configuration-file |
139 | (home-configuration-file h))) |
140 | "Return a variant of H that stores its own provenance information, |
141 | including CONFIGURATION-FILE, if available. This is achieved by adding an |
142 | instance of PROVENANCE-HOME-TYPE to its configurations." |
143 | (home |
144 | (inherit h) |
145 | (configurations (cons (user-home provenance-home-type configuration-file) |
146 | (home-configurations h))))) |
147 |