From 86be605f3ff2d25d96d12b31977769880e736b87 Mon Sep 17 00:00:00 2001 From: Robert Helgesson Date: Mon, 24 Feb 2020 23:58:13 +0100 Subject: [PATCH] files: manage static files in state directory This improves atomicity since all files managed by Home Manager will immediately switch over when the link $XDG_DATA_HOME/home-manager/files is swapped over to the new generation. Removing obsolete files and adding new ones must still done non-atomically but this improves the situation significantly. --- doc/release-notes/rl-2003.adoc | 20 ++++++++++++++++++++ modules/files.nix | 28 +++++++++++++++++++++------- modules/home-environment.nix | 5 +++++ 3 files changed, 46 insertions(+), 7 deletions(-) diff --git a/doc/release-notes/rl-2003.adoc b/doc/release-notes/rl-2003.adoc index bc94b15ce..5e00b3f6e 100644 --- a/doc/release-notes/rl-2003.adoc +++ b/doc/release-notes/rl-2003.adoc @@ -74,6 +74,26 @@ new module `services.picom` should be used. This is because Nixpkgs no longer packages compton, and instead packages the (mostly) compatible fork called picom. +* Entries in <> are now linked into place via an +intermediate location, `$XDG_DATA_HOME/home-manager/files`, much like +`/etc/static` in NixOS. This change was made to makes the switch +process more atomic. ++ +In general this should not be noticeable but if you manually activate +an older generation it may (harmlessly) exit with an error along the +lines of ++ +.... +Existing file '/home/jane/foo' is in the way +Please move the above files and try again or use -b to move automatically. +.... ++ +where `/home/jane/foo` is a file managed through the <> +option. This is due to the older activation script not recognizing the +new XDG data home path as belonging to Home Manager. You can resolve +this by manually removing the `/home/jane/foo` symbolic link and +running the activate script again. + [[sec-release-20.03-state-version-changes]] === State Version Changes diff --git a/modules/files.nix b/modules/files.nix index 0b87d5391..8f4adb17d 100644 --- a/modules/files.nix +++ b/modules/files.nix @@ -50,12 +50,15 @@ in (mapAttrsToList (n: v: v.target) (filterAttrs (n: v: v.force) cfg)); + filesDir = "${config.xdg.dataHome}/home-manager/files"; + check = pkgs.writeText "check" '' . ${./lib-bash/color-echo.sh} # A symbolic link whose target path matches this pattern will be # considered part of a Home Manager generation. - homeFilePattern="$(readlink -e "${builtins.storeDir}")/*-home-manager-files/*" + homeFilePatternOld="$(readlink -e "${builtins.storeDir}")/*-home-manager-files/*" + homeFilePattern="${filesDir}/*" forcedPaths=(${forcedPaths}) @@ -76,6 +79,7 @@ in if [[ -n $forced ]]; then $VERBOSE_ECHO "Skipping collision check for $targetPath" elif [[ -e "$targetPath" \ + && ! "$(readlink "$targetPath")" == $homeFilePatternOld \ && ! "$(readlink "$targetPath")" == $homeFilePattern ]] ; then if [[ ! -L "$targetPath" && -n "$HOME_MANAGER_BACKUP_EXT" ]] ; then backup="$targetPath.$HOME_MANAGER_BACKUP_EXT" @@ -133,6 +137,8 @@ in # source and target generation. home.activation.linkGeneration = hm.dag.entryAfter ["writeBoundary"] ( let + filesDir = "${config.xdg.dataHome}/home-manager/files"; + link = pkgs.writeText "link" '' newGenFiles="$1" shift @@ -151,9 +157,10 @@ in cleanup = pkgs.writeText "cleanup" '' . ${./lib-bash/color-echo.sh} - # A symbolic link whose target path matches this pattern will be + # A symbolic link whose target path matches these patterns will be # considered part of a Home Manager generation. - homeFilePattern="$(readlink -e "${builtins.storeDir}")/*-home-manager-files/*" + homeFilePatternOld="$(readlink -e "${builtins.storeDir}")/*-home-manager-files/*" + homeFilePattern="${filesDir}/*" newGenFiles="$1" shift 1 @@ -161,7 +168,8 @@ in targetPath="$HOME/$relativePath" if [[ -e "$newGenFiles/$relativePath" ]] ; then $VERBOSE_ECHO "Checking $targetPath: exists" - elif [[ ! "$(readlink "$targetPath")" == $homeFilePattern ]] ; then + elif [[ ! "$(readlink "$targetPath")" == $homeFilePatternOld \ + && ! "$(readlink "$targetPath")" == $homeFilePattern ]] ; then warnEcho "Path '$targetPath' not link into Home Manager generation. Skipping delete." else $VERBOSE_ECHO "Checking $targetPath: gone (deleting)" @@ -189,9 +197,8 @@ in function linkNewGen() { echo "Creating home file links in $HOME" - local newGenFiles - newGenFiles="$(readlink -e "$newGenPath/home-files")" - find "$newGenFiles" \( -type f -or -type l \) \ + local newGenFiles="${filesDir}" + find "$newGenFiles/" \( -type f -or -type l \) \ -exec bash ${link} "$newGenFiles" {} + } @@ -215,6 +222,13 @@ in cleanOldGen + if [[ ! -e "${filesDir}" \ + || "${config.home-files}" != "$(readlink "${filesDir}")" ]] ; then + $DRY_RUN_CMD ln -Tsf $VERBOSE_ARG "${config.home-files}" "${filesDir}" + else + $VERBOSE_ECHO "No change in static files, skipping linking process" + fi + if [[ ! -v oldGenPath || "$oldGenPath" != "$newGenPath" ]] ; then echo "Creating profile generation $newGenNum" $DRY_RUN_CMD nix-env $VERBOSE_ARG --profile "$genProfilePath" --set "$newGenPath" diff --git a/modules/home-environment.nix b/modules/home-environment.nix index 0b2fb8f4e..8c2a6649d 100644 --- a/modules/home-environment.nix +++ b/modules/home-environment.nix @@ -431,6 +431,11 @@ in ${builtins.readFile ./lib-bash/activation-init.sh} + # Some activation blocks may need to maintain some state, which + # should be kept in this directory. We consider creation of this + # directory exempt from the write boundary. + $DRY_RUN_CMD mkdir $VERBOSE_ARG -p "${config.xdg.dataHome}/home-manager" + ${activationCmds} ''; in