diff options
| author | Giacomo Leidi <goodoldpaul@autistici.org> | 2024-03-06 21:52:42 +0100 |
|---|---|---|
| committer | Ludovic Courtès <ludo@gnu.org> | 2024-03-06 23:25:51 +0100 |
| commit | 01f685d56016ae529381a73daedccc1949b808ec (patch) | |
| tree | 61935a2682dcfa69ae4929acd7c896ca6433b4f2 | |
| parent | 9e3061a163abcae5ccd3823ba3d8f757a0478e40 (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.texi | 84 | ||||
| -rw-r--r-- | gnu/home/services/dotfiles.scm | 115 |
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 (@* | |||
| 111 | Copyright @copyright{} 2022 John Kehayias@* | 111 | Copyright @copyright{} 2022 John Kehayias@* |
| 112 | Copyright @copyright{} 2022–2023 Bruno Victal@* | 112 | Copyright @copyright{} 2022–2023 Bruno Victal@* |
| 113 | Copyright @copyright{} 2022 Ivan Vilata-i-Balaguer@* | 113 | Copyright @copyright{} 2022 Ivan Vilata-i-Balaguer@* |
| 114 | Copyright @copyright{} 2023 Giacomo Leidi@* | 114 | Copyright @copyright{} 2023-2024 Giacomo Leidi@* |
| 115 | Copyright @copyright{} 2022 Antero Mejr@* | 115 | Copyright @copyright{} 2022 Antero Mejr@* |
| 116 | Copyright @copyright{} 2023 Karl Hallsby@* | 116 | Copyright @copyright{} 2023 Karl Hallsby@* |
| 117 | Copyright @copyright{} 2023 Nathaniel Nicandro@* | 117 | Copyright @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 |
| 44668 | The @code{home-dotfiles-service-type} from @code{(gnu home services dotfiles)} | 44668 | The @code{home-dotfiles-service-type} from @code{(gnu home services dotfiles)} |
| 44669 | is designed to ease the way into using Guix Home for this kind of users, | 44669 | is designed to ease the way into using Guix Home for this kind of users, |
| 44670 | allowing them to point the service to their dotfiles directory, which must | 44670 | allowing them to point the service to their dotfiles directory without |
| 44671 | follow the layout suggested by | ||
| 44672 | @uref{https://www.gnu.org/software/stow/, GNU Stow}, | ||
| 44673 | and have their dotfiles automatically deployed to their user home, without | ||
| 44674 | migrating them to Guix native configurations. | 44671 | migrating them to Guix native configurations. |
| 44675 | 44672 | ||
| 44676 | The dotfiles directory layout is expected to be structured as follows. Please | 44673 | Please keep in mind that it is advisable to keep your dotfiles directories under |
| 44677 | keep in mind that it is advisable to keep your dotfiles directories under | ||
| 44678 | version control, for example in the same repository where you'd track your | 44674 | version control, for example in the same repository where you'd track your |
| 44679 | Guix Home configuration. | 44675 | Guix Home configuration. |
| 44680 | 44676 | ||
| 44677 | There 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/ | ||
| 44682 | dotfiles/ | ||
| 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 | |||
| 44698 | This tree structure is installed as is to the | ||
| 44699 | home directory upon @command{guix home reconfigure}. | ||
| 44700 | |||
| 44701 | The @code{'stow} layout, which must | ||
| 44702 | follow the layout suggested by | ||
| 44703 | @uref{https://www.gnu.org/software/stow/, GNU Stow} presents an additional | ||
| 44704 | application specific directory layer, just like: | ||
| 44705 | |||
| 44681 | @example | 44706 | @example |
| 44682 | ~$ tree -a ./dotfiles/ | 44707 | ~$ tree -a ./dotfiles/ |
| 44683 | dotfiles/ | 44708 | dotfiles/ |
| @@ -44707,8 +44732,10 @@ dotfiles/ | |||
| 44707 | @end example | 44732 | @end example |
| 44708 | 44733 | ||
| 44709 | For an informal specification please refer to the Stow manual | 44734 | For 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 |
| 44711 | be: | 44736 | GNU Stow's logic to the home directory upon @command{guix home reconfigure}. |
| 44737 | |||
| 44738 | A 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 | ||
| 44722 | The expected home directory state would then be: | 44749 | The 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 |
| 44744 | Home for users that already track their dotfiles under some kind of version | 44771 | Home for users that already track their dotfiles under some kind of version |
| 44745 | control. This service allows users to point Guix Home to their dotfiles | 44772 | control. This service allows users to point Guix Home to their dotfiles |
| 44746 | directory and have their files automatically deployed to their home directory | 44773 | directory and have their files automatically provisioned to their home |
| 44747 | just like Stow would, without migrating all of their dotfiles to Guix native | 44774 | directory, without migrating all of their dotfiles to Guix native |
| 44748 | configurations. | 44775 | configurations. |
| 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 |
| 44752 | Available @code{home-dotfiles-configuration} fields are: | 44781 | Available @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) |
| 44756 | The path where dotfile directories are resolved. By default dotfile directories | 44785 | The path where dotfile directories are resolved. By default dotfile |
| 44757 | are resolved relative the source location where | 44786 | directories 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) |
| 44761 | The list of dotfiles directories where @code{home-dotfiles-service-type} will | 44790 | The intended layout of the specified @code{directory}. It can be either |
| 44762 | look for application dotfiles. | 44791 | @code{'stow} or @code{'plain}. |
| 44792 | |||
| 44793 | @item @code{directories} (default: @code{'()}) (type: list-of-strings) | ||
| 44794 | The list of dotfiles directories where @code{home-dotfiles-service-type} | ||
| 44795 | will look for application dotfiles. | ||
| 44796 | |||
| 44797 | @item @code{packages} (type: maybe-list-of-strings) | ||
| 44798 | The names of a subset of the GNU Stow package layer directories. When provided | ||
| 44799 | the @code{home-dotfiles-service-type} will only provision dotfiles from this | ||
| 44800 | subset of applications. This field will be ignored if @code{layout} is set | ||
| 44801 | to @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) |
| 44765 | The list of file patterns @code{home-dotfiles-service-type} will exclude while | 44804 | The list of file patterns @code{home-dotfiles-service-type} will exclude |
| 44766 | visiting each one of the @code{directories}. | 44805 | while 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 |
| 44773 | The service is very similar to @code{home-files-service-type} (and | 44815 | The service is very similar to @code{home-files-service-type} (and |
| 44774 | actually extends it), but used for defining files, which will go to | 44816 | actually 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))) | 60 | or '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 | ||
| 72 | directories 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} | ||
| 82 | will 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 | ||
| 86 | the @code{home-dotfiles-service-type} will only provision dotfiles from this | ||
| 87 | subset of applications. This field will be ignored if @code{layout} is set | ||
| 88 | to @code{'plain}.") | ||
| 89 | (excluded | ||
| 90 | (list-of-strings %home-dotfiles-excluded) | ||
| 91 | "The list of file patterns @code{home-dotfiles-service-type} will exclude | ||
| 92 | while 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 |
| 56 | value. Each object is a pair where the first element is the relative path | 105 | value. Each object is a pair where the first element is the relative path |
| 57 | of a file and the second is a gexp representing the file content. Objects are | 106 | of a file and the second is a gexp representing the file content. Objects are |
| 58 | generated by recursively visiting DIRECTORY and mapping its contents to the | 107 | generated by recursively visiting DIRECTORY and mapping its contents to the |
| 59 | user's home directory, excluding files that match any of the patterns in EXCLUDED." | 108 | user'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 |
| 84 | value, generated following GNU Stow's algorithm for each of the | 130 | value, excluding files that match any of the patterns configured." |
| 85 | directories 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 |
| 117 | following GNU Stow's algorithm, and further processed during activation."))) | 178 | following GNU Stow's algorithm, and further processed during activation."))) |
