From 7c04351a57bb36133e6cf3b609e573b9e18b8eec Mon Sep 17 00:00:00 2001 From: Robert Helgesson Date: Wed, 16 Jan 2019 02:14:14 +0100 Subject: [PATCH] files: allow a wider range of source file names In particular support source files whose name start with `.` or contain characters not allowed in the nix store, such as spaces. Also add some test cases for `home.file`. --- modules/files.nix | 27 +++++++++------------- modules/lib/default.nix | 2 ++ modules/lib/file-type.nix | 24 ++----------------- modules/lib/strings.nix | 27 ++++++++++++++++++++++ tests/default.nix | 4 ++++ tests/modules/files/.hidden | 1 + tests/modules/files/executable.nix | 17 ++++++++++++++ tests/modules/files/hidden-source.nix | 16 +++++++++++++ tests/modules/files/source with spaces! | 1 + tests/modules/files/source-with-spaces.nix | 20 ++++++++++++++++ tests/modules/files/text-expected.txt | 2 ++ tests/modules/files/text.nix | 18 +++++++++++++++ 12 files changed, 121 insertions(+), 38 deletions(-) create mode 100644 modules/lib/strings.nix create mode 100644 tests/modules/files/.hidden create mode 100644 tests/modules/files/executable.nix create mode 100644 tests/modules/files/hidden-source.nix create mode 100644 tests/modules/files/source with spaces! create mode 100644 tests/modules/files/source-with-spaces.nix create mode 100644 tests/modules/files/text-expected.txt create mode 100644 tests/modules/files/text.nix diff --git a/modules/files.nix b/modules/files.nix index a9b8740d..1f79d5c1 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 9ca0dad6..754713ca 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 0c435aaa..efbb33b2 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 00000000..13d6bb03 --- /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 4e7858a9..1da2abf1 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 00000000..ca05448e --- /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 00000000..b286c2b4 --- /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 00000000..a8635398 --- /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 00000000..e1ace404 --- /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 00000000..1d593c64 --- /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 00000000..b3a0ff2d --- /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 00000000..6fc9a26f --- /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} + ''; + }; +}