diff --git a/README.md b/README.md index b5ed92b35..0b946a98d 100644 --- a/README.md +++ b/README.md @@ -9,32 +9,23 @@ read the warning below. Words of warning ---------------- -This project is in early development! I personally use it to manage +This project is under development. I personally use it to manage several user configurations but it may fail catastrophically for you. So beware! -To configure programs and services the Home Manager must write various -things to your home directory and possibly overwrite files you have -previously created. For example, if you use Home Manager to install -and configure Git then your `~/.gitconfig` will be replaced by a link -to a configuration generated by Home Manager: +In some cases Home Manager cannot detect whether it will overwrite a +previous manual configuration. For example, the Gnome Terminal module +will write to your dconf store and cannot tell whether a configuration +that it is about to be overwrite was from a previous Home Manager +generation or from manual configuration. -``` -$ ls -gG ~/.gitconfig -lrwxrwxrwx 1 73 Jan 8 21:59 /home/rycee/.gitconfig -> /nix/store/pk7g12816avnxyhnkbdhqhnlzrw7fsga-home-manager-files/.gitconfig -``` +Home Manager targets [NixOS][] version 17.03 (the current stable +version), it may or may not work on other Linux distributions and +NixOS versions. -So, if you already have a wonderful, painstakingly created -`~/.gitconfig` it will be gone. Home Manager will _not_ attempt to -backup the previous `~/.gitconfig` file. - -Further, Home Manager targets [NixOS][] version 17.03 (the current -stable version), it may or may not work on other Linux distributions -and NixOS versions. - -Finally, the `home-manager` tool does not explicitly support rollbacks -at the moment so if your home directory gets messed up you'll have to -fix it yourself (you can attempt to run the activation script for the +Also, the `home-manager` tool does not explicitly support rollbacks at +the moment so if your home directory gets messed up you'll have to fix +it yourself (you can attempt to run the activation script for the desired generation). Now when your expectations have been built up and you are eager to try @@ -132,6 +123,44 @@ $ home-manager build which will create a `result` link to a directory containing an activation script and the generated home directory files. +File safety +----------- + +To configure programs and services the Home Manager must write various +things to your home directory. To prevent overwriting any existing +files when switching to a new generation, Home Manager will attempt to +detect collisions between existing files and generated files. If any +such collision is detected the activation will terminate before +changing anything on your computer. + +For example, suppose you have a wonderful, painstakingly created +`~/.gitconfig` and add + +```nix +{ + # … + + programs.git = { + enable = true; + userName = "Jane Doe"; + userEmail = "jane.doe@example.org"; + }; + + # … +} +``` + +to your configuration. Attempting to switch to the generation will +then result in + +``` +$ home-manager switch +… +Activating checkLinkTargets +Existing file '/home/jdoe/.gitconfig' is in the way +Please move the above files and try again +``` + [Nix]: https://nixos.org/nix/ [NixOS]: https://nixos.org/ [Nixpkgs]: https://nixos.org/nixpkgs/ diff --git a/modules/home-environment.nix b/modules/home-environment.nix index b270b1cc3..3b62b77f4 100644 --- a/modules/home-environment.nix +++ b/modules/home-environment.nix @@ -246,6 +246,37 @@ in # script's "check" and the "write" phases. home.activation.writeBoundary = dagEntryAnywhere ""; + # This verifies that the links we are about to create will not + # overwrite an existing file. + home.activation.checkLinkTargets = dagEntryBefore ["writeBoundary"] ( + let + pattern = "-home-manager-files/"; + check = pkgs.writeText "check" '' + newGenFiles="$1" + shift + for sourcePath in "$@" ; do + relativePath="''${sourcePath#$newGenFiles/}" + targetPath="$HOME/$relativePath" + if [[ -e "$targetPath" \ + && ! "$(readlink -e "$targetPath")" =~ "${pattern}" ]] ; then + echo -e "Existing file '$targetPath' is in the way" + collision=1 + fi + done + + if [[ -v collision ]] ; then + echo -e "Please move the above files and try again" + exit 1 + fi + ''; + in + '' + newGenFiles="$(readlink -e "$newGenPath/home-files")" + find "$newGenFiles" -type f -print0 -or -type l -print0 \ + | xargs -0 bash ${check} "$newGenFiles" + '' + ); + home.activation.linkGeneration = dagEntryAfter ["writeBoundary"] ( let link = pkgs.writeText "link" ''