{ pkgs, tmpLib, lib, config, ... }:
let
homeFiles = lib.filterAttrs (name: conf: conf.enable) config.home.file;
sourceStorePath = file:
let
sourcePath = toString file.source;
sourceName = tmpLib.storeFileName (baseNameOf sourcePath);
in
if builtins.hasContext sourcePath
then file.source
else builtins.path { path = file.source; name = sourceName; };
in {
options = {
home = {
user = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
};
group = lib.mkOption {
type = lib.types.nullOr lib.types.str;
default = null;
};
homeDirectory = lib.mkOption {
type = lib.types.str;
default = "/home/${config.home.user}";
};
file = lib.mkOption {
type = lib.types.attrsOf tmpLib.homeFileType;
default = {};
};
packages = lib.mkOption {
type = lib.types.listOf lib.types.package;
default = [];
};
homeFilesPackage = lib.mkOption {
type = lib.types.package;
};
path = lib.mkOption {
type = lib.types.package;
};
pathsToLink = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ "/bin" "/share" ];
};
};
};
config = lib.mkIf (config.home.user != null) {
# tmpfiles.selfManagement.targetDir = "${config.home.homeDirectory}/.local/share/user-tmpfiles.d";
tmpfiles.defaultUser = config.home.user;
tmpfiles.defaultGroup = config.home.group;
home.path = pkgs.buildEnv {
name = "tmpactivator-profile";
paths = config.home.packages;
pathsToLink = config.home.pathsToLink;
};
tmpfiles.instances.home.files = [
{
type = "L+";
mode = "-";
user = "-";
group = "-";
text = "${config.home.path}";
target = "${config.home.homeDirectory}/.tmpactivator-profile";
}
] ++ (lib.attrValues (lib.mapAttrs (name: conf: {
type = "L+";
mode = "-";
user = "-";
group = "-";
text = "${config.home.homeFilesPackage}/${name}";
target = "${config.home.homeDirectory}/${name}";
}) homeFiles)) ++ (lib.attrValues (lib.mapAttrs (name: conf: {
type = "r";
mode = "-";
user = "-";
group = "-";
text = "-";
target = "${config.home.homeDirectory}/${name}";
}) homeFiles));
# This is from home-manager, see
# https://github.com/nix-community/home-manager/blob/6e090576c4824b16e8759ebca3958c5b09659ee8/modules/files.nix#L285
home.homeFilesPackage = pkgs.runCommandLocal "home-files" {
nativeBuildInputs = [ pkgs.xorg.lndir ];
}
(''
mkdir -p $out
# Needed in case /nix is a symbolic link.
realOut="$(realpath -m "$out")"
function insertFile() {
local source="$1"
local relTarget="$2"
local executable="$3"
local recursive="$4"
# If the target already exists then we have a collision. Note, this
# should not happen due to the assertion found in the 'files' module.
# We therefore simply log the conflict and otherwise ignore it, mainly
# to make the `files-target-config` test work as expected.
if [[ -e "$realOut/$relTarget" ]]; then
echo "File conflict for file '$relTarget'" >&2
return
fi
# Figure out the real absolute path to the target.
local target
target="$(realpath -m "$realOut/$relTarget")"
# Target path must be within $HOME.
if [[ ! $target == $realOut* ]] ; then
echo "Error installing file '$relTarget' outside \$HOME" >&2
exit 1
fi
mkdir -p "$(dirname "$target")"
if [[ -d $source ]]; then
if [[ $recursive ]]; then
mkdir -p "$target"
lndir -silent "$source" "$target"
else
ln -s "$source" "$target"
fi
else
[[ -x $source ]] && isExecutable=1 || isExecutable=""
# Link the file into the home file directory if possible,
# i.e., if the executable bit of the source is the same we
# expect for the target. Otherwise, we copy the file and
# set the executable bit to the expected value.
if [[ $executable == inherit || $isExecutable == $executable ]]; then
ln -s "$source" "$target"
else
cp "$source" "$target"
if [[ $executable == inherit ]]; then
# Don't change file mode if it should match the source.
:
elif [[ $executable ]]; then
chmod +x "$target"
else
chmod -x "$target"
fi
fi
fi
}
'' + lib.concatStrings (
lib.mapAttrsToList (n: v: ''
insertFile ${
lib.escapeShellArgs [
(sourceStorePath v)
n
(if v.executable == null
then "inherit"
else toString v.executable)
"false"
]}
'') homeFiles
));
};
}