summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGiacomo Leidi <goodoldpaul@autistici.org>2024-03-06 21:52:42 +0100
committerLudovic Courtès <ludo@gnu.org>2024-03-06 23:25:51 +0100
commit01f685d56016ae529381a73daedccc1949b808ec (patch)
tree61935a2682dcfa69ae4929acd7c896ca6433b4f2
parent9e3061a163abcae5ccd3823ba3d8f757a0478e40 (diff)
gnu: home: dotfiles: Properly support both plain and Stow directory layouts.
Fixes <https://issues.guix.gnu.org/68848>. The current implementation of the home-dotfiles-service-type contradicts the Guix manual. This patch properly implements both the plain and Stow dotfiles directory layouts. It does so by refactoring home-dotfiles-configuration adding a new packages field to support GNU Stow's users workflow and introducing a new layout field to switch between the two directory layouts. * gnu/home/services/dotfiles (home-dotfiles-configuration): Migrate to (gnu services configuration); [packages]: new field; [layout]: new field; (strip-stow-dotfile): new variable; (strip-plain-dotfile): new variable; (home-dotfiles-configuration->files): use the new fields; [directory-contents]: allow for selecting a subset of application dotfile directories; * doc/guix.texi: document the new layouts. Change-Id: I2e96037608353e360828290f055ec5271cfdfd48 Signed-off-by: Ludovic Courtès <ludo@gnu.org>
-rw-r--r--doc/guix.texi84
-rw-r--r--gnu/home/services/dotfiles.scm115
2 files changed, 151 insertions, 48 deletions
diff --git a/doc/guix.texi b/doc/guix.texi
index 176c92971fc..858d5751bfe 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -111,7 +111,7 @@ Copyright @copyright{} 2022 (@*
111Copyright @copyright{} 2022 John Kehayias@* 111Copyright @copyright{} 2022 John Kehayias@*
112Copyright @copyright{} 2022⁠–⁠2023 Bruno Victal@* 112Copyright @copyright{} 2022⁠–⁠2023 Bruno Victal@*
113Copyright @copyright{} 2022 Ivan Vilata-i-Balaguer@* 113Copyright @copyright{} 2022 Ivan Vilata-i-Balaguer@*
114Copyright @copyright{} 2023 Giacomo Leidi@* 114Copyright @copyright{} 2023-2024 Giacomo Leidi@*
115Copyright @copyright{} 2022 Antero Mejr@* 115Copyright @copyright{} 2022 Antero Mejr@*
116Copyright @copyright{} 2023 Karl Hallsby@* 116Copyright @copyright{} 2023 Karl Hallsby@*
117Copyright @copyright{} 2023 Nathaniel Nicandro@* 117Copyright @copyright{} 2023 Nathaniel Nicandro@*
@@ -44667,17 +44667,42 @@ directory, and some way of automatically deploy changes to their user home.
44667@cindex Stow-like dot file management 44667@cindex Stow-like dot file management
44668The @code{home-dotfiles-service-type} from @code{(gnu home services dotfiles)} 44668The @code{home-dotfiles-service-type} from @code{(gnu home services dotfiles)}
44669is designed to ease the way into using Guix Home for this kind of users, 44669is designed to ease the way into using Guix Home for this kind of users,
44670allowing them to point the service to their dotfiles directory, which must 44670allowing them to point the service to their dotfiles directory without
44671follow the layout suggested by
44672@uref{https://www.gnu.org/software/stow/, GNU Stow},
44673and have their dotfiles automatically deployed to their user home, without
44674migrating them to Guix native configurations. 44671migrating them to Guix native configurations.
44675 44672
44676The dotfiles directory layout is expected to be structured as follows. Please 44673Please keep in mind that it is advisable to keep your dotfiles directories under
44677keep in mind that it is advisable to keep your dotfiles directories under
44678version control, for example in the same repository where you'd track your 44674version control, for example in the same repository where you'd track your
44679Guix Home configuration. 44675Guix Home configuration.
44680 44676
44677There are two supported dotfiles directory layouts, for now. The
44678@code{'plain} layout, which is structured as follows:
44679
44680@example
44681~$ tree -a ./dotfiles/
44682dotfiles/
44683├── .gitconfig
44684├── .gnupg
44685│ ├── gpg-agent.conf
44686│ └── gpg.conf
44687├── .guile
44688├── .config
44689│ ├── guix
44690│ │ └── channels.scm
44691│ └── nixpkgs
44692│ └── config.nix
44693├── .nix-channels
44694├── .tmux.conf
44695└── .vimrc
44696@end example
44697
44698This tree structure is installed as is to the
44699home directory upon @command{guix home reconfigure}.
44700
44701The @code{'stow} layout, which must
44702follow the layout suggested by
44703@uref{https://www.gnu.org/software/stow/, GNU Stow} presents an additional
44704application specific directory layer, just like:
44705
44681@example 44706@example
44682~$ tree -a ./dotfiles/ 44707~$ tree -a ./dotfiles/
44683dotfiles/ 44708dotfiles/
@@ -44707,8 +44732,10 @@ dotfiles/
44707@end example 44732@end example
44708 44733
44709For an informal specification please refer to the Stow manual 44734For an informal specification please refer to the Stow manual
44710(@pxref{Top,,, stow, Introduction}). A suitable configuration would then 44735(@pxref{Top,,, stow, Introduction}). This tree structure is installed following
44711be: 44736GNU Stow's logic to the home directory upon @command{guix home reconfigure}.
44737
44738A suitable configuration with a @code{'plain} layout could be:
44712 44739
44713@lisp 44740@lisp
44714(home-environment 44741(home-environment
@@ -44716,7 +44743,7 @@ be:
44716 (services 44743 (services
44717 (service home-dotfiles-service-type 44744 (service home-dotfiles-service-type
44718 (home-dotfiles-configuration 44745 (home-dotfiles-configuration
44719 (directories (list "./dotfiles")))))) 44746 (directories '("./dotfiles"))))))
44720@end lisp 44747@end lisp
44721 44748
44722The expected home directory state would then be: 44749The expected home directory state would then be:
@@ -44743,32 +44770,47 @@ Return a service which is very similiar to @code{home-files-service-type}
44743(and actually extends it), but designed to ease the way into using Guix 44770(and actually extends it), but designed to ease the way into using Guix
44744Home for users that already track their dotfiles under some kind of version 44771Home for users that already track their dotfiles under some kind of version
44745control. This service allows users to point Guix Home to their dotfiles 44772control. This service allows users to point Guix Home to their dotfiles
44746directory and have their files automatically deployed to their home directory 44773directory and have their files automatically provisioned to their home
44747just like Stow would, without migrating all of their dotfiles to Guix native 44774directory, without migrating all of their dotfiles to Guix native
44748configurations. 44775configurations.
44749@end defvar 44776@end defvar
44750 44777
44778@c %start of fragment
44779
44751@deftp {Data Type} home-dotfiles-configuration 44780@deftp {Data Type} home-dotfiles-configuration
44752Available @code{home-dotfiles-configuration} fields are: 44781Available @code{home-dotfiles-configuration} fields are:
44753 44782
44754@table @asis 44783@table @asis
44755@item @code{source-directory} (default: @code{(current-source-directory)}) 44784@item @code{source-directory} (default: @code{(current-source-directory)}) (type: string)
44756The path where dotfile directories are resolved. By default dotfile directories 44785The path where dotfile directories are resolved. By default dotfile
44757are resolved relative the source location where 44786directories are resolved relative the source location where
44758@code{home-dotfiles-configuration} appears. 44787@code{home-dotfiles-configuration} appears.
44759 44788
44760@item @code{directories} (type: list-of-strings) 44789@item @code{layout} (default: @code{'plain}) (type: symbol)
44761The list of dotfiles directories where @code{home-dotfiles-service-type} will 44790The intended layout of the specified @code{directory}. It can be either
44762look for application dotfiles. 44791@code{'stow} or @code{'plain}.
44792
44793@item @code{directories} (default: @code{'()}) (type: list-of-strings)
44794The list of dotfiles directories where @code{home-dotfiles-service-type}
44795will look for application dotfiles.
44796
44797@item @code{packages} (type: maybe-list-of-strings)
44798The names of a subset of the GNU Stow package layer directories. When provided
44799the @code{home-dotfiles-service-type} will only provision dotfiles from this
44800subset of applications. This field will be ignored if @code{layout} is set
44801to @code{'plain}.
44763 44802
44764@item @code{exclude} (default: @code{'(".*~" ".*\\.swp" "\\.git" "\\.gitignore")}) 44803@item @code{excluded} (default: @code{'(".*~" ".*\\.swp" "\\.git" "\\.gitignore")}) (type: list-of-strings)
44765The list of file patterns @code{home-dotfiles-service-type} will exclude while 44804The list of file patterns @code{home-dotfiles-service-type} will exclude
44766visiting each one of the @code{directories}. 44805while visiting each one of the @code{directories}.
44767 44806
44768@end table 44807@end table
44769 44808
44770@end deftp 44809@end deftp
44771 44810
44811
44812@c %end of fragment
44813
44772@defvar home-xdg-configuration-files-service-type 44814@defvar home-xdg-configuration-files-service-type
44773The service is very similar to @code{home-files-service-type} (and 44815The service is very similar to @code{home-files-service-type} (and
44774actually extends it), but used for defining files, which will go to 44816actually extends it), but used for defining files, which will go to
diff --git a/gnu/home/services/dotfiles.scm b/gnu/home/services/dotfiles.scm
index 6a740c42ce4..823bdb03fb4 100644
--- a/gnu/home/services/dotfiles.scm
+++ b/gnu/home/services/dotfiles.scm
@@ -20,17 +20,25 @@
20(define-module (gnu home services dotfiles) 20(define-module (gnu home services dotfiles)
21 #:use-module (gnu home services) 21 #:use-module (gnu home services)
22 #:use-module (gnu services) 22 #:use-module (gnu services)
23 #:use-module (gnu services configuration)
23 #:autoload (guix build utils) (find-files) 24 #:autoload (guix build utils) (find-files)
25 #:use-module (guix diagnostics)
24 #:use-module (guix gexp) 26 #:use-module (guix gexp)
25 #:use-module (guix records) 27 #:use-module (guix i18n)
26 #:use-module ((guix utils) #:select (current-source-directory)) 28 #:use-module ((guix utils) #:select (current-source-directory))
27 #:use-module (srfi srfi-1) 29 #:use-module (srfi srfi-1)
28 #:use-module (ice-9 ftw) 30 #:use-module (ice-9 ftw)
31 #:use-module (ice-9 match)
29 #:use-module (ice-9 regex) 32 #:use-module (ice-9 regex)
30 #:export (home-dotfiles-service-type 33 #:export (home-dotfiles-service-type
34 home-dotfiles-configuration->files
35
31 home-dotfiles-configuration 36 home-dotfiles-configuration
32 home-dotfiles-configuration? 37 home-dotfiles-configuration?
38 home-dotfiles-configuration-fields
39 home-dotfiles-configuration-layout
33 home-dotfiles-configuration-source-directory 40 home-dotfiles-configuration-source-directory
41 home-dotfiles-configuration-packages
34 home-dotfiles-configuration-directories 42 home-dotfiles-configuration-directories
35 home-dotfiles-configuration-excluded)) 43 home-dotfiles-configuration-excluded))
36 44
@@ -40,26 +48,64 @@
40 "\\.git" 48 "\\.git"
41 "\\.gitignore")) 49 "\\.gitignore"))
42 50
43(define-record-type* <home-dotfiles-configuration> 51(define %home-dotfiles-layouts
44 home-dotfiles-configuration make-home-dotfiles-configuration 52 '(plain stow))
45 home-dotfiles-configuration? 53
46 (source-directory home-dotfiles-configuration-source-directory 54(define (sanitize-layout value)
47 (default (current-source-directory)) 55 (if (member value %home-dotfiles-layouts)
48 (innate)) 56 value
49 (directories home-dotfiles-configuration-directories ;list of strings 57 (raise
50 (default '())) 58 (formatted-message
51 (excluded home-dotfiles-configuration-excluded ;list of strings 59 (G_ "layout field of home-dotfiles-configuration should be either 'plain
52 (default %home-dotfiles-excluded))) 60or 'stow, but ~a was found.")
53 61 value))))
54(define (import-dotfiles directory files) 62
63(define list-of-strings?
64 (list-of string?))
65
66(define-maybe list-of-strings)
67
68(define-configuration/no-serialization home-dotfiles-configuration
69 (source-directory
70 (string (current-source-directory))
71 "The path where dotfile directories are resolved. By default dotfile
72directories are resolved relative the source location where
73@code{home-dotfiles-configuration} appears.")
74 (layout
75 (symbol 'plain)
76 "The intended layout of the specified @code{directory}. It can be either
77@code{'stow} or @code{'plain}."
78 (sanitizer sanitize-layout))
79 (directories
80 (list-of-strings '())
81 "The list of dotfiles directories where @code{home-dotfiles-service-type}
82will look for application dotfiles.")
83 (packages
84 (maybe-list-of-strings)
85 "The names of a subset of the GNU Stow package layer directories. When provided
86the @code{home-dotfiles-service-type} will only provision dotfiles from this
87subset of applications. This field will be ignored if @code{layout} is set
88to @code{'plain}.")
89 (excluded
90 (list-of-strings %home-dotfiles-excluded)
91 "The list of file patterns @code{home-dotfiles-service-type} will exclude
92while visiting @code{directory}."))
93
94(define (strip-stow-dotfile file-name directory)
95 (let ((dotfile-name (string-drop file-name (1+ (string-length directory)))))
96 (match (string-split dotfile-name #\/)
97 ((package parts ...)
98 (string-join parts "/")))))
99
100(define (strip-plain-dotfile file-name directory)
101 (string-drop file-name (+ 1 (string-length directory))))
102
103(define (import-dotfiles directory files strip)
55 "Return a list of objects compatible with @code{home-files-service-type}'s 104 "Return a list of objects compatible with @code{home-files-service-type}'s
56value. Each object is a pair where the first element is the relative path 105value. Each object is a pair where the first element is the relative path
57of a file and the second is a gexp representing the file content. Objects are 106of a file and the second is a gexp representing the file content. Objects are
58generated by recursively visiting DIRECTORY and mapping its contents to the 107generated by recursively visiting DIRECTORY and mapping its contents to the
59user's home directory, excluding files that match any of the patterns in EXCLUDED." 108user's home directory, excluding files that match any of the patterns in EXCLUDED."
60 (define (strip file)
61 (string-drop file (+ 1 (string-length directory))))
62
63 (define (format file) 109 (define (format file)
64 ;; Remove from FILE characters that cannot be used in the store. 110 ;; Remove from FILE characters that cannot be used in the store.
65 (string-append 111 (string-append
@@ -73,7 +119,7 @@ user's home directory, excluding files that match any of the patterns in EXCLUDE
73 file))) 119 file)))
74 120
75 (map (lambda (file) 121 (map (lambda (file)
76 (let ((stripped (strip file))) 122 (let ((stripped (strip file directory)))
77 (list stripped 123 (list stripped
78 (local-file file (format stripped) 124 (local-file file (format stripped)
79 #:recursive? #t)))) 125 #:recursive? #t))))
@@ -81,18 +127,25 @@ user's home directory, excluding files that match any of the patterns in EXCLUDE
81 127
82(define (home-dotfiles-configuration->files config) 128(define (home-dotfiles-configuration->files config)
83 "Return a list of objects compatible with @code{home-files-service-type}'s 129 "Return a list of objects compatible with @code{home-files-service-type}'s
84value, generated following GNU Stow's algorithm for each of the 130value, excluding files that match any of the patterns configured."
85directories in CONFIG, excluding files that match any of the patterns configured." 131 (define stow? (eq? (home-dotfiles-configuration-layout config) 'stow))
86 (define excluded 132 (define excluded
87 (home-dotfiles-configuration-excluded config)) 133 (home-dotfiles-configuration-excluded config))
88 (define exclusion-rx 134 (define exclusion-rx
89 (make-regexp (string-append "^.*(" (string-join excluded "|") ")$"))) 135 (make-regexp (string-append "^.*(" (string-join excluded "|") ")$")))
90 136
91 (define (directory-contents directory) 137 (define* (directory-contents directory #:key (packages #f))
92 (find-files directory 138 (define (filter-files directory)
93 (lambda (file stat) 139 (find-files directory
94 (not (regexp-exec exclusion-rx 140 (lambda (file stat)
95 (basename file)))))) 141 (not (regexp-exec exclusion-rx
142 (basename file))))))
143 (if (and stow? packages (maybe-value-set? packages))
144 (append-map filter-files
145 (map (lambda (pkg)
146 (string-append directory "/" pkg))
147 packages))
148 (filter-files directory)))
96 149
97 (define (resolve directory) 150 (define (resolve directory)
98 ;; Resolve DIRECTORY relative to the 'source-directory' field of CONFIG. 151 ;; Resolve DIRECTORY relative to the 'source-directory' field of CONFIG.
@@ -103,15 +156,23 @@ directories in CONFIG, excluding files that match any of the patterns configured
103 156
104 (append-map (lambda (directory) 157 (append-map (lambda (directory)
105 (let* ((directory (resolve directory)) 158 (let* ((directory (resolve directory))
106 (contents (directory-contents directory))) 159 (packages
107 (import-dotfiles directory contents))) 160 (home-dotfiles-configuration-packages config))
161 (contents
162 (directory-contents directory
163 #:packages packages))
164 (strip
165 (if stow? strip-stow-dotfile strip-plain-dotfile)))
166 (import-dotfiles directory contents strip)))
108 (home-dotfiles-configuration-directories config))) 167 (home-dotfiles-configuration-directories config)))
109 168
110(define-public home-dotfiles-service-type 169(define-public home-dotfiles-service-type
111 (service-type (name 'home-dotfiles) 170 (service-type (name 'home-dotfiles)
112 (extensions 171 (extensions
113 (list (service-extension home-files-service-type 172 (list (service-extension home-files-service-type
114 home-dotfiles-configuration->files))) 173 (lambda (config)
174 (when config
175 (home-dotfiles-configuration->files config))))))
115 (default-value (home-dotfiles-configuration)) 176 (default-value (home-dotfiles-configuration))
116 (description "Files that will be put in the user's home directory 177 (description "Files that will be put in the user's home directory
117following GNU Stow's algorithm, and further processed during activation."))) 178following GNU Stow's algorithm, and further processed during activation.")))