diff --git a/modules/files.nix b/modules/files.nix index a9b8740db..1f79d5c15 100644 --- a/modules/files.nix +++ b/modules/files.nix @@ -14,6 +14,15 @@ let inherit homeDirectory lib pkgs; }).fileType; + sourceStorePath = file: + let + sourcePath = toString file.source; + sourceName = config.lib.strings.storeFileName (baseNameOf sourcePath); + in + if builtins.hasContext sourcePath + then file.source + else builtins.path { path = file.source; name = sourceName; }; + # A symbolic link whose target path matches this pattern will be # considered part of a Home Manager generation. homeFilePattern = "${builtins.storeDir}/*-home-manager-files/*"; @@ -36,20 +45,6 @@ in }; config = { - assertions = [ - (let - badFiles = - filter (f: hasPrefix "." (baseNameOf f)) - (map (v: toString v.source) - (attrValues cfg)); - badFilesStr = toString badFiles; - in - { - assertion = badFiles == []; - message = "Source file names must not start with '.': ${badFilesStr}"; - }) - ]; - # This verifies that the links we are about to create will not # overwrite an existing file. home.activation.checkLinkTargets = dag.entryBefore ["writeBoundary"] ( @@ -201,7 +196,7 @@ in '' declare -A changedFiles '' + concatMapStrings (v: '' - cmp --quiet "${v.source}" "${config.home.homeDirectory}/${v.target}" \ + cmp --quiet "${sourceStorePath v}" "${homeDirectory}/${v.target}" \ && changedFiles["${v.target}"]=0 \ || changedFiles["${v.target}"]=1 '') (filter (v: v.onChange != "") (attrValues cfg)) @@ -277,7 +272,7 @@ in } '' + concatStrings ( mapAttrsToList (n: v: '' - insertFile "${v.source}" \ + insertFile "${sourceStorePath v}" \ "${v.target}" \ "${if v.executable == null then "inherit" diff --git a/modules/lib/default.nix b/modules/lib/default.nix index 9ca0dad6b..754713cab 100644 --- a/modules/lib/default.nix +++ b/modules/lib/default.nix @@ -16,6 +16,8 @@ entryBefore = d.dagEntryBefore; }; + strings = import ./strings.nix { inherit lib; }; + shell = import ./shell.nix { inherit lib; }; zsh = import ./zsh.nix { inherit lib; }; } diff --git a/modules/lib/file-type.nix b/modules/lib/file-type.nix index 0c435aaa9..efbb33b23 100644 --- a/modules/lib/file-type.nix +++ b/modules/lib/file-type.nix @@ -4,27 +4,7 @@ with lib; let - # Figures out a valid Nix store name for the given path. - storeFileName = path: - let - # All characters that are considered safe. Note "-" is not - # included to avoid "-" followed by digit being interpreted as a - # version. - safeChars = - [ "+" "." "_" "?" "=" ] - ++ lowerChars - ++ upperChars - ++ stringToCharacters "0123456789"; - - empties = l: genList (x: "") (length l); - - unsafeInName = stringToCharacters ( - replaceStrings safeChars (empties safeChars) path - ); - - safeName = replaceStrings unsafeInName (empties unsafeInName) path; - in - "home_file_" + safeName; + stringsExtra = import ./strings.nix { inherit lib; }; in @@ -113,7 +93,7 @@ in source = mkIf (config.text != null) ( mkDefault (pkgs.writeTextFile { inherit (config) executable text; - name = storeFileName name; + name = stringsExtra.storeFileName name; }) ); }; diff --git a/modules/lib/strings.nix b/modules/lib/strings.nix new file mode 100644 index 000000000..13d6bb03b --- /dev/null +++ b/modules/lib/strings.nix @@ -0,0 +1,27 @@ +{ lib }: + +with lib; + +{ + # Figures out a valid Nix store name for the given path. + storeFileName = path: + let + # All characters that are considered safe. Note "-" is not + # included to avoid "-" followed by digit being interpreted as a + # version. + safeChars = + [ "+" "." "_" "?" "=" ] + ++ lowerChars + ++ upperChars + ++ stringToCharacters "0123456789"; + + empties = l: genList (x: "") (length l); + + unsafeInName = stringToCharacters ( + replaceStrings safeChars (empties safeChars) path + ); + + safeName = replaceStrings unsafeInName (empties unsafeInName) path; + in + "hm_" + safeName; +} diff --git a/tests/default.nix b/tests/default.nix index 4e7858a9b..1da2abf17 100644 --- a/tests/default.nix +++ b/tests/default.nix @@ -16,6 +16,10 @@ import nmt { modules = import ../modules/modules.nix { inherit pkgs; lib = pkgs.lib; }; testedAttrPath = [ "home" "activationPackage" ]; tests = { + files-executable = ./modules/files/executable.nix; + files-hidden-source = ./modules/files/hidden-source.nix; + files-source-with-spaces = ./modules/files/source-with-spaces.nix; + files-text = ./modules/files/text.nix; git-with-most-options = ./modules/programs/git.nix; git-with-str-extra-config = ./modules/programs/git-with-str-extra-config.nix; texlive-minimal = ./modules/programs/texlive-minimal.nix; diff --git a/tests/modules/files/.hidden b/tests/modules/files/.hidden new file mode 100644 index 000000000..ca05448e7 --- /dev/null +++ b/tests/modules/files/.hidden @@ -0,0 +1 @@ +The name of this file has a dot prefix. diff --git a/tests/modules/files/executable.nix b/tests/modules/files/executable.nix new file mode 100644 index 000000000..b286c2b49 --- /dev/null +++ b/tests/modules/files/executable.nix @@ -0,0 +1,17 @@ +{ config, lib, ... }: + +with lib; + +{ + config = { + home.file."executable" = { + text = ""; + executable = true; + }; + + nmt.script = '' + assertFileExists home-files/executable + assertFileIsExecutable home-files/executable; + ''; + }; +} diff --git a/tests/modules/files/hidden-source.nix b/tests/modules/files/hidden-source.nix new file mode 100644 index 000000000..a8635398f --- /dev/null +++ b/tests/modules/files/hidden-source.nix @@ -0,0 +1,16 @@ +{ config, lib, ... }: + +with lib; + +{ + config = { + home.file.".hidden".source = ./.hidden; + + nmt.script = '' + assertFileExists home-files/.hidden; + assertFileContent home-files/.hidden ${ + builtins.path { path = ./.hidden; name = "expected"; } + } + ''; + }; +} diff --git a/tests/modules/files/source with spaces! b/tests/modules/files/source with spaces! new file mode 100644 index 000000000..e1ace4041 --- /dev/null +++ b/tests/modules/files/source with spaces! @@ -0,0 +1 @@ +Source with spaces! diff --git a/tests/modules/files/source-with-spaces.nix b/tests/modules/files/source-with-spaces.nix new file mode 100644 index 000000000..1d593c642 --- /dev/null +++ b/tests/modules/files/source-with-spaces.nix @@ -0,0 +1,20 @@ +{ config, lib, ... }: + +with lib; + +{ + config = { + home.file."source with spaces!".source = ./. + "/source with spaces!"; + + nmt.script = '' + assertFileExists 'home-files/source with spaces!'; + assertFileContent 'home-files/source with spaces!' \ + ${ + builtins.path { + path = ./. + "/source with spaces!"; + name = "source-with-spaces-expected"; + } + } + ''; + }; +} diff --git a/tests/modules/files/text-expected.txt b/tests/modules/files/text-expected.txt new file mode 100644 index 000000000..b3a0ff2db --- /dev/null +++ b/tests/modules/files/text-expected.txt @@ -0,0 +1,2 @@ +This is the +expected text. diff --git a/tests/modules/files/text.nix b/tests/modules/files/text.nix new file mode 100644 index 000000000..6fc9a26fc --- /dev/null +++ b/tests/modules/files/text.nix @@ -0,0 +1,18 @@ +{ config, lib, ... }: + +with lib; + +{ + config = { + home.file."using-text".text = '' + This is the + expected text. + ''; + + nmt.script = '' + assertFileExists home-files/using-text + assertFileIsNotExecutable home-files/using-text + assertFileContent home-files/using-text ${./text-expected.txt} + ''; + }; +}