mirror of
https://github.com/nix-community/home-manager
synced 2024-12-24 18:59:47 +01:00
Merge branch 'pr-59'
This commit is contained in:
commit
a0afb6ec8e
7 changed files with 205 additions and 133 deletions
|
@ -15,9 +15,7 @@ in
|
|||
pkgs.stdenv.mkDerivation {
|
||||
name = "home-manager";
|
||||
|
||||
phases = [ "installPhase" ];
|
||||
|
||||
installPhase = ''
|
||||
buildCommand = ''
|
||||
install -v -D -m755 ${./home-manager} $out/bin/home-manager
|
||||
|
||||
substituteInPlace $out/bin/home-manager \
|
||||
|
|
|
@ -6,8 +6,17 @@ with import ./lib/dag.nix { inherit lib; };
|
|||
let
|
||||
|
||||
cfg = config.home.file;
|
||||
|
||||
homeDirectory = config.home.homeDirectory;
|
||||
|
||||
fileType = (import lib/file-type.nix {
|
||||
inherit homeDirectory lib pkgs;
|
||||
}).fileType;
|
||||
|
||||
# A symbolic link whose target path matches this pattern will be
|
||||
# considered part of a Home Manager generation.
|
||||
homeFilePattern = "${builtins.storeDir}/*-home-manager-files/*";
|
||||
|
||||
in
|
||||
|
||||
{
|
||||
|
@ -15,50 +24,7 @@ in
|
|||
home.file = mkOption {
|
||||
description = "Attribute set of files to link into the user home.";
|
||||
default = {};
|
||||
type = types.loaOf (types.submodule (
|
||||
{ name, config, ... }: {
|
||||
options = {
|
||||
target = mkOption {
|
||||
type = types.str;
|
||||
apply = removePrefix (homeDirectory + "/");
|
||||
description = ''
|
||||
Path to target file relative to <envar>HOME</envar>.
|
||||
'';
|
||||
};
|
||||
|
||||
text = mkOption {
|
||||
default = null;
|
||||
type = types.nullOr types.lines;
|
||||
description = "Text of the file.";
|
||||
};
|
||||
|
||||
source = mkOption {
|
||||
type = types.path;
|
||||
description = ''
|
||||
Path of the source file. The file name must not start
|
||||
with a period since Nix will not allow such names in
|
||||
the Nix store.
|
||||
</para><para>
|
||||
This may refer to a directory.
|
||||
'';
|
||||
};
|
||||
|
||||
mode = mkOption {
|
||||
type = types.str;
|
||||
default = "444";
|
||||
description = "The permissions to apply to the file.";
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
target = mkDefault name;
|
||||
source = mkIf (config.text != null) (
|
||||
let name' = "user-etc-" + baseNameOf name;
|
||||
in mkDefault (pkgs.writeText name' config.text)
|
||||
);
|
||||
};
|
||||
})
|
||||
);
|
||||
type = fileType "<envar>HOME</envar>" homeDirectory;
|
||||
};
|
||||
|
||||
home-files = mkOption {
|
||||
|
@ -83,11 +49,23 @@ in
|
|||
})
|
||||
];
|
||||
|
||||
warnings =
|
||||
let
|
||||
badFiles =
|
||||
map (f: f.target)
|
||||
(filter (f: f.mode != null)
|
||||
(attrValues cfg));
|
||||
badFilesStr = toString badFiles;
|
||||
in
|
||||
mkIf (badFiles != []) [
|
||||
("The 'mode' field is deprecated for 'home.file', "
|
||||
+ "use 'executable' instead: ${badFilesStr}")
|
||||
];
|
||||
|
||||
# This verifies that the links we are about to create will not
|
||||
# overwrite an existing file.
|
||||
home.activation.checkLinkTargets = dagEntryBefore ["writeBoundary"] (
|
||||
let
|
||||
pattern = "-home-manager-files/";
|
||||
check = pkgs.writeText "check" ''
|
||||
. ${./lib-bash/color-echo.sh}
|
||||
|
||||
|
@ -97,7 +75,7 @@ in
|
|||
relativePath="''${sourcePath#$newGenFiles/}"
|
||||
targetPath="$HOME/$relativePath"
|
||||
if [[ -e "$targetPath" \
|
||||
&& ! "$(readlink "$targetPath")" =~ "${pattern}" ]] ; then
|
||||
&& ! "$(readlink "$targetPath")" == ${homeFilePattern} ]] ; then
|
||||
errorEcho "Existing file '$targetPath' is in the way"
|
||||
collision=1
|
||||
fi
|
||||
|
@ -123,8 +101,6 @@ in
|
|||
|
||||
home.activation.linkGeneration = dagEntryAfter ["writeBoundary"] (
|
||||
let
|
||||
pattern = "-home-manager-files/";
|
||||
|
||||
link = pkgs.writeText "link" ''
|
||||
newGenFiles="$1"
|
||||
shift
|
||||
|
@ -147,7 +123,7 @@ in
|
|||
targetPath="$HOME/$relativePath"
|
||||
if [[ -e "$newGenFiles/$relativePath" ]] ; then
|
||||
$VERBOSE_ECHO "Checking $targetPath: exists"
|
||||
elif [[ ! "$(readlink "$targetPath")" =~ "${pattern}" ]] ; then
|
||||
elif [[ ! "$(readlink "$targetPath")" == ${homeFilePattern} ]] ; then
|
||||
warnEcho "Path '$targetPath' not link into Home Manager generation. Skipping delete."
|
||||
else
|
||||
$VERBOSE_ECHO "Checking $targetPath: gone (deleting)"
|
||||
|
@ -210,30 +186,57 @@ in
|
|||
home-files = pkgs.stdenv.mkDerivation {
|
||||
name = "home-manager-files";
|
||||
|
||||
phases = [ "installPhase" ];
|
||||
# Symlink directories and files that have the right execute bit.
|
||||
# Copy files that need their execute bit changed or use the
|
||||
# deprecated 'mode' option.
|
||||
buildCommand = ''
|
||||
mkdir -p $out
|
||||
|
||||
installPhase =
|
||||
"mkdir -p $out\n" +
|
||||
concatStringsSep "\n" (
|
||||
mapAttrsToList (n: v:
|
||||
''
|
||||
target="$(realpath -m "$out/${v.target}")"
|
||||
function insertFile() {
|
||||
local source="$1"
|
||||
local relTarget="$2"
|
||||
local executable="$3"
|
||||
local mode="$4" # For backwards compatibility.
|
||||
|
||||
# Target file must be within $HOME.
|
||||
if [[ ! "$target" =~ "$out" ]] ; then
|
||||
echo "Error installing file '${v.target}' outside \$HOME" >&2
|
||||
exit 1
|
||||
fi
|
||||
# Figure out the real absolute path to the target.
|
||||
local target
|
||||
target="$(realpath -m "$out/$relTarget")"
|
||||
|
||||
if [ -d "${v.source}" ]; then
|
||||
mkdir -p "$(dirname "$out/${v.target}")"
|
||||
ln -s "${v.source}" "$target"
|
||||
# Target path must be within $HOME.
|
||||
if [[ ! $target =~ $out ]] ; then
|
||||
echo "Error installing file '$relTarget' outside \$HOME" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$(dirname "$target")"
|
||||
if [[ -d $source ]]; then
|
||||
ln -s "$source" "$target"
|
||||
elif [[ $mode ]]; then
|
||||
install -m "$mode" "$source" "$target"
|
||||
else
|
||||
[[ -x $source ]] && isExecutable=1 || isExecutable=""
|
||||
if [[ $executable == symlink || $isExecutable == $executable ]]; then
|
||||
ln -s "$source" "$target"
|
||||
else
|
||||
cp "$source" "$target"
|
||||
if [[ $executable ]]; then
|
||||
chmod +x "$target"
|
||||
else
|
||||
install -D -m${v.mode} "${v.source}" "$target"
|
||||
chmod -x "$target"
|
||||
fi
|
||||
''
|
||||
) cfg
|
||||
);
|
||||
fi
|
||||
fi
|
||||
}
|
||||
'' + concatStrings (
|
||||
mapAttrsToList (n: v: ''
|
||||
insertFile "${v.source}" \
|
||||
"${v.target}" \
|
||||
"${if v.executable == null
|
||||
then "symlink"
|
||||
else builtins.toString v.executable}" \
|
||||
"${builtins.toString v.mode}"
|
||||
'') cfg
|
||||
);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
@ -265,7 +265,7 @@ in
|
|||
pkgs.nix
|
||||
];
|
||||
|
||||
sf = pkgs.writeText "activation-script" ''
|
||||
activationScript = pkgs.writeScript "activation-script" ''
|
||||
#!${pkgs.stdenv.shell}
|
||||
|
||||
set -eu
|
||||
|
@ -283,10 +283,10 @@ in
|
|||
pkgs.stdenv.mkDerivation {
|
||||
name = "home-manager-generation";
|
||||
|
||||
phases = [ "installPhase" ];
|
||||
buildCommand = ''
|
||||
mkdir -p $out
|
||||
|
||||
installPhase = ''
|
||||
install -D -m755 ${sf} $out/activate
|
||||
cp ${activationScript} $out/activate
|
||||
|
||||
substituteInPlace $out/activate \
|
||||
--subst-var-by GENERATION_DIR $out
|
||||
|
|
105
modules/lib/file-type.nix
Normal file
105
modules/lib/file-type.nix
Normal file
|
@ -0,0 +1,105 @@
|
|||
{ homeDirectory, lib, pkgs }:
|
||||
|
||||
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;
|
||||
|
||||
in
|
||||
|
||||
{
|
||||
# Constructs a type suitable for a `home.file` like option. The
|
||||
# target path may be either absolute or relative, in which case it
|
||||
# is relative the `basePath` argument (which itself must be an
|
||||
# absolute path).
|
||||
#
|
||||
# Arguments:
|
||||
# - basePathDesc docbook compatible description of the base path
|
||||
# - basePath the file base path
|
||||
fileType = basePathDesc: basePath: types.loaOf (types.submodule (
|
||||
{ name, config, ... }: {
|
||||
options = {
|
||||
target = mkOption {
|
||||
type = types.str;
|
||||
apply = p:
|
||||
let
|
||||
absPath = if hasPrefix "/" p then p else "${basePath}/${p}";
|
||||
in
|
||||
removePrefix (homeDirectory + "/") absPath;
|
||||
description = ''
|
||||
Path to target file relative to ${basePathDesc}.
|
||||
'';
|
||||
};
|
||||
|
||||
text = mkOption {
|
||||
default = null;
|
||||
type = types.nullOr types.lines;
|
||||
description = "Text of the file.";
|
||||
};
|
||||
|
||||
source = mkOption {
|
||||
type = types.path;
|
||||
description = ''
|
||||
Path of the source file. The file name must not start
|
||||
with a period since Nix will not allow such names in
|
||||
the Nix store.
|
||||
</para><para>
|
||||
This may refer to a directory.
|
||||
'';
|
||||
};
|
||||
|
||||
mode = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
default = null;
|
||||
description = ''
|
||||
The permissions to apply to the file.
|
||||
</para><para>
|
||||
DEPRECATED: use <varname>home.file.<name?>.executable</varname>
|
||||
instead.
|
||||
'';
|
||||
};
|
||||
|
||||
executable = mkOption {
|
||||
type = types.nullOr types.bool;
|
||||
default = null;
|
||||
description = ''
|
||||
Set the execute bit. If <literal>null</literal>, defaults to the mode
|
||||
of the <varname>source</varname> file or to <literal>false</literal>
|
||||
for files created through the <varname>text</varname> option.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
target = mkDefault name;
|
||||
source = mkIf (config.text != null) (
|
||||
mkDefault (pkgs.writeTextFile {
|
||||
inherit (config) executable text;
|
||||
name = storeFileName name;
|
||||
})
|
||||
);
|
||||
};
|
||||
}
|
||||
));
|
||||
}
|
|
@ -425,6 +425,23 @@ in
|
|||
A new window manager module is available: 'xsession.windowManager.i3'.
|
||||
'';
|
||||
}
|
||||
|
||||
{
|
||||
time = "2017-11-06T13:23:17+00:00";
|
||||
condition = any (f: f.mode != null) (attrValues config.home.file);
|
||||
message = ''
|
||||
The
|
||||
|
||||
home.file.<name?>.mode
|
||||
|
||||
option is now deprecated. Please use
|
||||
|
||||
home.file.<name?>.executable
|
||||
|
||||
instead. The 'mode' option will be completely removed
|
||||
December 6, 2017.
|
||||
'';
|
||||
}
|
||||
];
|
||||
};
|
||||
}
|
||||
|
|
|
@ -6,55 +6,10 @@ let
|
|||
|
||||
cfg = config.xdg;
|
||||
|
||||
fileType = basePathDesc: basePath: (types.loaOf (types.submodule (
|
||||
{ name, config, ... }: {
|
||||
options = {
|
||||
target = mkOption {
|
||||
type = types.str;
|
||||
apply = p: "${basePath}/${p}";
|
||||
description = ''
|
||||
Path to target file relative to <varname>${basePathDesc}</varname>.
|
||||
'';
|
||||
};
|
||||
|
||||
text = mkOption {
|
||||
default = null;
|
||||
type = types.nullOr types.lines;
|
||||
description = "Text of the file.";
|
||||
};
|
||||
|
||||
source = mkOption {
|
||||
type = types.path;
|
||||
description = ''
|
||||
Path of the source file. The file name must not start
|
||||
with a period since Nix will not allow such names in
|
||||
the Nix store.
|
||||
</para><para>
|
||||
This may refer to a directory.
|
||||
'';
|
||||
};
|
||||
|
||||
executable = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "Whether the file should be executable.";
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
target = mkDefault name;
|
||||
source = mkIf (config.text != null) (
|
||||
let
|
||||
file = pkgs.writeTextFile {
|
||||
inherit (config) text executable;
|
||||
name = "user-etc-" + baseNameOf name;
|
||||
};
|
||||
in
|
||||
mkDefault file
|
||||
);
|
||||
};
|
||||
}
|
||||
)));
|
||||
fileType = (import ../lib/file-type.nix {
|
||||
inherit (config.home) homeDirectory;
|
||||
inherit lib pkgs;
|
||||
}).fileType;
|
||||
|
||||
defaultCacheHome = "${config.home.homeDirectory}/.cache";
|
||||
defaultConfigHome = "${config.home.homeDirectory}/.config";
|
||||
|
@ -81,7 +36,7 @@ in
|
|||
};
|
||||
|
||||
configFile = mkOption {
|
||||
type = fileType "xdg.configHome" cfg.configHome;
|
||||
type = fileType "<varname>xdg.configHome</varname>" cfg.configHome;
|
||||
default = {};
|
||||
description = ''
|
||||
Attribute set of files to link into the user's XDG
|
||||
|
@ -126,13 +81,7 @@ in
|
|||
})
|
||||
|
||||
{
|
||||
home.file =
|
||||
let
|
||||
f = n: v: {
|
||||
inherit (v) source target;
|
||||
mode = if v.executable then "777" else "444";
|
||||
};
|
||||
in mapAttrsToList f cfg.configFile;
|
||||
home.file = cfg.configFile;
|
||||
}
|
||||
];
|
||||
}
|
||||
|
|
|
@ -105,7 +105,7 @@ in
|
|||
'';
|
||||
|
||||
home.file.".xsession" = {
|
||||
mode = "555";
|
||||
executable = true;
|
||||
text = ''
|
||||
if [[ ! -v HM_XPROFILE_SOURCED ]]; then
|
||||
. ~/.xprofile
|
||||
|
|
Loading…
Reference in a new issue