nix-community / home-manager

Manage a user environment using Nix [maintainer=@rycee]
https://nix-community.github.io/home-manager/
MIT License
6.41k stars 1.71k forks source link

Add outOfStoreSymlink Option to `home.file` #3514

Open DrRuhe opened 1 year ago

DrRuhe commented 1 year ago

I'd like to have an configuration option for home.file entries, that allows to declare these files as outOfStoreSymlinks. So something like this:

home.file.".config" = {
   source = ./config;
   recursive = true;
   outOfStoreSymlink = true;
};

This would allow me to have a config folder for the related things in my dotfiles under VCS, but still allow the files to be changed by the programs. Changes could then be managed by the VCS.

As a concrete example: The config files of KDE and related programs are plenty. And I'd like to manage these via nix and track them in the dotfiles. When these link to the files in the dotfiles directory, I could still use the GUI for managing the Settings, which would not be possible with links to immutable files in the nix store.

In particular, I'd like it to work with the recursive option mentioned above since AFAICT currently it is only possible to do this with single files.

I think this also helps with discoverability.

montchr commented 1 year ago

In my experience with a flakes-only configuration, I misused mkOutOfStoreSymlink initially -- after much frustration and digging through prior conversations about its usage, I realised that a relative path (like ./config in your example) would not result in an out-of-store symlink at all, because a relative path in a flake is always relative to the flake's store path. It took some time to figure out that it only has the desired effect when its argument is an absolute path on the filesystem. AFAIK, there's no "flake-native" way to detect the flake's out-of-store path on a filesystem, so I've resorted to a concoction of custom module options, environment variables, and hard-coded paths to get the desired behavior.

So, with that said, I suspect that adding an option to the file linker options as described would also necessitate the addition of at least one other option to define an assumed base path for the flake-based home-manager configuration.

But overall I agree that it would be nice to establish some clearer conventions for dealing with this kind of setup!

montchr commented 1 year ago

I marked my previous comment as "outdated" since I'm not sure if my musings were accurate or not… I had been under the impression that mkOutOfStoreSymlink requires an absolute path argument in a flake-based configuration. While #257 seems to indicate otherwise (hence my hesitance in spouting misinformation), it also predates the widespread adoption of flakes -- other more recent issues (#2660, #2085) indicate absolute paths are indeed required for flakes.

eliandoran commented 1 year ago

@montchr , I had a similar experience on my side.

Using the following:

{
  home.file = {
    ".config/hypr/hyprland.conf".source = config.lib.file.mkOutOfStoreSymlink ./hyprland/hyprland.conf;
  };
}

Results in the creation of the following symlink chain:

~/.config/hypr/hyprland.conf
 -> /nix/store/k76965c6lx8xdqc2swha74s8k0hw7xgc-home-manager-files/.config/hypr/hyprland.conf
 -> /nix/store/9wm464xzpmwnaav4q0li0nmdm98slfab-hm_hyprland.conf
 -> /nix/store/mnaq3pzm0qrkvs8kpgpjw456iprj36sy-source/core/hyprland/hyprland.conf

In the previous case, the symlink failed since it was pointing to a missing file.

Passing a string instead, the path will be relative to the Nix store:

/nix/store/5rnm8ch7ybg5ddk1jj20c2avrbrji0af-hm_hyprland.conf -> ./hyprland/hyprland.conf

Thus, an absolute path must be used.

stale[bot] commented 1 year ago

Thank you for your contribution! I marked this issue as stale due to inactivity. Please be considerate of people watching this issue and receiving notifications before commenting 'I have this issue too'. We welcome additional information that will help resolve this issue. Please read the relevant sections below before commenting.

If you are the original author of the issue

* If this is resolved, please consider closing it so that the maintainers know not to focus on this. * If this might still be an issue, but you are not interested in promoting its resolution, please consider closing it while encouraging others to take over and reopen an issue if they care enough. * If you know how to solve the issue, please consider submitting a Pull Request that addresses this issue.

If you are not the original author of the issue

* If you are also experiencing this issue, please add details of your situation to help with the debugging process. * If you know how to solve the issue, please consider submitting a Pull Request that addresses this issue.

Memorandum on closing issues

Don't be afraid to manually close an issue, even if it holds valuable information. Closed issues stay in the system for people to search, read, cross-reference, or even reopen – nothing is lost! Closing obsolete issues is an important way to help maintainers focus their time and effort.

montchr commented 1 year ago

A potential benefit of a dedicated option for OOS-symlinks could be validating the type of the option definition. Perhaps a warning could be logged if the value points to a store path, which is most likely not desirable.

stale[bot] commented 11 months ago

Thank you for your contribution! I marked this issue as stale due to inactivity. Please be considerate of people watching this issue and receiving notifications before commenting 'I have this issue too'. We welcome additional information that will help resolve this issue. Please read the relevant sections below before commenting.

If you are the original author of the issue

