nix-community / impermanence

Modules to help you handle persistent state on systems with ephemeral root storage [maintainer=@talyz]
MIT License
1.01k stars 76 forks source link

Symlinks created via the home-manager module are broken #177

Open Janrupf opened 3 months ago

Janrupf commented 3 months ago

Adding the following configuration creates a broken symlink persistent in /home/janrupf:

home.persistence."/persistent/home/janrupf" = {
  directories = [
    { directory = "persistent"; method = "symlink"; }
  ];
}

Inspecting:

$ readlink persistent
/nix/store/vlidns7s3sp46bkw65nmr8wj7lbhvkvx-home-manager-files/persistent
$ readlink /nix/store/vlidns7s3sp46bkw65nmr8wj7lbhvkvx-home-manager-files/persistent
/nix/store/iwbbjrdnqrffd8bjv4yi5yvanhj6w8f0-persistent-home-janrupf-persistent
$ readlink /nix/store/iwbbjrdnqrffd8bjv4yi5yvanhj6w8f0-persistent-home-janrupf-persistent
/persistent/home/janrupf/persistent

So the symlink does point to the correct location afterall - the issue is, that location is never created. The creation is supposed to happen here: https://github.com/nix-community/impermanence/blob/a33ef102a02ce77d3e39c25197664b7a636f9c30/home-manager.nix#L451-L461

Inspecting the flake output reveals that the command added to home.activation.createTargetFileDirectories is mkdir -p /persistent/home/janrupf/. - notice the dot at the end. I think this is caused by the dirOf call on line 458.

Janrupf commented 3 months ago

Temporary fix:

  home.activation.createTargetFileDirectoriesFixup =
  let
    isSymlink = entry: (!lib.isString entry) && (entry.method == "symlink");
    entryPath = entry: if lib.isString entry then entry else entry.directory;

    cfg = config.home.persistence;

    persistenceRoots = lib.attrNames cfg;
    mkdirPersistentLocations = map (root:
      let
        persistentDiskPath = cfg.${root}.persistentStoragePath;
        persistentDirectories = 
          # Collect all directories which are symlinks
          (map entryPath (lib.filter isSymlink cfg.${root}.directories)) ++
          # And all directories of files
          (map lib.dirOf (map entryPath (cfg.${root}.files)));

        completePaths = map (path: lib.path.append (/. + root) path) persistentDirectories;
        mkdirCommands = lib.concatMapStrings (completePath: "run mkdir -p ${lib.escapeShellArg completePath}\n") completePaths;
      in ''
        # mkdir's for ${root}

        ${mkdirCommands}
      ''
    ) persistenceRoots;
  in
    config.lib.dag.entryBetween [ "writeBoundary" ] [ "createTargetFileDirectories" ] (
      lib.concatMapStrings (cmds: "${cmds}\n") mkdirPersistentLocations
    );

This should create all directories as intended when put into the home-manager configuration.

Schwanzskala commented 2 days ago

I've also ran into this problem. The temporary fix works, although for me with one minor modification; I had to change lib.dirOf to just dirOf.