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 {
|
pkgs.stdenv.mkDerivation {
|
||||||
name = "home-manager";
|
name = "home-manager";
|
||||||
|
|
||||||
phases = [ "installPhase" ];
|
buildCommand = ''
|
||||||
|
|
||||||
installPhase = ''
|
|
||||||
install -v -D -m755 ${./home-manager} $out/bin/home-manager
|
install -v -D -m755 ${./home-manager} $out/bin/home-manager
|
||||||
|
|
||||||
substituteInPlace $out/bin/home-manager \
|
substituteInPlace $out/bin/home-manager \
|
||||||
|
|
|
@ -6,8 +6,17 @@ with import ./lib/dag.nix { inherit lib; };
|
||||||
let
|
let
|
||||||
|
|
||||||
cfg = config.home.file;
|
cfg = config.home.file;
|
||||||
|
|
||||||
homeDirectory = config.home.homeDirectory;
|
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
|
in
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -15,50 +24,7 @@ in
|
||||||
home.file = mkOption {
|
home.file = mkOption {
|
||||||
description = "Attribute set of files to link into the user home.";
|
description = "Attribute set of files to link into the user home.";
|
||||||
default = {};
|
default = {};
|
||||||
type = types.loaOf (types.submodule (
|
type = fileType "<envar>HOME</envar>" homeDirectory;
|
||||||
{ 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)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
})
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
home-files = mkOption {
|
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
|
# This verifies that the links we are about to create will not
|
||||||
# overwrite an existing file.
|
# overwrite an existing file.
|
||||||
home.activation.checkLinkTargets = dagEntryBefore ["writeBoundary"] (
|
home.activation.checkLinkTargets = dagEntryBefore ["writeBoundary"] (
|
||||||
let
|
let
|
||||||
pattern = "-home-manager-files/";
|
|
||||||
check = pkgs.writeText "check" ''
|
check = pkgs.writeText "check" ''
|
||||||
. ${./lib-bash/color-echo.sh}
|
. ${./lib-bash/color-echo.sh}
|
||||||
|
|
||||||
|
@ -97,7 +75,7 @@ in
|
||||||
relativePath="''${sourcePath#$newGenFiles/}"
|
relativePath="''${sourcePath#$newGenFiles/}"
|
||||||
targetPath="$HOME/$relativePath"
|
targetPath="$HOME/$relativePath"
|
||||||
if [[ -e "$targetPath" \
|
if [[ -e "$targetPath" \
|
||||||
&& ! "$(readlink "$targetPath")" =~ "${pattern}" ]] ; then
|
&& ! "$(readlink "$targetPath")" == ${homeFilePattern} ]] ; then
|
||||||
errorEcho "Existing file '$targetPath' is in the way"
|
errorEcho "Existing file '$targetPath' is in the way"
|
||||||
collision=1
|
collision=1
|
||||||
fi
|
fi
|
||||||
|
@ -123,8 +101,6 @@ in
|
||||||
|
|
||||||
home.activation.linkGeneration = dagEntryAfter ["writeBoundary"] (
|
home.activation.linkGeneration = dagEntryAfter ["writeBoundary"] (
|
||||||
let
|
let
|
||||||
pattern = "-home-manager-files/";
|
|
||||||
|
|
||||||
link = pkgs.writeText "link" ''
|
link = pkgs.writeText "link" ''
|
||||||
newGenFiles="$1"
|
newGenFiles="$1"
|
||||||
shift
|
shift
|
||||||
|
@ -147,7 +123,7 @@ in
|
||||||
targetPath="$HOME/$relativePath"
|
targetPath="$HOME/$relativePath"
|
||||||
if [[ -e "$newGenFiles/$relativePath" ]] ; then
|
if [[ -e "$newGenFiles/$relativePath" ]] ; then
|
||||||
$VERBOSE_ECHO "Checking $targetPath: exists"
|
$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."
|
warnEcho "Path '$targetPath' not link into Home Manager generation. Skipping delete."
|
||||||
else
|
else
|
||||||
$VERBOSE_ECHO "Checking $targetPath: gone (deleting)"
|
$VERBOSE_ECHO "Checking $targetPath: gone (deleting)"
|
||||||
|
@ -210,30 +186,57 @@ in
|
||||||
home-files = pkgs.stdenv.mkDerivation {
|
home-files = pkgs.stdenv.mkDerivation {
|
||||||
name = "home-manager-files";
|
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 =
|
function insertFile() {
|
||||||
"mkdir -p $out\n" +
|
local source="$1"
|
||||||
concatStringsSep "\n" (
|
local relTarget="$2"
|
||||||
mapAttrsToList (n: v:
|
local executable="$3"
|
||||||
''
|
local mode="$4" # For backwards compatibility.
|
||||||
target="$(realpath -m "$out/${v.target}")"
|
|
||||||
|
|
||||||
# Target file must be within $HOME.
|
# Figure out the real absolute path to the target.
|
||||||
if [[ ! "$target" =~ "$out" ]] ; then
|
local target
|
||||||
echo "Error installing file '${v.target}' outside \$HOME" >&2
|
target="$(realpath -m "$out/$relTarget")"
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -d "${v.source}" ]; then
|
# Target path must be within $HOME.
|
||||||
mkdir -p "$(dirname "$out/${v.target}")"
|
if [[ ! $target =~ $out ]] ; then
|
||||||
ln -s "${v.source}" "$target"
|
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
|
else
|
||||||
install -D -m${v.mode} "${v.source}" "$target"
|
chmod -x "$target"
|
||||||
fi
|
fi
|
||||||
''
|
fi
|
||||||
) cfg
|
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
|
pkgs.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
sf = pkgs.writeText "activation-script" ''
|
activationScript = pkgs.writeScript "activation-script" ''
|
||||||
#!${pkgs.stdenv.shell}
|
#!${pkgs.stdenv.shell}
|
||||||
|
|
||||||
set -eu
|
set -eu
|
||||||
|
@ -283,10 +283,10 @@ in
|
||||||
pkgs.stdenv.mkDerivation {
|
pkgs.stdenv.mkDerivation {
|
||||||
name = "home-manager-generation";
|
name = "home-manager-generation";
|
||||||
|
|
||||||
phases = [ "installPhase" ];
|
buildCommand = ''
|
||||||
|
mkdir -p $out
|
||||||
|
|
||||||
installPhase = ''
|
cp ${activationScript} $out/activate
|
||||||
install -D -m755 ${sf} $out/activate
|
|
||||||
|
|
||||||
substituteInPlace $out/activate \
|
substituteInPlace $out/activate \
|
||||||
--subst-var-by GENERATION_DIR $out
|
--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'.
|
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;
|
cfg = config.xdg;
|
||||||
|
|
||||||
fileType = basePathDesc: basePath: (types.loaOf (types.submodule (
|
fileType = (import ../lib/file-type.nix {
|
||||||
{ name, config, ... }: {
|
inherit (config.home) homeDirectory;
|
||||||
options = {
|
inherit lib pkgs;
|
||||||
target = mkOption {
|
}).fileType;
|
||||||
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
|
|
||||||
);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
)));
|
|
||||||
|
|
||||||
defaultCacheHome = "${config.home.homeDirectory}/.cache";
|
defaultCacheHome = "${config.home.homeDirectory}/.cache";
|
||||||
defaultConfigHome = "${config.home.homeDirectory}/.config";
|
defaultConfigHome = "${config.home.homeDirectory}/.config";
|
||||||
|
@ -81,7 +36,7 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
configFile = mkOption {
|
configFile = mkOption {
|
||||||
type = fileType "xdg.configHome" cfg.configHome;
|
type = fileType "<varname>xdg.configHome</varname>" cfg.configHome;
|
||||||
default = {};
|
default = {};
|
||||||
description = ''
|
description = ''
|
||||||
Attribute set of files to link into the user's XDG
|
Attribute set of files to link into the user's XDG
|
||||||
|
@ -126,13 +81,7 @@ in
|
||||||
})
|
})
|
||||||
|
|
||||||
{
|
{
|
||||||
home.file =
|
home.file = cfg.configFile;
|
||||||
let
|
|
||||||
f = n: v: {
|
|
||||||
inherit (v) source target;
|
|
||||||
mode = if v.executable then "777" else "444";
|
|
||||||
};
|
|
||||||
in mapAttrsToList f cfg.configFile;
|
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,7 +105,7 @@ in
|
||||||
'';
|
'';
|
||||||
|
|
||||||
home.file.".xsession" = {
|
home.file.".xsession" = {
|
||||||
mode = "555";
|
executable = true;
|
||||||
text = ''
|
text = ''
|
||||||
if [[ ! -v HM_XPROFILE_SOURCED ]]; then
|
if [[ ! -v HM_XPROFILE_SOURCED ]]; then
|
||||||
. ~/.xprofile
|
. ~/.xprofile
|
||||||
|
|
Loading…
Reference in a new issue