* If this is resolved, please consider closing it so that the maintainers know not to focus on this. * If this might still be an issue, but you are not interested in promoting its resolution, please consider closing it while encouraging others to take over and reopen an issue if they care enough. * If you know how to solve the issue, please consider submitting a Pull Request that addresses this issue.

If you are not the original author of the issue

* If you are also experiencing this issue, please add details of your situation to help with the debugging process. * If you know how to solve the issue, please consider submitting a Pull Request that addresses this issue.

Memorandum on closing issues

Don't be afraid to manually close an issue, even if it holds valuable information. Closed issues stay in the system for people to search, read, cross-reference, or even reopen – nothing is lost! Closing obsolete issues is an important way to help maintainers focus their time and effort.

stereomato commented 9 months ago

i just needed this so this is relevant still

sealor commented 6 months ago

For me, this issue is still relevant too!

I'm also using a flake for my configuration. The following helper function solved my issues with the absolute path:

createSymlink = localPath:
    config.lib.file.mkOutOfStoreSymlink "${config.xdg.configHome}/home-manager/${localPath}";

In my case I would like to use:

home.file."bin" = {
   source = "${config.xdg.configHome}/home-manager/bin";
   recursive = true;
   outOfStoreSymlink = true;
};

It would be nice to have this feature in future versions. Thanks!

urob commented 4 months ago

I figured out a way to do this with listFilesRecursive. Here's my minimal working example:

# home.nix
{ config, pkgs, lib, ... }:
let
    utils = import ./utils.nix { inherit config pkgs lib; };
    dotfiles = "${config.home.homeDirectory}/dotfiles";

in {
    # patch the attrSet with linkHomeFiles (see utils.nix below)
    home.file = utils.linkHomeFiles {
        # set outOfStoreSymlink = true and recursive = true to recursively link all files within source
        "bin" =           { source = "${dotfiles}/bin"; outOfStoreSymlink = true; recursive = true; };
        ".config" =       { source = "${dotfiles}/config"; outOfStoreSymlink = true; recursive = true; };
        ".ssh" =          { source = "${dotfiles}/config/ssh"; outOfStoreSymlink = true; recursive = true; };

        # set outOfStoreSymlink = true to replicate normal mkOutOfStoreSymlink behavior
        ".editorconfig" = { source = "${dotfiles}/config/editorconfig/config"; outOfStoreSymlink = true; };
        ".zshenv" =       { source = "${dotfiles}/config/zsh/.zshenv"; outOfStoreSymlink = true; };

        # set outOfStoreSymlink = false for default behavior. E.g.,
        ".profile".text = '' . "${config.home.profileDirectory}/etc/profile.d/hm-session-vars.sh" '';
    };
}
# utils.nix
{ config, pkgs, lib, ... }:
{
    # Add out-of-store-symlinks to given attrSet
    linkHomeFiles =
        let
            ln = config.lib.file.mkOutOfStoreSymlink;
            lndir = path: link: builtins.listToAttrs (
                map (file: {
                    name = "${link}/${lib.path.removePrefix (/. + path) (/. + file)}";
                    value = { source = ln "${file}"; };
                }) (lib.filesystem.listFilesRecursive path)
            );
            rmopts = attrs: builtins.removeAttrs attrs ["source" "recursive" "outOfStoreSymlink"];
        in
            fileAttrs: lib.attrsets.concatMapAttrs (name: value:
                if value.outOfStoreSymlink or false
                then
                    if value.recursive or false
                    then
                        # Recursive version of mkOutOfStoreSymlink
                        lib.attrsets.mapAttrs (_: attrs: attrs // rmopts value) (lndir value.source name)
                    else
                        # Same as mkOutOfStoreSymlink
                        { "${name}" = { source = ln value.source; } // rmopts value; }
                else
                    # Use default handler for in-store links
                    { "${name}" = value; }
                ) fileAttrs;
}

Note: since we are linking to /home, we need to pass --impure to home-manager when (re)building. E.g.,

home-manager switch --flake . --impure
stale[bot] commented 1 month ago

Thank you for your contribution! I marked this issue as stale due to inactivity. Please be considerate of people watching this issue and receiving notifications before commenting 'I have this issue too'. We welcome additional information that will help resolve this issue. Please read the relevant sections below before commenting.

If you are the original author of the issue

* If this is resolved, please consider closing it so that the maintainers know not to focus on this. * If this might still be an issue, but you are not interested in promoting its resolution, please consider closing it while encouraging others to take over and reopen an issue if they care enough. * If you know how to solve the issue, please consider submitting a Pull Request that addresses this issue.

If you are not the original author of the issue

* If you are also experiencing this issue, please add details of your situation to help with the debugging process. * If you know how to solve the issue, please consider submitting a Pull Request that addresses this issue.

Memorandum on closing issues

Don't be afraid to manually close an issue, even if it holds valuable information. Closed issues stay in the system for people to search, read, cross-reference, or even reopen – nothing is lost! Closing obsolete issues is an important way to help maintainers focus their time and effort.

tjkirch commented 1 month ago

Not stale.