allow Home Manager to be used as a NixOS module
This is a NixOS module that is intended to be imported into a NixOS system configuration. It allows the system users to be set up directly from the system configuration. The actual profile switch is performed by a oneshot systemd unit per configured user that acts much like the regular `home-manager switch` command. With this implementation, the NixOS module does not work properly with the `nixos-rebuild build-vm` command. This can be solved by using the `users.users.<name?>.packages` option to install packages but this does not work flawlessly with certain Nixpkgs packages. In particular, for programs using the Qt libraries.
This commit is contained in:
parent
563a20fc82
commit
1bc59f7290
|
@ -9,4 +9,6 @@ rec {
|
||||||
install = import ./home-manager/install.nix {
|
install = import ./home-manager/install.nix {
|
||||||
inherit home-manager pkgs;
|
inherit home-manager pkgs;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
nixos = import ./nixos;
|
||||||
}
|
}
|
||||||
|
|
|
@ -291,6 +291,7 @@ in
|
||||||
# script's "check" and the "write" phases.
|
# script's "check" and the "write" phases.
|
||||||
home.activation.writeBoundary = dag.entryAnywhere "";
|
home.activation.writeBoundary = dag.entryAnywhere "";
|
||||||
|
|
||||||
|
# Install packages to the user environment.
|
||||||
home.activation.installPackages = dag.entryAfter ["writeBoundary"] ''
|
home.activation.installPackages = dag.entryAfter ["writeBoundary"] ''
|
||||||
$DRY_RUN_CMD nix-env -i ${cfg.path}
|
$DRY_RUN_CMD nix-env -i ${cfg.path}
|
||||||
'';
|
'';
|
||||||
|
|
|
@ -568,6 +568,52 @@ in
|
||||||
GTK configurations.
|
GTK configurations.
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
time = "2018-02-06T20:23:34+00:00";
|
||||||
|
message = ''
|
||||||
|
It is now possible to use Home Manager as a NixOS module.
|
||||||
|
This allows you to prepare user environments from the system
|
||||||
|
configuration file, which often is more convenient than
|
||||||
|
using the 'home-manager' tool. It also opens up additional
|
||||||
|
possibilities, for example, to automatically configure user
|
||||||
|
environments in NixOS declarative containers or on systems
|
||||||
|
deployed through NixOps.
|
||||||
|
|
||||||
|
This feature should be considered experimental for now and
|
||||||
|
some critial limitations apply. For example, it is currently
|
||||||
|
not possible to use 'nixos-rebuild build-vm' when using the
|
||||||
|
Home Manager NixOS module. That said, it should be
|
||||||
|
reasonably robust and stable for simpler use cases.
|
||||||
|
|
||||||
|
To make Home Manager available in your NixOS system
|
||||||
|
configuration you can add
|
||||||
|
|
||||||
|
imports = [
|
||||||
|
"''${builtins.fetchTarball https://github.com/rycee/home-manager/archive/master.tar.gz}/nixos"
|
||||||
|
];
|
||||||
|
|
||||||
|
to your 'configuration.nix' file. This will introduce a new
|
||||||
|
NixOS option called 'home-manager.users' whose type is an
|
||||||
|
attribute set mapping user names to Home Manager
|
||||||
|
configurations.
|
||||||
|
|
||||||
|
For example, a NixOS configuration may include the lines
|
||||||
|
|
||||||
|
users.users.eve.isNormalUser = true;
|
||||||
|
home-manager.users.eve = {
|
||||||
|
home.packages = [ pkgs.atool pkgs.httpie ];
|
||||||
|
programs.bash.enable = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
and after a 'nixos-rebuild switch' the user eve's
|
||||||
|
environment should include a basic Bash configuration and
|
||||||
|
the packages atool and httpie.
|
||||||
|
|
||||||
|
More detailed documentation on the intricacies of this new
|
||||||
|
feature is slowly forthcoming.
|
||||||
|
'';
|
||||||
|
}
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,9 @@
|
||||||
|
|
||||||
# Whether to enable module type checking.
|
# Whether to enable module type checking.
|
||||||
, check ? true
|
, check ? true
|
||||||
|
|
||||||
|
# Whether these modules are inside a NixOS submodule.
|
||||||
|
, nixosSubmodule ? false
|
||||||
}:
|
}:
|
||||||
|
|
||||||
with lib;
|
with lib;
|
||||||
|
@ -75,10 +78,17 @@ let
|
||||||
];
|
];
|
||||||
|
|
||||||
pkgsModule = {
|
pkgsModule = {
|
||||||
|
options.nixosSubmodule = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
internal = true;
|
||||||
|
readOnly = true;
|
||||||
|
};
|
||||||
|
|
||||||
config._module.args.baseModules = modules;
|
config._module.args.baseModules = modules;
|
||||||
config._module.args.pkgs = lib.mkDefault pkgs;
|
config._module.args.pkgs = lib.mkDefault pkgs;
|
||||||
config._module.check = check;
|
config._module.check = check;
|
||||||
config.lib = import ./lib { inherit lib; };
|
config.lib = import ./lib { inherit lib; };
|
||||||
|
config.nixosSubmodule = nixosSubmodule;
|
||||||
config.nixpkgs.system = mkDefault pkgs.system;
|
config.nixpkgs.system = mkDefault pkgs.system;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ in
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
config = mkIf cfg.enable {
|
config = mkIf (cfg.enable && !config.nixosSubmodule) {
|
||||||
home.packages = [
|
home.packages = [
|
||||||
(import ../../home-manager {
|
(import ../../home-manager {
|
||||||
inherit pkgs;
|
inherit pkgs;
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
systemctlPath:
|
#!/usr/bin/env bash
|
||||||
''
|
|
||||||
function isStartable() {
|
function isStartable() {
|
||||||
local service="$1"
|
local service="$1"
|
||||||
[[ $(${systemctlPath} --user show -p RefuseManualStart "$service") == *=no ]]
|
[[ $(systemctl --user show -p RefuseManualStart "$service") == *=no ]]
|
||||||
}
|
}
|
||||||
|
|
||||||
function isStoppable() {
|
function isStoppable() {
|
||||||
if [[ -v oldGenPath ]] ; then
|
if [[ -v oldGenPath ]] ; then
|
||||||
local service="$1"
|
local service="$1"
|
||||||
[[ $(${systemctlPath} --user show -p RefuseManualStop "$service") == *=no ]]
|
[[ $(systemctl --user show -p RefuseManualStop "$service") == *=no ]]
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,19 +53,19 @@ function systemdPostReload() {
|
||||||
--old-line-format='-%L' \
|
--old-line-format='-%L' \
|
||||||
--unchanged-line-format=' %L' \
|
--unchanged-line-format=' %L' \
|
||||||
"$oldServiceFiles" "$newServiceFiles" \
|
"$oldServiceFiles" "$newServiceFiles" \
|
||||||
> $servicesDiffFile || true
|
> "$servicesDiffFile" || true
|
||||||
|
|
||||||
local -a maybeRestart=( $(grep '^ ' $servicesDiffFile | cut -c2-) )
|
local -a maybeRestart=( $(grep '^ ' "$servicesDiffFile" | cut -c2-) )
|
||||||
local -a maybeStop=( $(grep '^-' $servicesDiffFile | cut -c2-) )
|
local -a maybeStop=( $(grep '^-' "$servicesDiffFile" | cut -c2-) )
|
||||||
local -a maybeStart=( $(grep '^+' $servicesDiffFile | cut -c2-) )
|
local -a maybeStart=( $(grep '^+' "$servicesDiffFile" | cut -c2-) )
|
||||||
local -a toRestart=( )
|
local -a toRestart=( )
|
||||||
local -a toStop=( )
|
local -a toStop=( )
|
||||||
local -a toStart=( )
|
local -a toStart=( )
|
||||||
|
|
||||||
for f in ''${maybeRestart[@]} ; do
|
for f in "${maybeRestart[@]}" ; do
|
||||||
if isStoppable "$f" \
|
if isStoppable "$f" \
|
||||||
&& isStartable "$f" \
|
&& isStartable "$f" \
|
||||||
&& ${systemctlPath} --quiet --user is-active "$f" \
|
&& systemctl --quiet --user is-active "$f" \
|
||||||
&& ! cmp --quiet \
|
&& ! cmp --quiet \
|
||||||
"$oldUserServicePath/$f" \
|
"$oldUserServicePath/$f" \
|
||||||
"$newUserServicePath/$f" ; then
|
"$newUserServicePath/$f" ; then
|
||||||
|
@ -73,32 +73,32 @@ function systemdPostReload() {
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
for f in ''${maybeStop[@]} ; do
|
for f in "${maybeStop[@]}" ; do
|
||||||
if isStoppable "$f" ; then
|
if isStoppable "$f" ; then
|
||||||
toStop+=("$f")
|
toStop+=("$f")
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
for f in ''${maybeStart[@]} ; do
|
for f in "${maybeStart[@]}" ; do
|
||||||
if isStartable "$f" ; then
|
if isStartable "$f" ; then
|
||||||
toStart+=("$f")
|
toStart+=("$f")
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
rm -r $workDir
|
rm -r "$workDir"
|
||||||
|
|
||||||
local sugg=""
|
local sugg=""
|
||||||
|
|
||||||
if [[ -n "''${toRestart[@]}" ]] ; then
|
if [[ -n "${toRestart[@]}" ]] ; then
|
||||||
sugg="''${sugg}systemctl --user restart ''${toRestart[@]}\n"
|
sugg="${sugg}systemctl --user restart ${toRestart[@]}\n"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -n "''${toStop[@]}" ]] ; then
|
if [[ -n "${toStop[@]}" ]] ; then
|
||||||
sugg="''${sugg}systemctl --user stop ''${toStop[@]}\n"
|
sugg="${sugg}systemctl --user stop ${toStop[@]}\n"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -n "''${toStart[@]}" ]] ; then
|
if [[ -n "${toStart[@]}" ]] ; then
|
||||||
sugg="''${sugg}systemctl --user start ''${toStart[@]}\n"
|
sugg="${sugg}systemctl --user start ${toStart[@]}\n"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -n "$sugg" ]] ; then
|
if [[ -n "$sugg" ]] ; then
|
||||||
|
@ -107,6 +107,8 @@ function systemdPostReload() {
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
$DRY_RUN_CMD ${systemctlPath} --user daemon-reload
|
oldGenPath="$1"
|
||||||
|
newGenPath="$2"
|
||||||
|
|
||||||
|
$DRY_RUN_CMD systemctl --user daemon-reload
|
||||||
systemdPostReload
|
systemdPostReload
|
||||||
''
|
|
|
@ -145,14 +145,30 @@ in
|
||||||
(buildServices "timer" cfg.timers)
|
(buildServices "timer" cfg.timers)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
# Run systemd service reload if user is logged in. If we're
|
||||||
|
# running this from the NixOS module then XDG_RUNTIME_DIR is not
|
||||||
|
# set and systemd commands will fail. We'll therefore have to
|
||||||
|
# set it ourselves in that case.
|
||||||
home.activation.reloadSystemD = dag.entryAfter ["linkGeneration"] (
|
home.activation.reloadSystemD = dag.entryAfter ["linkGeneration"] (
|
||||||
if cfg.startServices then
|
let
|
||||||
|
autoReloadCmd = ''
|
||||||
|
${pkgs.ruby}/bin/ruby ${./systemd-activate.rb} \
|
||||||
|
"''${oldGenPath=}" "$newGenPath" "${servicesStartTimeoutMs}"
|
||||||
|
'';
|
||||||
|
|
||||||
|
legacyReloadCmd = ''
|
||||||
|
bash ${./systemd-activate.sh} "''${oldGenPath=}" "$newGenPath"
|
||||||
|
'';
|
||||||
|
in
|
||||||
''
|
''
|
||||||
PATH=${dirOf cfg.systemctlPath} \
|
if who | grep -q '^${config.home.username} '; then
|
||||||
${pkgs.ruby}/bin/ruby ${./systemd-activate.rb} \
|
XDG_RUNTIME_DIR=''${XDG_RUNTIME_DIR:-/run/user/$(id -u)} \
|
||||||
"''${oldGenPath=}" "$newGenPath" "${servicesStartTimeoutMs}"
|
PATH=${dirOf cfg.systemctlPath}:$PATH \
|
||||||
|
${if cfg.startServices then autoReloadCmd else legacyReloadCmd}
|
||||||
|
else
|
||||||
|
echo "User ${config.home.username} not logged in. Skipping."
|
||||||
|
fi
|
||||||
''
|
''
|
||||||
else import ./systemd-activate.nix cfg.systemctlPath
|
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
|
|
57
nixos/default.nix
Normal file
57
nixos/default.nix
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
{ config, lib, pkgs, utils, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
|
||||||
|
let
|
||||||
|
|
||||||
|
cfg = config.home-manager;
|
||||||
|
|
||||||
|
hmModule = types.submodule ({name, ...}: {
|
||||||
|
imports = import ../modules/modules.nix {
|
||||||
|
inherit lib pkgs;
|
||||||
|
nixosSubmodule = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
home.username = config.users.users.${name}.name;
|
||||||
|
home.homeDirectory = config.users.users.${name}.home;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
in
|
||||||
|
|
||||||
|
{
|
||||||
|
options = {
|
||||||
|
home-manager.users = mkOption {
|
||||||
|
type = types.attrsOf hmModule;
|
||||||
|
default = {};
|
||||||
|
description = ''
|
||||||
|
Per-user Home Manager configuration.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf (cfg.users != {}) {
|
||||||
|
systemd.services = mapAttrs' (username: usercfg:
|
||||||
|
nameValuePair ("home-manager-${utils.escapeSystemdPath username}") {
|
||||||
|
description = "Home Manager environment for ${username}";
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
|
||||||
|
serviceConfig = {
|
||||||
|
User = username;
|
||||||
|
Type = "oneshot";
|
||||||
|
RemainAfterExit = "yes";
|
||||||
|
SyslogIdentifier = "hm-activate-${username}";
|
||||||
|
|
||||||
|
# The activation script is run by a login shell to make sure
|
||||||
|
# that the user is given a sane Nix environment.
|
||||||
|
ExecStart = pkgs.writeScript "activate-${username}" ''
|
||||||
|
#! ${pkgs.stdenv.shell} -el
|
||||||
|
echo Activating home-manager configuration for ${username}
|
||||||
|
exec ${usercfg.home.activationPackage}/activate
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
||||||
|
) cfg.users;
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in a new issue