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:
parent
f174f90fdf
commit
4bed99c71c
|
@ -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"
|
||||
|
|
|
@ -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; };
|
||||
}
|
||||
|
|
|
@ -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
27
modules/lib/strings.nix
Normal 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;
|
||||
}
|
|
@ -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;
|
||||
|
|
1
tests/modules/files/.hidden
Normal file
1
tests/modules/files/.hidden
Normal file
|
@ -0,0 +1 @@
|
|||
The name of this file has a dot prefix.
|
17
tests/modules/files/executable.nix
Normal file
17
tests/modules/files/executable.nix
Normal 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;
|
||||
'';
|
||||
};
|
||||
}
|
16
tests/modules/files/hidden-source.nix
Normal file
16
tests/modules/files/hidden-source.nix
Normal 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"; }
|
||||
}
|
||||
'';
|
||||
};
|
||||
}
|
1
tests/modules/files/source with spaces!
Normal file
1
tests/modules/files/source with spaces!
Normal file
|
@ -0,0 +1 @@
|
|||
Source with spaces!
|
20
tests/modules/files/source-with-spaces.nix
Normal file
20
tests/modules/files/source-with-spaces.nix
Normal 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";
|
||||
}
|
||||
}
|
||||
'';
|
||||
};
|
||||
}
|
2
tests/modules/files/text-expected.txt
Normal file
2
tests/modules/files/text-expected.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
This is the
|
||||
expected text.
|
18
tests/modules/files/text.nix
Normal file
18
tests/modules/files/text.nix
Normal 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}
|
||||
'';
|
||||
};
|
||||
}
|
Loading…
Reference in a new issue