home: Add fold-home. * home.scm (fold-home): New procedure. (user-home, home-type, home-extension, home-extension-point) (base-home-configuration): New data types. (root-home-type): New variable. * doc: Update every document to the new home-type syntax. * home/hexchat.scm (hexchat-home-type): New variable. * home/keepassxc.scm (keepassxc-home-type): New variable. * home/openbox.scm (openbox-home-type): New variable. * home/ssh.scm (ssh-home-type): New variable. * home/utils.scm (simple-file-home, symlink-file-home): Return a user-home.
doc/README.md
39 | 39 | (home | |
40 | 40 | (data-directory "/data/alice") | |
41 | 41 | (configurations | |
42 | - | (list (something-home ...) | |
43 | - | (something-else-home ...) | |
44 | - | (other-stuff-home ...) | |
45 | - | (yet-another-config-home ...) | |
46 | - | ...))) | |
42 | + | (list | |
43 | + | (user-home something-home-type ...) | |
44 | + | (user-home something-else-home-type ...) | |
45 | + | (user-home other-stuff-home-type ...) | |
46 | + | (user-home yet-another-config-home-type ...) | |
47 | + | ...))) | |
47 | 48 | ``` | |
48 | 49 | ||
49 | 50 | #### Desktop and Window Managers |
doc/general.md
22 | 22 | (simple-file-home (local-file "gitconfig") ".gitconfig") | |
23 | 23 | ``` | |
24 | 24 | ||
25 | - | **Scheme Procedure**: (symlink-file-home from to) | |
25 | + | **Scheme Procedure**: (symlink-file-home to from) | |
26 | 26 | ||
27 | 27 | Create a symlink from the user home at _from_ that points to _to_. For instance, | |
28 | 28 | if you want to create a symlink to a file that can change over time, you can |
doc/hexchat.md
11 | 11 | ------------------ | |
12 | 12 | ||
13 | 13 | The main configuration is not complete compared to the configuration possibilities | |
14 | - | of the client. It currently only implements a very minimal set of configurations | |
14 | + | of the client. It currently only implements a very minimal set of configurations | |
15 | 15 | that are required for it to work properly. | |
16 | 16 | ||
17 | - | **Scheme Procedure**: (hexchat-home config) | |
17 | + | **Scheme Variable**: hexchat-home-type | |
18 | 18 | ||
19 | - | Generates configuration files for hexchat, according to config, an | |
20 | - | hexchat-configuration object. | |
19 | + | The type of the service that generates configuration files for hexchat. Its | |
20 | + | value is an hexchat-configuration object. | |
21 | 21 | ||
22 | 22 | **Data Type**: hexchat-configuration | |
23 | 23 | ||
… | |||
33 | 33 | ------- | |
34 | 34 | ||
35 | 35 | ```scheme | |
36 | - | (hexchat-home | |
37 | - | (hexchat-configuration | |
38 | - | (servlist | |
39 | - | (list | |
40 | - | (hexchat-server | |
41 | - | (name "R??zosup") | |
42 | - | (servers '("irc.rezosup.org/6697")) | |
43 | - | (flags '(cycle global honor-proxy ssl favorite | |
44 | - | allow-invalid-certificates))))) | |
45 | - | (log-dir "/data/alice/.local/share/hexchat/logs") | |
46 | - | (scrollback-dir "/data/alice/.local/share/hexchat/scrollback"))) | |
36 | + | (user-home | |
37 | + | hexchat-home-type | |
38 | + | (hexchat-configuration | |
39 | + | (servlist | |
40 | + | (list | |
41 | + | (hexchat-server | |
42 | + | (name "R??zosup") | |
43 | + | (servers '("irc.rezosup.org/6697")) | |
44 | + | (flags '(cycle global honor-proxy ssl favorite | |
45 | + | allow-invalid-certificates))))) | |
46 | + | (log-dir "/data/alice/.local/share/hexchat/logs") | |
47 | + | (scrollback-dir "/data/alice/.local/share/hexchat/scrollback"))) | |
47 | 48 | ``` | |
48 | 49 | ||
49 | 50 | ||
… | |||
74 | 75 | automatically connect to this network when hexchat is started, _honor-proxy_ | |
75 | 76 | to honor system-wide proxy configuration, _allow-invalid-certificates_ to | |
76 | 77 | allow invalid certificates (self-signed, expired, ...) and _favorite_ to add | |
77 | - | it to the list of favorite networks. | |
77 | > | ||
78 | 0 | > | \ No newline at end of file |
78 | + | it to the list of favorite networks. |
doc/home.md
32 | 32 | (home hexchat)) | |
33 | 33 | ``` | |
34 | 34 | ||
35 | - | **Scheme Procedure**: (home basedir inputs #:guix-symlink #:guix-config-symlink #:local-symlink #:cache-symlink)) | |
35 | + | **Data Type**: home | |
36 | 36 | ||
37 | - | Create a package that can then be installed with `guix package -f` from _inputs_ | |
38 | - | and essential configuration. _inputs_ must be a list of inputs, namely a list | |
39 | - | of file-like objects. The resulting package is a union of all the inputs, with | |
40 | - | the addition of a few essential symlinks. | |
37 | + | This data type represents the configuration of a home directory. It is the | |
38 | + | type of the data a home configuration file must return. It is composed of | |
39 | + | the following fields: | |
41 | 40 | ||
42 | - | By default, when no input is given, and no keyword argument is used, these | |
43 | - | symlinks are created: | |
41 | + | * **data-directory**: The read-write directory that contains the user files. | |
42 | + | * **base-configuration** (default: a base-home-configuration object that takes | |
43 | + | the data-directory value into account, see below): A base-home-configuration | |
44 | + | object that contains configuration for required parts of the home configuration. | |
45 | + | * **configurations** (default: '()): A list of additional configurations. | |
46 | + | ||
47 | + | By default, when no base-configuration is given, the configuration contains | |
48 | + | instructions for these symlinks to be created: | |
44 | 49 | ||
45 | 50 | * `~/.guix-profile` ??? `${basedir}/.guix-profile` | |
46 | 51 | * `~/.config/guix` ??? `${basedir}/.config/guix` | |
47 | 52 | * `~/.local` ??? `${basedir}/.local` | |
48 | 53 | * `~/.cache` ??? `${basedir}/.cache` | |
49 | 54 | ||
50 | - | When the corresponding keyword argument is used, the associated path is used as | |
51 | - | the target of the symbolic link. | |
51 | > | ||
52 | 0 | > | \ No newline at end of file |
55 | + | **Data Type**: base-home-configuration | |
56 | + | ||
57 | + | This is the type of a basic home directory configuration that contains information | |
58 | + | for basic required configurations. It contains the following fields: | |
59 | + | ||
60 | + | * **guix-symlink**: The path to the default guix profile, in a read-write directory. | |
61 | + | * **guix-config-symlink**: The path to the configuration directory of guix. | |
62 | + | * **local-symlink**: The path to a read-write directory that corresponds to the | |
63 | + | .local directory. | |
64 | + | * **cache-symlink**: The path to a read-write directory that corresponds to the | |
65 | + | .cache directory. | |
66 | + | ||
67 | + | **Scheme Variable**: root-home-type | |
68 | + | ||
69 | + | The type of the root service, to which every service is folded. Its value | |
70 | + | is an association list that associates paths relative to the home directory | |
71 | + | with a path in the filesystem or a file-like object. | |
72 | + | ||
73 | + | It also contains a default extension point, whose value must be an association | |
74 | + | list that is concatenated to the already configured association list. |
doc/keepassxc.md
13 | 13 | of keepassxc. It currently implemets most of the configuration, but excludes | |
14 | 14 | a few difficult parts. | |
15 | 15 | ||
16 | - | **Scheme Procedure**: (keepassxc-home config) | |
16 | + | **Scheme Variable**: keepassxc-home-type | |
17 | 17 | ||
18 | - | Generates a configuration file for keepassxc, according to config, a | |
19 | - | keepassxc-configuration object. | |
18 | + | The type of the service that generates a configuration file for keepassxc. Its | |
19 | + | value is a keepassxc-configuration object. | |
20 | 20 | ||
21 | 21 | **Data Type**: keepassxc-configuration | |
22 | 22 | ||
… | |||
55 | 55 | ------- | |
56 | 56 | ||
57 | 57 | ```scheme | |
58 | - | (keepassxc-home | |
58 | + | (user-home | |
59 | + | keepassxc-home-type | |
59 | 60 | (keepassxc-configuration | |
60 | 61 | (last-databases '("/data/alice/pass.kbdx")) | |
61 | 62 | (last-dir "/data/alice") |
doc/openbox.md
8 | 8 | Main Configuration | |
9 | 9 | ------------------ | |
10 | 10 | ||
11 | - | Openbox is configured by using the `openbox-home` procedure. | |
11 | + | Openbox is configured by using the `openbox-home-type` service type. | |
12 | 12 | ||
13 | - | **Scheme Procedure**: (openbox-home config) | |
13 | + | **Scheme Procedure**: openbox-home-type | |
14 | 14 | ||
15 | - | Generates configuration files for openbox, according to config, an | |
16 | - | openbox-configuration object. | |
15 | + | The type of service that generates configuration files for openbox. Its value | |
16 | + | is an openbox-configuration object. | |
17 | 17 | ||
18 | 18 | **Data Type**: openbox-configuration | |
19 | 19 | ||
… | |||
34 | 34 | --------------------- | |
35 | 35 | ||
36 | 36 | ```scheme | |
37 | - | (openbox-home | |
37 | + | (user-home | |
38 | + | openbox-home-type | |
38 | 39 | (openbox-configuration | |
39 | 40 | (autostart (local-file "files/autostart")) | |
40 | 41 | (menus | |
… | |||
97 | 98 | * **label**: a label shown in the parent menu that executes this element | |
98 | 99 | * **command**: the command to execute when the user clicks on this menu element. | |
99 | 100 | It can be a string or a g-expression, as seen in the first example. | |
100 | - | * **notify?** (default #t): whether to use startupnotify | |
100 | > | ||
101 | 0 | > | \ No newline at end of file |
101 | + | * **notify?** (default #t): whether to use startupnotify |
doc/ssh.md
8 | 8 | ------------------ | |
9 | 9 | ||
10 | 10 | The main configuration is not complete compared to the configuration possibilities | |
11 | - | of the client. It however implements a big part of the possibilities. | |
11 | + | of the client. It however implements a big part of the possibilities. | |
12 | 12 | ||
13 | - | **Scheme Procedure**: (ssh-home config) | |
13 | + | **Scheme Variable**: ssh-home-type | |
14 | 14 | ||
15 | - | Generates configuration files for OpenSSH, according to config, an | |
16 | - | ssh-configuration object. | |
15 | + | The type of service that generates configuration files for OpenSSH. Its value | |
16 | + | is an ssh-configuration object. | |
17 | 17 | ||
18 | 18 | **Data Type**: ssh-configuration | |
19 | 19 | ||
… | |||
36 | 36 | ------- | |
37 | 37 | ||
38 | 38 | ```scheme | |
39 | - | (ssh-home | |
39 | + | (user-home | |
40 | + | ssh-home-type | |
40 | 41 | (ssh-configuration | |
41 | 42 | (authorized-keys '("ssh-rsa ... alice@computilo")) | |
42 | 43 | (known-hosts |
home.scm
23 | 23 | #:use-module (guix licenses) | |
24 | 24 | #:use-module (guix packages) | |
25 | 25 | #:use-module (guix records) | |
26 | + | #:use-module (guix ui) | |
27 | + | #:use-module (guix utils) | |
26 | 28 | #:use-module (ice-9 match) | |
29 | + | #:use-module (ice-9 vlist) | |
27 | 30 | #:use-module (home build utils) | |
31 | + | #:use-module (srfi srfi-1) | |
32 | + | #:use-module (srfi srfi-9) | |
33 | + | #:use-module (srfi srfi-35) | |
28 | 34 | #:export (use-home-modules | |
29 | 35 | home | |
30 | 36 | home? | |
… | |||
33 | 39 | home-guix-config-symlink | |
34 | 40 | home-local-symlink | |
35 | 41 | home-cache-symlink | |
36 | - | home->derivation)) | |
42 | + | home->derivation | |
43 | + | ||
44 | + | user-home | |
45 | + | user-home-type | |
46 | + | user-home-value | |
47 | + | ||
48 | + | home-type | |
49 | + | home-type? | |
50 | + | home-type-name | |
51 | + | home-type-extensions | |
52 | + | home-type-extension-points | |
53 | + | home-type-default-value | |
54 | + | home-type-description | |
55 | + | home-type-location | |
56 | + | ||
57 | + | home-extension | |
58 | + | home-extension? | |
59 | + | home-extension-target | |
60 | + | home-extension-target-point | |
61 | + | home-extension-compute | |
62 | + | ||
63 | + | home-extension-point | |
64 | + | home-extension-point? | |
65 | + | home-extension-point-name | |
66 | + | home-extension-point-compose | |
67 | + | home-extension-point-extend | |
68 | + | ||
69 | + | root-home-type)) | |
70 | + | ||
71 | + | ;;; | |
72 | + | ;;; Service-like home configuration mechanism | |
73 | + | ;;; | |
74 | + | ||
75 | + | (define &no-default-value | |
76 | + | '(no default value)) | |
77 | + | ||
78 | + | (define-record-type* <home-extension> home-extension make-home-extension | |
79 | + | home-extension? | |
80 | + | (target home-extension-target ; home-type | |
81 | + | (default #f)) | |
82 | + | (point home-extension-target-point ; symbol | |
83 | + | (default #f)) | |
84 | + | ;; A function that takes the user-home value as parameter and returns the | |
85 | + | ;; value to be passed to the extension point. | |
86 | + | (compute home-extension-compute)) | |
87 | + | ||
88 | + | (define-record-type* <home-extension-point> home-extension-point | |
89 | + | make-home-extension-point home-extension-point? | |
90 | + | (name home-extension-point-name ; symbol | |
91 | + | (default #f)) | |
92 | + | ;; A function that composes extensions. It is passed a list of extension values | |
93 | + | ;; and returns one extension value | |
94 | + | (compose home-extension-point-compose) | |
95 | + | ;; A function that extends the original service value with the result of | |
96 | + | ;; the composition. It is passed two arguments: the user-home value and the | |
97 | + | ;; result of the composition. It returns a new user-home value. | |
98 | + | (extend home-extension-point-extend)) ; any(value) -> any(compose) -> any(value) | |
99 | + | ||
100 | + | (define-record-type* <home-type> home-type make-home-type | |
101 | + | home-type? | |
102 | + | (name home-type-name) ; symbol | |
103 | + | (extensions home-type-extensions ; list home-extension | |
104 | + | (default #f)) | |
105 | + | (extension-points home-type-extension-points ; list home-extension-point | |
106 | + | (default #f)) | |
107 | + | (default-value home-type-default-value ; any | |
108 | + | (default &no-default-value)) | |
109 | + | (description home-type-description ; string | |
110 | + | (default #f)) | |
111 | + | (location home-type-location | |
112 | + | (default (and=> (current-source-location) | |
113 | + | source-properties->location)) | |
114 | + | (innate))) | |
115 | + | ||
116 | + | (define-record-type <user-home> | |
117 | + | (make-user-home type value) | |
118 | + | user-home? | |
119 | + | (type user-home-type) | |
120 | + | (value user-home-value)) | |
121 | + | ||
122 | + | (define-syntax user-home | |
123 | + | (syntax-rules () | |
124 | + | "Return a user-home instance of TYPE. The user-home value is VALUE or, if | |
125 | + | omitted, TYPE's default value." | |
126 | + | ((_ type value) | |
127 | + | (make-user-home type value)) | |
128 | + | ((_ type) | |
129 | + | (%user-home-with-default-value (current-source-location) | |
130 | + | type)))) | |
131 | + | ||
132 | + | (define (%user-home-with-default-value location type) | |
133 | + | "Return an instance of user-home type TYPE with its default value, if any. If | |
134 | + | TYPE does not have a default value, an error is raised." | |
135 | + | ;; TODO: Currently this is a run-time error but with a little bit macrology | |
136 | + | ;; we could turn it into an expansion-time error. | |
137 | + | (let ((default (home-type-default-value type))) | |
138 | + | (if (eq? default &no-default-value) | |
139 | + | (let ((location (source-properties->location location))) | |
140 | + | (raise | |
141 | + | (condition | |
142 | + | (&missing-value-home-error (type type) (location location)) | |
143 | + | (&message | |
144 | + | (message (format #f (G_ "~a: no value specified \ | |
145 | + | for home configuration of type '~a'") | |
146 | + | (location->string location) | |
147 | + | (home-type-name type))))))) | |
148 | + | (user-home type default)))) | |
149 | + | ||
150 | + | (define-condition-type &home-error &error | |
151 | + | home-error?) | |
152 | + | ||
153 | + | (define-condition-type &missing-value-home-error &home-error | |
154 | + | missing-value-home-error? | |
155 | + | (type missing-value-home-error-type) | |
156 | + | (location missing-value-home-error-location)) | |
157 | + | ||
158 | + | (define-condition-type &missing-target-home-error &home-error | |
159 | + | missing-target-home-error? | |
160 | + | (home missing-target-home-error-home) | |
161 | + | (target-type missing-target-home-error-target-type)) | |
162 | + | ||
163 | + | (define-condition-type &ambiguous-target-home-error &home-error | |
164 | + | ambiguous-target-home-error? | |
165 | + | (home missing-target-home-error-home) | |
166 | + | (target-type missing-target-home-error-target-type)) | |
167 | + | ||
168 | + | (define root-home-type | |
169 | + | (home-type | |
170 | + | (name 'root) | |
171 | + | (extension-points | |
172 | + | (list (home-extension-point | |
173 | + | (compose (lambda (l) (apply append l))) | |
174 | + | (extend (lambda (v l) (apply append v l)))))) | |
175 | + | (default-value '()) | |
176 | + | (description "The root home type"))) | |
177 | + | ||
178 | + | ;;; | |
179 | + | ;;; Home composition | |
180 | + | ;;; | |
181 | + | ||
182 | + | (define (missing-target-error home target-type) | |
183 | + | (raise | |
184 | + | (condition (&missing-target-home-error | |
185 | + | (home home) | |
186 | + | (target-type target-type)) | |
187 | + | (&message | |
188 | + | (message | |
189 | + | (format #f (G_ "no target of type '~a' for home configuration '~a'") | |
190 | + | (home-type-name target-type) | |
191 | + | (home-type-name | |
192 | + | (user-home-type home)))))))) | |
193 | + | ||
194 | + | (define (home-back-edges homes) | |
195 | + | "Return a procedure which, when passed a user-home from HOMES, returns the | |
196 | + | list of <user-home> that extend it." | |
197 | + | (define (add-edges home edges) | |
198 | + | (define (add-edge extension edges) | |
199 | + | (define (add-extension-edge target edges) | |
200 | + | (vhash-consq target home edges)) | |
201 | + | (let ((target-type (home-extension-target extension)) | |
202 | + | (target-point (home-extension-target-point extension))) | |
203 | + | (match (filter | |
204 | + | (lambda (home) | |
205 | + | (if target-type | |
206 | + | (eq? (user-home-type home) target-type) | |
207 | + | (and target-point | |
208 | + | (not (null? (filter | |
209 | + | (lambda (extension-point) | |
210 | + | (eq? | |
211 | + | (home-extension-point-name extension-point) | |
212 | + | target-point)) | |
213 | + | (home-type-extension-points | |
214 | + | (user-home-type home)))))))) | |
215 | + | homes) | |
216 | + | ((target ...) | |
217 | + | (fold add-extension-edge edges target)) | |
218 | + | (() | |
219 | + | (missing-target-error home target-type))))) | |
220 | + | (let ((extensions (home-type-extensions (user-home-type home)))) | |
221 | + | (if extensions | |
222 | + | (fold add-edge edges (home-type-extensions (user-home-type home))) | |
223 | + | edges))) | |
224 | + | ||
225 | + | (let ((edges (fold add-edges vlist-null homes))) | |
226 | + | (lambda (node) | |
227 | + | (reverse (vhash-foldq* cons '() node edges))))) | |
228 | + | ||
229 | + | (define (instantiate-missing-user-homes homes) | |
230 | + | "Return HOMES, a list of user-home, augmented with any user-home targeted by | |
231 | + | extensions and missing from HOMES. Only home types with a default value can | |
232 | + | be instantiated; other missing user-homes lead to a '&missing-target-home-error'." | |
233 | + | (let loop ((homes homes)) | |
234 | + | (define (not-present? target) | |
235 | + | (let ((target-type (home-extension-target target))) | |
236 | + | (and target-type | |
237 | + | (null? (filter (lambda (home) | |
238 | + | (eq? (user-home-type home) target-type)) | |
239 | + | homes))))) | |
240 | + | (define additions | |
241 | + | (append-map | |
242 | + | (lambda (home) | |
243 | + | (let ((extensions (home-type-extensions (user-home-type home)))) | |
244 | + | (if extensions | |
245 | + | (map (lambda (extension) (home-extension-target extension)) | |
246 | + | (filter not-present? extensions)) | |
247 | + | '()))) | |
248 | + | homes)) | |
249 | + | ||
250 | + | (define (uniq lst) | |
251 | + | (let loop ((types lst) (result '())) | |
252 | + | (match types | |
253 | + | ((type types ...) | |
254 | + | (if (member type types) | |
255 | + | (loop types result) | |
256 | + | (loop types (cons type result)))) | |
257 | + | (() result)))) | |
258 | + | ||
259 | + | (define generated | |
260 | + | (map | |
261 | + | (lambda (type) | |
262 | + | (let ((default (home-type-default-value type))) | |
263 | + | (if (eq? default &no-default-value) | |
264 | + | (missing-target-error home target-type) | |
265 | + | (user-home type default)))) | |
266 | + | (uniq additions))) | |
267 | + | ||
268 | + | (if (null? generated) | |
269 | + | homes | |
270 | + | (loop (append homes generated))))) | |
271 | + | ||
272 | + | (define (fold-home target-type homes) | |
273 | + | "fold HOMES by successively resolving extension points, until we get only | |
274 | + | one user-home of type TARGET-TYPE." | |
275 | + | (define dependents | |
276 | + | (home-back-edges homes)) | |
277 | + | ||
278 | + | (define (apply-extension target) | |
279 | + | (define (update-extension extensions point home) | |
280 | + | (let* ((point-name (home-extension-point-name point)) | |
281 | + | (matched-extensions | |
282 | + | (filter (lambda (extension) | |
283 | + | (if point-name | |
284 | + | (eq? (home-extension-target-point extension) | |
285 | + | point-name) | |
286 | + | (eq? (home-extension-target extension) | |
287 | + | (user-home-type target)))) | |
288 | + | (home-type-extensions (user-home-type home))))) | |
289 | + | (if (null? matched-extensions) | |
290 | + | extensions | |
291 | + | (append | |
292 | + | (map | |
293 | + | (lambda (extension) | |
294 | + | `(,point-name ,((home-extension-compute extension) | |
295 | + | (user-home-value home)))) | |
296 | + | matched-extensions) | |
297 | + | extensions)))) | |
298 | + | (lambda (home) | |
299 | + | (let loop ((extensions '()) (points (home-type-extension-points | |
300 | + | (user-home-type target)))) | |
301 | + | (match points | |
302 | + | (() extensions) | |
303 | + | ((point points ...) | |
304 | + | (loop | |
305 | + | (update-extension extensions point home) | |
306 | + | points)))))) | |
307 | + | ||
308 | + | (define (merge-extensions points extensions) | |
309 | + | (let loop ((points points) (extensions extensions)) | |
310 | + | (match points | |
311 | + | (() extensions) | |
312 | + | (((point value) points ...) | |
313 | + | (loop points (acons point | |
314 | + | (let ((v (assoc-ref extensions point))) | |
315 | + | (if v (cons value v) (list value))) | |
316 | + | extensions)))))) | |
317 | + | ||
318 | + | (define (compose-extension target) | |
319 | + | (lambda (extension) | |
320 | + | (match extension | |
321 | + | ((point-name values ...) | |
322 | + | (match (filter | |
323 | + | (lambda (extension-point) | |
324 | + | (eq? (home-extension-point-name extension-point) point-name)) | |
325 | + | (home-type-extension-points (user-home-type target))) | |
326 | + | ((point) | |
327 | + | (list | |
328 | + | point-name | |
329 | + | ((home-extension-point-compose point) | |
330 | + | values)))))))) | |
331 | + | ||
332 | + | (match (filter (lambda (home) | |
333 | + | (eq? (user-home-type home) target-type)) | |
334 | + | homes) | |
335 | + | ((sink) | |
336 | + | (let loop ((sink sink)) | |
337 | + | (let* ((dependents (map loop (dependents sink))) | |
338 | + | (extensions (map (apply-extension sink) dependents)) | |
339 | + | (extensions (fold merge-extensions '() extensions)) | |
340 | + | (extensions (map (compose-extension sink) extensions))) | |
341 | + | (user-home | |
342 | + | (user-home-type sink) | |
343 | + | (let ((extension-points (home-type-extension-points (user-home-type sink)))) | |
344 | + | (if extension-points | |
345 | + | (fold (lambda (extension-point value) | |
346 | + | (let* ((extend (home-extension-point-extend extension-point)) | |
347 | + | (name (home-extension-point-name extension-point)) | |
348 | + | (extension-value (assoc-ref extensions name))) | |
349 | + | (if extension-value | |
350 | + | (extend value extension-value) | |
351 | + | value))) | |
352 | + | (user-home-value sink) | |
353 | + | (home-type-extension-points (user-home-type sink))) | |
354 | + | (user-home-value sink))))))) | |
355 | + | (() | |
356 | + | (raise | |
357 | + | (condition (&missing-target-home-error | |
358 | + | (home #f) | |
359 | + | (target-type target-type)) | |
360 | + | (&message | |
361 | + | (message (format #f (G_ "home configuration of type '~a' not found") | |
362 | + | (home-type-name target-type))))))) | |
363 | + | (x | |
364 | + | (raise | |
365 | + | (condition (&ambiguous-target-home-error | |
366 | + | (home #f) | |
367 | + | (target-type target-type)) | |
368 | + | (&message | |
369 | + | (message (format #f (G_ "home configuration of type '~a' not found") | |
370 | + | (home-type-name target-type))))))))) | |
371 | + | ||
372 | + | ;;; | |
373 | + | ;;; Home generation | |
374 | + | ;;; | |
37 | 375 | ||
38 | 376 | (define-syntax use-home-modules | |
39 | 377 | (syntax-rules () | |
… | |||
44 | 382 | make-home | |
45 | 383 | home? | |
46 | 384 | (data-directory home-data-directory) | |
47 | - | (guix-symlink home-guix-symlink (thunked) | |
48 | - | (default (string-append | |
49 | - | (home-data-directory this-record) | |
50 | - | "/.guix-profile"))) | |
51 | - | (guix-config-symlink home-guix-config-symlink (thunked) | |
52 | - | (default (string-append | |
53 | - | (home-data-directory this-record) | |
54 | - | "/.config/guix"))) | |
55 | - | (local-symlink home-local-symlink (thunked) | |
56 | - | (default (string-append | |
57 | - | (home-data-directory this-record) | |
58 | - | "/.local"))) | |
59 | - | (cache-symlink home-cache-symlink (thunked) | |
60 | - | (default (string-append | |
61 | - | (home-data-directory this-record) | |
62 | - | "/.cache"))) | |
385 | + | (base-configuration home-base-configuration (thunked) | |
386 | + | (default | |
387 | + | (base-home-configuration | |
388 | + | (guix-symlink | |
389 | + | (string-append (home-data-directory this-record) "/.guix-profile")) | |
390 | + | (guix-config-symlink | |
391 | + | (string-append (home-data-directory this-record) "/.config/guix")) | |
392 | + | (local-symlink | |
393 | + | (string-append (home-data-directory this-record) "/.local")) | |
394 | + | (cache-symlink | |
395 | + | (string-append (home-data-directory this-record) "/.cache"))))) | |
63 | 396 | (configurations home-configurations | |
64 | - | (default '()))) | |
397 | + | (default (list (user-home root-home-type))))) | |
398 | + | ||
399 | + | (define-record-type* <base-home-configuration> base-home-configuration | |
400 | + | make-base-home-configuration | |
401 | + | base-home-configuration? | |
402 | + | (guix-symlink base-home-configuration-guix-symlink) | |
403 | + | (guix-config-symlink base-home-configuration-guix-config-symlink) | |
404 | + | (local-symlink base-home-configuration-local-symlink) | |
405 | + | (cache-symlink base-home-configuration-cache-symlink)) | |
406 | + | ||
407 | + | (define base-home-type | |
408 | + | (home-type | |
409 | + | (name 'guix) | |
410 | + | (extensions | |
411 | + | (list | |
412 | + | (home-extension | |
413 | + | (target root-home-type) | |
414 | + | (compute (lambda (config) | |
415 | + | `((".guix-profile" | |
416 | + | ,(base-home-configuration-guix-symlink config)) | |
417 | + | (".config/guix" | |
418 | + | ,(base-home-configuration-guix-config-symlink config)) | |
419 | + | (".local" | |
420 | + | ,(base-home-configuration-local-symlink config)) | |
421 | + | (".cache" | |
422 | + | ,(base-home-configuration-cache-symlink config)))))))))) | |
65 | 423 | ||
66 | 424 | (define (home->derivation home) | |
67 | 425 | (define builder | |
68 | 426 | (with-imported-modules | |
69 | - | '((guix build utils) (home build utils)) | |
427 | + | '((guix build utils) (home build utils) (ice-9 match)) | |
70 | 428 | #~(begin | |
71 | - | (use-modules (guix build utils) (home build utils)) | |
72 | - | (mkdir-p (home-file #$output ".config")) | |
73 | - | ;; For guix | |
74 | - | (symlink #$(home-guix-config-symlink home) (home-file #$output ".config/guix")) | |
75 | - | (symlink #$(home-guix-symlink home) (home-file #$output ".guix-profile")) | |
76 | - | ;; symlink writeable directories | |
77 | - | (symlink #$(home-local-symlink home) (home-file #$output ".local")) | |
78 | - | (symlink #$(home-cache-symlink home) (home-file #$output ".cache")) | |
79 | - | ;; rest of the files | |
80 | - | (for-each | |
81 | - | (lambda (config) | |
82 | - | (with-directory-excursion config | |
83 | - | (for-each | |
84 | - | (lambda (f) | |
85 | - | (mkdir-p (home-file #$output (dirname f))) | |
86 | - | (symlink (home-file config f) | |
87 | - | (home-file #$output f))) | |
88 | - | (find-files "." ".")))) | |
89 | - | (list #$@(home-configurations home)))))) | |
429 | + | (use-modules (guix build utils) (home build utils) (ice-9 match)) | |
430 | + | #$(let* ((homes (instantiate-missing-user-homes | |
431 | + | (cons | |
432 | + | (user-home base-home-type (home-base-configuration home)) | |
433 | + | (home-configurations home)))) | |
434 | + | (root (fold-home root-home-type homes)) | |
435 | + | (configs (user-home-value root))) | |
436 | + | #~(for-each | |
437 | + | (lambda (config) | |
438 | + | (match config | |
439 | + | ((home-name target) | |
440 | + | (mkdir-p (dirname (home-file #$output home-name))) | |
441 | + | (symlink target (home-file #$output home-name))))) | |
442 | + | '(#$@configs)))))) | |
90 | 443 | (gexp->derivation "home" builder | |
91 | 444 | #:substitutable? #f | |
92 | 445 | #:local-build? #t)) |
home/hexchat.scm
21 | 21 | #:use-module (guix records) | |
22 | 22 | #:use-module (gnu packages lxde) | |
23 | 23 | #:use-module (ice-9 match) | |
24 | + | #:use-module (home) | |
24 | 25 | #:export (hexchat-configuration | |
25 | 26 | hexchat-configuration-servlist | |
26 | 27 | hexchat-configuration-log-dir | |
… | |||
34 | 35 | hexchat-server-servers | |
35 | 36 | hexchat-server-flags | |
36 | 37 | ||
37 | - | hexchat-home)) | |
38 | + | hexchat-home-type)) | |
38 | 39 | ||
39 | 40 | (define-record-type* <hexchat-server> | |
40 | 41 | hexchat-server make-hexchat-server | |
… | |||
100 | 101 | (log-dir hexchat-configuration-log-dir) | |
101 | 102 | (scrollback-dir hexchat-configuraton-scrollback-dir)) | |
102 | 103 | ||
103 | - | (define (hexchat-home config) | |
104 | - | (computed-file "hexchat-home" | |
105 | - | (match config | |
106 | - | (($ <hexchat-configuration> servlist log-dir scrollback-dir) | |
107 | - | #~(let ((servlist.conf #$(plain-file "servlist.conf" (generate-hexchat-servlist servlist))) | |
108 | - | (hexchat-dir (string-append #$output "/.config/hexchat"))) | |
109 | - | (use-modules (guix build utils)) | |
110 | - | (mkdir-p hexchat-dir) | |
111 | - | (symlink #$log-dir (string-append hexchat-dir "/logs")) | |
112 | - | (symlink #$scrollback-dir (string-append hexchat-dir "/scrollback")) | |
113 | - | (copy-file servlist.conf (string-append hexchat-dir "/servlist.conf"))))) | |
114 | - | #:options | |
115 | - | '(#:local-build? #t | |
116 | - | #:modules ((guix build utils))))) | |
116 | > | ||
117 | 0 | > | \ No newline at end of file |
104 | + | (define hexchat-home-type | |
105 | + | (home-type | |
106 | + | (name 'hexchat) | |
107 | + | (extensions | |
108 | + | (list | |
109 | + | (home-extension | |
110 | + | (target root-home-type) | |
111 | + | (compute | |
112 | + | (lambda (config) | |
113 | + | (match config | |
114 | + | (($ <hexchat-configuration> servlist log-dir scrollback-dir) | |
115 | + | (let ((servlist.conf (plain-file "servlist.conf" (generate-hexchat-servlist servlist)))) | |
116 | + | `((".config/hexchat/servlist.conf" ,servlist.conf) | |
117 | + | (".config/hexchat/logs" ,log-dir) | |
118 | + | (".config/hexchat/scrollback" ,scrollback-dir)))))))))))) |
home/keepassxc.scm
22 | 22 | #:use-module (gnu packages lxde) | |
23 | 23 | #:use-module (ice-9 match) | |
24 | 24 | #:use-module (home utils) | |
25 | + | #:use-module (home) | |
25 | 26 | #:export (keepassxc-configuration | |
26 | 27 | keepassxc-last-databases | |
27 | 28 | keepassxc-last-dir | |
… | |||
42 | 43 | keepassxc-wordlist | |
43 | 44 | keepassxc-word-separator | |
44 | 45 | ||
45 | - | keepassxc-home)) | |
46 | + | keepassxc-home-type)) | |
46 | 47 | ||
47 | 48 | (define-record-type* <keepassxc-configuration> | |
48 | 49 | keepassxc-configuration make-keepassxc-configuration | |
… | |||
84 | 85 | (word-separator keepassxc-word-separator | |
85 | 86 | (default " "))) | |
86 | 87 | ||
87 | - | (define (keepassxc-home config) | |
88 | - | (computed-file "keepassxc-home" | |
89 | - | (let ((keepassxc.ini (make-ini-file "keepassxc.ini" | |
90 | - | `(("General" | |
91 | - | (("LastDatabases" | |
92 | - | ,(string-join | |
93 | - | (keepassxc-last-databases config) | |
94 | - | ", ")) | |
95 | - | ("LastDir" | |
96 | - | ,(or (keepassxc-last-dir config) "")) | |
97 | - | ("LastOpenedDatabases" | |
98 | - | ,(string-join | |
99 | - | (keepassxc-last-opened-databases config) | |
100 | - | ", ")))) | |
101 | - | ("GUI" | |
102 | - | (("HidePasswords" | |
103 | - | ,(keepassxc-hide-passwords? config)) | |
104 | - | ("HideUsernames" | |
105 | - | ,(keepassxc-hide-usernames? config)))) | |
106 | - | ("generator" | |
107 | - | (("EASCII" | |
108 | - | ,(keepassxc-use-eascii? config)) | |
109 | - | ("EnsureEvery" | |
110 | - | ,(keepassxc-ensure-every? config)) | |
111 | - | ("ExcludeAlike" | |
112 | - | ,(keepassxc-exclude-alike? config)) | |
113 | - | ("Length" | |
114 | - | ,(keepassxc-password-length config)) | |
115 | - | ("LowerCase" | |
116 | - | ,(keepassxc-use-lower-case? config)) | |
117 | - | ("Numbers" | |
118 | - | ,(keepassxc-use-numbers? config)) | |
119 | - | ("SpecialChars" | |
120 | - | ,(keepassxc-use-special-chars? config)) | |
121 | - | ("Type" | |
122 | - | ,(keepassxc-type config)) | |
123 | - | ("UpperCase" | |
124 | - | ,(keepassxc-use-upper-case? config)) | |
125 | - | ("WordCount" | |
126 | - | ,(keepassxc-word-count config)) | |
127 | - | ("WordList" | |
128 | - | ,(keepassxc-wordlist config)) | |
129 | - | ("WordSeparator" | |
130 | - | ,(keepassxc-word-separator config)))))))) | |
131 | - | #~(begin | |
132 | - | (let ((keepassxc-dir (string-append #$output "/.config/keepassxc"))) | |
133 | - | (use-modules (guix build utils)) | |
134 | - | (mkdir-p keepassxc-dir) | |
135 | - | (copy-file #$keepassxc.ini (string-append keepassxc-dir "/keepassxc.ini"))))) | |
136 | - | #:options | |
137 | - | '(#:local-build? #t | |
138 | - | #:modules ((guix build utils))))) | |
88 | + | (define keepassxc-home-type | |
89 | + | (home-type | |
90 | + | (name 'keepassxc) | |
91 | + | (extensions | |
92 | + | (list | |
93 | + | (home-extension | |
94 | + | (target root-home-type) | |
95 | + | (compute (lambda (config) | |
96 | + | (let ((keepassxc.ini | |
97 | + | (make-ini-file "keepassxc.ini" | |
98 | + | `(("General" | |
99 | + | (("LastDatabases" | |
100 | + | ,(string-join | |
101 | + | (keepassxc-last-databases config) | |
102 | + | ", ")) | |
103 | + | ("LastDir" | |
104 | + | ,(or (keepassxc-last-dir config) "")) | |
105 | + | ("LastOpenedDatabases" | |
106 | + | ,(string-join | |
107 | + | (keepassxc-last-opened-databases config) | |
108 | + | ", ")))) | |
109 | + | ("GUI" | |
110 | + | (("HidePasswords" | |
111 | + | ,(keepassxc-hide-passwords? config)) | |
112 | + | ("HideUsernames" | |
113 | + | ,(keepassxc-hide-usernames? config)))) | |
114 | + | ("generator" | |
115 | + | (("EASCII" | |
116 | + | ,(keepassxc-use-eascii? config)) | |
117 | + | ("EnsureEvery" | |
118 | + | ,(keepassxc-ensure-every? config)) | |
119 | + | ("ExcludeAlike" | |
120 | + | ,(keepassxc-exclude-alike? config)) | |
121 | + | ("Length" | |
122 | + | ,(keepassxc-password-length config)) | |
123 | + | ("LowerCase" | |
124 | + | ,(keepassxc-use-lower-case? config)) | |
125 | + | ("Numbers" | |
126 | + | ,(keepassxc-use-numbers? config)) | |
127 | + | ("SpecialChars" | |
128 | + | ,(keepassxc-use-special-chars? config)) | |
129 | + | ("Type" | |
130 | + | ,(keepassxc-type config)) | |
131 | + | ("UpperCase" | |
132 | + | ,(keepassxc-use-upper-case? config)) | |
133 | + | ("WordCount" | |
134 | + | ,(keepassxc-word-count config)) | |
135 | + | ("WordList" | |
136 | + | ,(keepassxc-wordlist config)) | |
137 | + | ("WordSeparator" | |
138 | + | ,(keepassxc-word-separator config)))))))) | |
139 | + | `((".config/keepassxc/keepassxc.ini" ,keepassxc.ini)))))))))) |
home/openbox.scm
23 | 23 | #:use-module (guix packages) | |
24 | 24 | #:use-module (guix records) | |
25 | 25 | #:use-module (gnu packages lxde) | |
26 | + | #:use-module (home) | |
26 | 27 | #:use-module (ice-9 match) | |
27 | 28 | #:export (openbox-configuration | |
28 | 29 | openbox-configuration? | |
… | |||
39 | 40 | openbox-element-menu | |
40 | 41 | openbox-element-execute | |
41 | 42 | ||
42 | - | openbox-home)) | |
43 | + | openbox-home-type)) | |
43 | 44 | ||
44 | 45 | (define-record-type* <openbox-configuration> | |
45 | 46 | openbox-configuration make-openbox-configuration | |
… | |||
114 | 115 | (#f (plain-file "rc.xml" "")) | |
115 | 116 | (_ rc))) | |
116 | 117 | ||
117 | - | (define (openbox-home config) | |
118 | - | (computed-file "openbox-home" | |
119 | - | (match config | |
120 | - | (($ <openbox-configuration> autostart environ menus root-elements rc) | |
121 | - | #~(let ((menu.xml #$(computed-file "menu.xml" (generate-openbox-menu menus root-elements))) | |
122 | - | (autostart #$(openbox-autostart autostart)) | |
123 | - | (environ #$(openbox-environment environ)) | |
124 | - | (rc #$(openbox-rc rc)) | |
125 | - | (openbox-dir (string-append #$output "/.config/openbox"))) | |
126 | - | (use-modules (guix build utils)) | |
127 | - | (mkdir-p openbox-dir) | |
128 | - | (copy-file autostart (string-append openbox-dir "/autostart")) | |
129 | - | (copy-file environ (string-append openbox-dir "/environment")) | |
130 | - | (copy-file menu.xml (string-append openbox-dir "/menu.xml")) | |
131 | - | (copy-file rc (string-append openbox-dir "/rc.xml"))))) | |
132 | - | #:options | |
133 | - | '(#:local-build? #t | |
134 | - | #:modules ((guix build utils))))) | |
134 | > | ||
135 | 0 | > | \ No newline at end of file |
118 | + | (define openbox-home-type | |
119 | + | (home-type | |
120 | + | (name 'openbox) | |
121 | + | (default-value (openbox-configuration)) | |
122 | + | (extensions | |
123 | + | (list | |
124 | + | (home-extension | |
125 | + | (target root-home-type) | |
126 | + | (compute | |
127 | + | (lambda (config) | |
128 | + | (match config | |
129 | + | (($ <openbox-configuration> autostart environ menus root-elements rc) | |
130 | + | `((".config/openbox/menu.xml" | |
131 | + | ,(computed-file "menu.xml" (generate-openbox-menu menus root-elements))) | |
132 | + | (".config/openbox/autostart" ,(openbox-autostart autostart)) | |
133 | + | (".config/openbox/environment" ,(openbox-environment environ)) | |
134 | + | (".config/openbox/rc.xml" ,(openbox-rc rc)))))))))))) |
home/ssh.scm
21 | 21 | #:use-module (guix records) | |
22 | 22 | #:use-module (gnu packages lxde) | |
23 | 23 | #:use-module (ice-9 match) | |
24 | + | #:use-module (home) | |
24 | 25 | #:export (ssh-configuration | |
25 | 26 | ssh-configuration-authorized-keys | |
26 | 27 | ssh-configuration-known-hosts | |
… | |||
39 | 40 | ssh-known-host-configuration-algo | |
40 | 41 | ssh-known-host-configuration-key | |
41 | 42 | ||
42 | - | ssh-home)) | |
43 | + | ssh-home-type)) | |
43 | 44 | ||
44 | 45 | (define-record-type* <ssh-host-configuration> | |
45 | 46 | ssh-host-configuration make-ssh-host-configuration | |
… | |||
129 | 130 | (define (generate-ssh-known-hosts lst) | |
130 | 131 | (string-join (map known-host lst) "\n")) | |
131 | 132 | ||
132 | - | (define (ssh-home config) | |
133 | - | (computed-file "ssh-home" | |
134 | - | (match config | |
135 | - | (($ <ssh-configuration> authorized-keys known-hosts hosts default-host) | |
136 | - | #~(let ((config #$(plain-file "config" (generate-ssh-config hosts default-host))) | |
137 | - | (known-hosts #$(plain-file "known_hosts" | |
138 | - | (generate-ssh-known-hosts known-hosts))) | |
139 | - | (authorized-keys #$(plain-file | |
140 | - | "authorized_keys" | |
141 | - | (generate-ssh-authorized-keys authorized-keys))) | |
142 | - | (ssh-dir (string-append #$output "/.ssh"))) | |
143 | - | (use-modules (guix build utils)) | |
144 | - | (mkdir-p ssh-dir) | |
145 | - | (copy-file authorized-keys (string-append ssh-dir "/authorized_keys")) | |
146 | - | (copy-file known-hosts (string-append ssh-dir "/known_hosts")) | |
147 | - | (copy-file config (string-append ssh-dir "/config"))))) | |
148 | - | #:options | |
149 | - | '(#:local-build? #t | |
150 | - | #:modules ((guix build utils))))) | |
133 | + | (define ssh-home-type | |
134 | + | (home-type | |
135 | + | (name 'ssh) | |
136 | + | (extensions | |
137 | + | (list | |
138 | + | (home-extension | |
139 | + | (target root-home-type) | |
140 | + | (compute (lambda (config) | |
141 | + | (match config | |
142 | + | (($ <ssh-configuration> authorized-keys known-hosts hosts default-host) | |
143 | + | (let ((config (plain-file "config" (generate-ssh-config hosts default-host))) | |
144 | + | (known-hosts (plain-file "known_hosts" | |
145 | + | (generate-ssh-known-hosts known-hosts))) | |
146 | + | (authorized-keys (plain-file "authorized_keys" | |
147 | + | (generate-ssh-authorized-keys authorized-keys)))) | |
148 | + | `((".ssh/authorized_keys" ,authorized-keys) (".ssh/known_hosts" ,known-hosts) | |
149 | + | (".ssh/config" ,config)))))))))))) |
home/utils.scm
19 | 19 | #:use-module (guix build utils) | |
20 | 20 | #:use-module (guix gexp) | |
21 | 21 | #:use-module (ice-9 match) | |
22 | + | #:use-module (home) | |
22 | 23 | #:export (simple-file-home | |
23 | 24 | symlink-file-home | |
24 | 25 | make-ini-file)) | |
25 | 26 | ||
26 | 27 | (define (simple-file-home file-gexp location) | |
27 | - | "Add @var{file-gexp}, a file-like object, to the user home at @var{location}." | |
28 | - | (computed-file "simple-file-home" | |
29 | - | #~(begin | |
30 | - | (use-modules (guix build utils)) | |
31 | - | (mkdir-p (string-append #$output "/" #$(dirname location))) | |
32 | - | (copy-file #$file-gexp (string-append #$output "/" #$location))) | |
33 | - | #:options | |
34 | - | '(#:local-build? #t | |
35 | - | #:modules ((guix build utils))))) | |
28 | + | (user-home | |
29 | + | (home-type | |
30 | + | (name 'simple-file) | |
31 | + | (extensions | |
32 | + | (list | |
33 | + | (home-extension | |
34 | + | (target root-home-type) | |
35 | + | (compute | |
36 | + | (const `((,location ,file-gexp)))))))) | |
37 | + | #t)) | |
36 | 38 | ||
37 | - | (define (symlink-file-home from to) | |
39 | + | (define (symlink-file-home to from) | |
38 | 40 | "Create a symlink from the user home at @var{from} that points to @var{to}." | |
39 | - | (computed-file "symlink-file-home" | |
40 | - | #~(begin | |
41 | - | (use-modules (guix build utils)) | |
42 | - | (mkdir-p (string-append #$output "/" #$(dirname to))) | |
43 | - | (symlink #$from (string-append #$output "/" #$to))) | |
44 | - | #:options | |
45 | - | '(#:local-build? #t | |
46 | - | #:modules ((guix build utils))))) | |
41 | + | (user-home | |
42 | + | (home-type | |
43 | + | (name 'symlink-file) | |
44 | + | (extensions | |
45 | + | (list | |
46 | + | (home-extension | |
47 | + | (target root-home-type) | |
48 | + | (compute | |
49 | + | (const `((,from ,to)))))))) | |
50 | + | #t)) | |
47 | 51 | ||
48 | 52 | (define (make-ini-file name config) | |
49 | 53 | (define (make-ini-section name config) |