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`.
This commit is contained in:
Robert Helgesson 2019-01-16 02:14:14 +01:00
parent 46f787950a
commit 7c04351a57
No known key found for this signature in database
GPG key ID: 36BDAA14C2797E89
12 changed files with 121 additions and 38 deletions

View file

@ -14,6 +14,15 @@ let
inherit homeDirectory lib pkgs; inherit homeDirectory lib pkgs;
}).fileType; }).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 # A symbolic link whose target path matches this pattern will be
# considered part of a Home Manager generation. # considered part of a Home Manager generation.
homeFilePattern = "${builtins.storeDir}/*-home-manager-files/*"; homeFilePattern = "${builtins.storeDir}/*-home-manager-files/*";
@ -36,20 +45,6 @@ in
}; };
config = { 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 # This verifies that the links we are about to create will not
# overwrite an existing file. # overwrite an existing file.
home.activation.checkLinkTargets = dag.entryBefore ["writeBoundary"] ( home.activation.checkLinkTargets = dag.entryBefore ["writeBoundary"] (
@ -201,7 +196,7 @@ in
'' ''
declare -A changedFiles declare -A changedFiles
'' + concatMapStrings (v: '' '' + 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}"]=0 \
|| changedFiles["${v.target}"]=1 || changedFiles["${v.target}"]=1
'') (filter (v: v.onChange != "") (attrValues cfg)) '') (filter (v: v.onChange != "") (attrValues cfg))
@ -277,7 +272,7 @@ in
} }
'' + concatStrings ( '' + concatStrings (
mapAttrsToList (n: v: '' mapAttrsToList (n: v: ''
insertFile "${v.source}" \ insertFile "${sourceStorePath v}" \
"${v.target}" \ "${v.target}" \
"${if v.executable == null "${if v.executable == null
then "inherit" then "inherit"

View file

@ -16,6 +16,8 @@
entryBefore = d.dagEntryBefore; entryBefore = d.dagEntryBefore;
}; };
strings = import ./strings.nix { inherit lib; };
shell = import ./shell.nix { inherit lib; }; shell = import ./shell.nix { inherit lib; };
zsh = import ./zsh.nix { inherit lib; }; zsh = import ./zsh.nix { inherit lib; };
} }

View file

@ -4,27 +4,7 @@ with lib;
let let
# Figures out a valid Nix store name for the given path. stringsExtra = import ./strings.nix { inherit lib; };
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;
in in
@ -113,7 +93,7 @@ in
source = mkIf (config.text != null) ( source = mkIf (config.text != null) (
mkDefault (pkgs.writeTextFile { mkDefault (pkgs.writeTextFile {
inherit (config) executable text; inherit (config) executable text;
name = storeFileName name; name = stringsExtra.storeFileName name;
}) })
); );
}; };

27
modules/lib/strings.nix Normal file
View file

@ -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;
}

View file

@ -16,6 +16,10 @@ import nmt {
modules = import ../modules/modules.nix { inherit pkgs; lib = pkgs.lib; }; modules = import ../modules/modules.nix { inherit pkgs; lib = pkgs.lib; };
testedAttrPath = [ "home" "activationPackage" ]; testedAttrPath = [ "home" "activationPackage" ];
tests = { 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-most-options = ./modules/programs/git.nix;
git-with-str-extra-config = ./modules/programs/git-with-str-extra-config.nix; git-with-str-extra-config = ./modules/programs/git-with-str-extra-config.nix;
texlive-minimal = ./modules/programs/texlive-minimal.nix; texlive-minimal = ./modules/programs/texlive-minimal.nix;

View file

@ -0,0 +1 @@
The name of this file has a dot prefix.

View file

@ -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;
'';
};
}

View file

@ -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"; }
}
'';
};
}

View file

@ -0,0 +1 @@
Source with spaces!

View file

@ -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";
}
}
'';
};
}

View file

@ -0,0 +1,2 @@
This is the
expected text.

View file

@ -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}
'';
};
}