The code below lets you create a directory in your home-manager config that auto-creates symlinks for non-Nix dotfiles from ~/.config/ using xdg.configFile and mkOutOfStoreSymlink. This gives the following advantages:
- You can keep all your non-Nix dotfiles in your home-manager repo.
- You don't have to do
home-manager switchon each dotfile change. Changes are instantly reflected. - You don't need to hard-code the paths for each symlink, the linking is instead file-based. Just add the new dotfile to the directory, and you're done.
For example, if your home-manager config is in ~/nixos/ and looks like this...:
~/nixos
├── ...
├── flake.nix
└── configs
├── starship.toml
└── fish
└── config.fish
...then home-mamager will create the following symlinks in ~/.config:
~/.config
├── ...
├── starship.toml -> ~/nixos/configs/starship.toml
└── fish
└── config.fish -> ~/nixos/configs/fish/config.fish
Don't forget to git add when adding a new dotfile. Otherwise, they will be ignored by Nix. Then run home-manager switch to create the new symlink.
{ config }:
let
# -------------------------------------------------------------------------
# `configsNixPath`: a Nix path type to a directory who's files/directories
# should be symlinked to from `~/.config/`.
#
# `configsAbsPath`: an absolute string path to `configsPath`.
#
# The reason this function requires two path parameters to the same
# directory is that it uses `builtins.readDir` which would require
# `--impure` if only an absolute path were used. If using only a literal
# Nix path, the symlinks would point to the Nix store, and thereby require
# a build whenever a config file is edited.
#
# `returns`: an attribute set suitable for `xdg.configFile`.
# -----------------------------------------------------------------
mkSymlinks =
configsAbsPath: configsNixPath:
let
inherit (config.lib.file) mkOutOfStoreSymlink;
# Turn relative paths into xdg.configFile entries
mkSymlink = nixPath: {
name = nixPath;
value.source = mkOutOfStoreSymlink "${configsNixPath}/${nixPath}";
};
# Recursively read in all files in all subdirectories
readDirRecursive =
relPath: nixPath:
nixPath
|> builtins.readDir
|> builtins.attrNames
|> map (
name:
let
entryType = nixPath |> builtins.readDir |> (entries: entries.${name});
relPath' = if relPath == "" then name else "${relPath}/${name}";
nixPath' = "${nixPath}/${name}";
in
# Don't include paths to the directories themselves
if entryType == "directory" then readDirRecursive relPath' nixPath' else [ relPath' ]
)
|> builtins.concatLists;
in readDirRecursive "" configsAbsPath |> map mkSymlink |> builtins.listToAttrs;
in {
# Change the arguments to point to your `configs` directory
xdg.configFile = mkSymlinks ./configs "~/nixos/configs";
}Please let me know if there's some way to improve the implementation so that you only have to pass in one path parameter.
https://github.com/nix-community/impermanence?tab=readme-ov-file#home-manager