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`.

(cherry picked from commit 7c04351a57)
This commit is contained in:
Robert Helgesson 2019-01-16 02:14:14 +01:00
parent f174f90fdf
commit 4bed99c71c
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;
}).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"

View file

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

View file

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

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

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