Toggle diff (273 lines)
diff --git a/doc/guix.texi b/doc/guix.texi
index db0c751ded..c723631abe 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -44204,6 +44204,114 @@ to use alternative services to implement more advanced use cases like
read-only home. Feel free to experiment and share your results.
@end defvar
+It is often the case that Guix Home users already have a setup for versioning
+their user configuration files (also known as @emph{dotfiles}) in a single
+directory, and some way of automatically deploy changes to their user home.
+
+The @code{home-dotfiles-service-type} from @code{(gnu home services dotfiles)}
+is designed to ease the way into using Guix Home for this kind of users,
+allowing them to point the service to their dotfiles directory, which must
+follow the layout suggested by
+@uref{https://www.gnu.org/software/stow/, GNU Stow},
+and have their dotfiles automatically deployed to their user home, without
+migrating them to Guix native configurations.
+
+The dotfiles directory layout is expected to be structured as follows. Please
+keep in mind that it is advisable to keep your dotfiles directories under
+version control, for example in the same repository where you'd track your
+Guix Home configuration.
+
+@example
+~$ tree -a ./dotfiles/
+dotfiles/
+??? git
+? ??? .gitconfig
+??? gpg
+? ??? .gnupg
+? ??? gpg-agent.conf
+? ??? gpg.conf
+??? guile
+? ??? .guile
+??? guix
+? ??? .config
+? ??? guix
+? ??? channels.scm
+??? nix
+? ??? .config
+? ? ??? nixpkgs
+? ? ??? config.nix
+? ??? .nix-channels
+??? tmux
+? ??? .tmux.conf
+??? vim
+ ??? .vimrc
+
+13 directories, 10 files
+@end example
+
+For an informal specification please refer to the Stow manual
+(@pxref{Top,,, stow, Introduction}). A suitable configuration would then
+be:
+
+@lisp
+(home-environment
+ ;; @dots{}
+ (services
+ (service home-dotfiles-service-type
+ (home-dotfiles-configuration
+ (directories (list "./dotfiles"))))))
+@end lisp
+
+The expected home directory state would then be:
+
+@example
+.
+??? .config
+? ??? guix
+? ? ??? channels.scm
+? ??? nixpkgs
+? ??? config.nix
+??? .gitconfig
+??? .gnupg
+? ??? gpg-agent.conf
+? ??? gpg.conf
+??? .guile
+??? .nix-channels
+??? .tmux.conf
+??? .vimrc
+@end example
+
+@defvar home-dotfiles-service-type
+Return a service which is very similiar to @code{home-files-service-type}
+(and actually extends it), but designed to ease the way into using Guix
+Home for users that already track their dotfiles under some kind of version
+control. This service allows users to point Guix Home to their dotfiles
+directory and have their files automatically deployed to their home directory
+just like Stow would, without migrating all of their dotfiles to Guix native
+configurations.
+@end defvar
+
+@deftp {Data Type} home-dotfiles-configuration
+Available @code{home-dotfiles-configuration} fields are:
+
+@table @asis
+@item @code{source-directory} (default: @code{(current-source-directory)})
+The path where dotfile directories are resolved. By default dotfile directories
+are resolved relative the source location where
+@code{home-dotfiles-configuration} appears.
+
+@item @code{directories} (type: list-of-strings)
+The list of dotfiles directories where @code{home-dotfiles-service-type} will
+look for application dotfiles.
+
+@item @code{exclude} (default: @code{'(".*~" ".*\\.swp" "\\.git" "\\.gitignore")})
+The list of file patterns @code{home-dotfiles-service-type} will exclude while
+visiting each one of the @code{directories}.
+
+@end table
+
+@end deftp
+
@defvar home-xdg-configuration-files-service-type
The service is very similar to @code{home-files-service-type} (and
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
new file mode 100644
index 0000000000..7226b6bcff
--- /dev/null
+++ b/gnu/home/services/dotfiles.scm
@@ -0,0 +1,120 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2024 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2024 Giacomo Leidi <goodoldpaul@autistici.org>
+;;;
+;;; 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 <http://www.gnu.org/licenses/>.
+
+(define-module (gnu home services dotfiles)
+ #:use-module (gnu home services)
+ #:use-module (gnu services)
+ #:autoload (guix build utils) (find-files)
+ #:use-module (guix gexp)
+ #:use-module (guix records)
+ #:use-module ((guix utils) #:select (current-source-directory))
+ #:use-module (srfi srfi-1)
+ #:use-module (ice-9 ftw)
+ #:use-module (ice-9 regex)
+ #:export (home-dotfiles-service-type
+ home-dotfiles-configuration
+ home-dotfiles-configuration?
+ home-dotfiles-configuration-source-directory
+ home-dotfiles-configuration-directories
+ home-dotfiles-configuration-excluded))
+
+(define %home-dotfiles-excluded
+ '(".*~"
+ ".*\\.swp"
+ "\\.git"
+ "\\.gitignore"))
+
+(define-record-type* <home-dotfiles-configuration>
+ home-dotfiles-configuration make-home-dotfiles-configuration
+ home-dotfiles-configuration?
+ (source-directory home-dotfiles-configuration-source-directory
+ (default (current-source-directory))
+ (innate))
+ (directories home-dotfiles-configuration-directories ;list of strings
+ (default '()))
+ (excluded home-dotfiles-configuration-excluded ;list of strings
+ (default %home-dotfiles-excluded)))
+
+(define (import-dotfiles directory files)
+ "Return a list of objects compatible with @code{home-files-service-type}'s
+value. Each object is a pair where the first element is the relative path
+of a file and the second is a gexp representing the file content. Objects are
+generated by recursively visiting DIRECTORY and mapping its contents to the
+user's home directory, excluding files that match any of the patterns in EXCLUDED."
+ (define (strip file)
+ (string-join
+ (cdr
+ (string-split (string-drop file (+ 1 (string-length directory)))
+ #\/))
+ "/"))
+ (define (format file)
+ ;; Remove from FILE characters that cannot be used in the store.
+ (string-append
+ "home-dotfiles-"
+ (string-map (lambda (chr)
+ (if (and (char-set-contains? char-set:ascii chr)
+ (char-set-contains? char-set:graphic chr)
+ (not (memv chr '(#\. #\/ #\space))))
+ chr
+ #\-))
+ file)))
+
+ (map (lambda (file)
+ (let ((stripped (strip file)))
+ (list stripped
+ (local-file file (format stripped)
+ #:recursive? #t))))
+ files))
+
+(define (home-dotfiles-configuration->files config)
+ "Return a list of objects compatible with @code{home-files-service-type}'s
+value, generated following GNU Stow's algorithm for each of the
+directories in CONFIG, excluding files that match any of the patterns configured."
+ (define excluded
+ (home-dotfiles-configuration-excluded config))
+ (define exclusion-rx
+ (make-regexp (string-append "^.*(" (string-join excluded "|") ")$")))
+
+ (define (directory-contents directory)
+ (find-files directory
+ (lambda (file stat)
+ (not (regexp-exec exclusion-rx
+ (basename file))))))
+
+ (define (resolve directory)
+ ;; Resolve DIRECTORY relative to the 'source-directory' field of CONFIG.
+ (if (string-prefix? "/" directory)
+ directory
+ (in-vicinity (home-dotfiles-configuration-source-directory config)
+ directory)))
+
+ (append-map (lambda (directory)
+ (let* ((directory (resolve directory))
+ (contents (directory-contents directory)))
+ (import-dotfiles directory contents)))
+ (home-dotfiles-configuration-directories config)))
+
+(define-public home-dotfiles-service-type
+ (service-type (name 'home-dotfiles)
+ (extensions
+ (list (service-extension home-files-service-type
+ home-dotfiles-configuration->files)))
+ (default-value (home-dotfiles-configuration))
+ (description "Files that will be put in the user's home directory
+following GNU Stow's algorithm, and further processed during activation.")))
diff --git a/gnu/local.mk b/gnu/local.mk
index ab63bd5881..f11c65277e 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -96,6 +96,7 @@ GNU_SYSTEM_MODULES = \
%D%/home/services.scm \
%D%/home/services/desktop.scm \
%D%/home/services/dict.scm \
+ %D%/home/services/dotfiles.scm \
%D%/home/services/symlink-manager.scm \
%D%/home/services/fontutils.scm \
%D%/home/services/gnupg.scm \
diff --git a/po/guix/POTFILES.in b/po/guix/POTFILES.in
index 154ad4e530..d13e19619d 100644
--- a/po/guix/POTFILES.in
+++ b/po/guix/POTFILES.in
@@ -14,6 +14,7 @@ gnu/services/samba.scm
gnu/services/version-control.scm
gnu/home/services.scm
gnu/home/services/desktop.scm
+gnu/home/services/dotfiles.scm
gnu/home/services/fontutils.scm
gnu/home/services/gnupg.scm
gnu/home/services/guix.scm
base-commit: 8ae8b9804fa4aef23d4028563559bf7bec52fec2
--
2.41.0