diff --git a/modules/files.nix b/modules/files.nix index 1ecc18217..cb00ce6a0 100644 --- a/modules/files.nix +++ b/modules/files.nix @@ -163,11 +163,12 @@ in home.activation.linkGeneration = hm.dag.entryAfter ["writeBoundary"] ( let link = pkgs.writeShellScript "link" '' - newGenFiles="$1" - shift + rootPath="$1" + newGenFiles="$2" + shift 2 for sourcePath in "$@" ; do relativePath="''${sourcePath#$newGenFiles/}" - targetPath="$HOME/$relativePath" + targetPath="$rootPath/$relativePath" if [[ -e "$targetPath" && ! -L "$targetPath" && -n "$HOME_MANAGER_BACKUP_EXT" ]] ; then backup="$targetPath.$HOME_MANAGER_BACKUP_EXT" $DRY_RUN_CMD mv $VERBOSE_ARG "$targetPath" "$backup" || errorEcho "Moving '$targetPath' failed!" @@ -184,10 +185,11 @@ in # considered part of a Home Manager generation. homeFilePattern="$(readlink -e ${escapeShellArg builtins.storeDir})/*-home-manager-files/*" - newGenFiles="$1" - shift 1 + rootPath="$1" + newGenFiles="$2" + shift 2 for relativePath in "$@" ; do - targetPath="$HOME/$relativePath" + targetPath="$rootPath/$relativePath" if [[ -e "$newGenFiles/$relativePath" ]] ; then $VERBOSE_ECHO "Checking $targetPath: exists" elif [[ ! "$(readlink "$targetPath")" == $homeFilePattern ]] ; then @@ -199,7 +201,8 @@ in # Recursively delete empty parent directories. targetDir="$(dirname "$relativePath")" if [[ "$targetDir" != "." ]] ; then - pushd "$HOME" > /dev/null + # TODO + # pushd "$HOME" > /dev/null # Call rmdir with a relative path excluding $HOME. # Otherwise, it might try to delete $HOME and exit @@ -208,7 +211,7 @@ in -p --ignore-fail-on-non-empty \ "$targetDir" - popd > /dev/null + # popd > /dev/null fi fi done @@ -216,12 +219,17 @@ in in '' function linkNewGen() { - echo "Creating home file links in $HOME" + echo "Creating home file links" + + local rootPath= + if [[ $newGenLayoutVersion -eq 0 ]] ; then + rootPath="$HOME" + fi local newGenFiles newGenFiles="$(readlink -e "$newGenPath/home-files")" find "$newGenFiles" \( -type f -or -type l \) \ - -exec bash ${link} "$newGenFiles" {} + + -exec bash ${link} "$rootPath" "$newGenFiles" {} + } function cleanOldGen() { @@ -229,17 +237,35 @@ in return fi - echo "Cleaning up orphan links from $HOME" + echo "Cleaning up orphan links" local newGenFiles oldGenFiles newGenFiles="$(readlink -e "$newGenPath/home-files")" oldGenFiles="$(readlink -e "$oldGenPath/home-files")" - # Apply the cleanup script on each leaf in the old - # generation. The find command below will print the - # relative path of the entry. - find "$oldGenFiles" '(' -type f -or -type l ')' -printf '%P\0' \ - | xargs -0 bash ${cleanup} "$newGenFiles" + # When transitioning from layout 0 to 1 we need to prefix all old + # paths with the home directory. Conversely, if we ever go from + # layout 1 to 0 we need to "subtract" the home directory from the + # old generation path, this is done by appending an "antiprefix" to + # the layout 1 paths. + local prefix= antiprefix= + if [[ $oldGenLayoutVersion -eq 0 && $newGenLayoutVersion -ge 1 ]] ; then + prefix="''${HOME#/}/" + elif [[ $oldGenLayoutVersion -ge 1 && $newGenLayoutVersion -eq 0 ]] ; then + antiprefix="/$HOME" + fi + + local rootPath= + if [[ $newGenLayoutVersion -eq 0 ]] ; then + rootPath="$HOME" + fi + + $VERBOSE_ECHO "Orphan link cleanup uses prefix=$prefix antiprefix=$antiprefix rootPath=$rootPath" + + # Apply the cleanup script on each leaf in the old generation. The + # find command below will print the relative path of the entry. + find "$oldGenFiles$antiprefix" '(' -type f -or -type l ')' -printf "$prefix%P\0" \ + | xargs -0 bash ${cleanup} "$rootPath" "$newGenFiles" } cleanOldGen diff --git a/modules/home-environment.nix b/modules/home-environment.nix index ee9c3bd27..e13991d94 100644 --- a/modules/home-environment.nix +++ b/modules/home-environment.nix @@ -622,6 +622,13 @@ in mkdir $out/bin ln -s $out/activate $out/bin/home-manager-generation + # The generation directory layout version. + # + # - Version 0 (also implied when file is missing) means + # "legacy layout". + # - Version 1 adds full home file paths. + echo 0 > $out/version + substituteInPlace $out/activate \ --subst-var-by GENERATION_DIR $out diff --git a/modules/lib-bash/activation-init.sh b/modules/lib-bash/activation-init.sh index f95008ee7..ab1a7ec37 100755 --- a/modules/lib-bash/activation-init.sh +++ b/modules/lib-bash/activation-init.sh @@ -5,9 +5,17 @@ function setupVars() { local profilesPath="$nixStateDir/profiles/per-user/$USER" local gcPath="$nixStateDir/gcroots/per-user/$USER" - genProfilePath="$profilesPath/home-manager" - newGenPath="@GENERATION_DIR@"; - newGenGcPath="$gcPath/current-home" + declare -gr genProfilePath="$profilesPath/home-manager" + declare -gr newGenPath="@GENERATION_DIR@"; + declare -gr newGenGcPath="$gcPath/current-home" + + declare -g newGenLayoutVersion + if [[ -f $newGenPath/version ]]; then + newGenLayoutVersion=$(< "$newGenPath/version") + else + newGenLayoutVersion=0 + fi + readonly newGenLayoutVersion local greatestGenNum greatestGenNum=$( \ @@ -23,7 +31,14 @@ function setupVars() { fi if [[ -e $profilesPath/home-manager ]] ; then + declare -g oldGenPath oldGenLayoutVersion oldGenPath="$(readlink -e "$profilesPath/home-manager")" + if [[ -f $oldGenPath/version ]]; then + oldGenLayoutVersion=$(< "$oldGenPath/version") + else + oldGenLayoutVersion=0 + fi + readonly oldGenPath oldGenLayoutVersion fi $VERBOSE_ECHO "Sanity checking oldGenNum and oldGenPath"