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 {
|
||||
inherit home-manager pkgs;
|
||||
};
|
||||
|
||||
nixos = import ./nixos;
|
||||
}
|
||||
|
|
|
@ -291,6 +291,7 @@ in
|
|||
# script's "check" and the "write" phases.
|
||||
home.activation.writeBoundary = dag.entryAnywhere "";
|
||||
|
||||
# Install packages to the user environment.
|
||||
home.activation.installPackages = dag.entryAfter ["writeBoundary"] ''
|
||||
$DRY_RUN_CMD nix-env -i ${cfg.path}
|
||||
'';
|
||||
|
|
|
@ -568,6 +568,52 @@ in
|
|||
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.
|
||||
, check ? true
|
||||
|
||||
# Whether these modules are inside a NixOS submodule.
|
||||
, nixosSubmodule ? false
|
||||
}:
|
||||
|
||||
with lib;
|
||||
|
@ -75,10 +78,17 @@ let
|
|||
];
|
||||
|
||||
pkgsModule = {
|
||||
options.nixosSubmodule = mkOption {
|
||||
type = types.bool;
|
||||
internal = true;
|
||||
readOnly = true;
|
||||
};
|
||||
|
||||
config._module.args.baseModules = modules;
|
||||
config._module.args.pkgs = lib.mkDefault pkgs;
|
||||
config._module.check = check;
|
||||
config.lib = import ./lib { inherit lib; };
|
||||
config.nixosSubmodule = nixosSubmodule;
|
||||
config.nixpkgs.system = mkDefault pkgs.system;
|
||||
};
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ in
|
|||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
config = mkIf (cfg.enable && !config.nixosSubmodule) {
|
||||
home.packages = [
|
||||
(import ../../home-manager {
|
||||
inherit pkgs;
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
systemctlPath:
|
||||
''
|
||||
#!/usr/bin/env bash
|
||||
|
||||
function isStartable() {
|
||||
local service="$1"
|
||||
[[ $(${systemctlPath} --user show -p RefuseManualStart "$service") == *=no ]]
|
||||
[[ $(systemctl --user show -p RefuseManualStart "$service") == *=no ]]
|
||||
}
|
||||
|
||||
function isStoppable() {
|
||||
if [[ -v oldGenPath ]] ; then
|
||||
local service="$1"
|
||||
[[ $(${systemctlPath} --user show -p RefuseManualStop "$service") == *=no ]]
|
||||
[[ $(systemctl --user show -p RefuseManualStop "$service") == *=no ]]
|
||||
fi
|
||||
}
|
||||
|
||||
|
@ -53,19 +53,19 @@ function systemdPostReload() {
|
|||
--old-line-format='-%L' \
|
||||
--unchanged-line-format=' %L' \
|
||||
"$oldServiceFiles" "$newServiceFiles" \
|
||||
> $servicesDiffFile || true
|
||||
> "$servicesDiffFile" || true
|
||||
|
||||
local -a maybeRestart=( $(grep '^ ' $servicesDiffFile | cut -c2-) )
|
||||
local -a maybeStop=( $(grep '^-' $servicesDiffFile | cut -c2-) )
|
||||
local -a maybeStart=( $(grep '^+' $servicesDiffFile | cut -c2-) )
|
||||
local -a maybeRestart=( $(grep '^ ' "$servicesDiffFile" | cut -c2-) )
|
||||
local -a maybeStop=( $(grep '^-' "$servicesDiffFile" | cut -c2-) )
|
||||
local -a maybeStart=( $(grep '^+' "$servicesDiffFile" | cut -c2-) )
|
||||
local -a toRestart=( )
|
||||
local -a toStop=( )
|
||||
local -a toStart=( )
|
||||
|
||||
for f in ''${maybeRestart[@]} ; do
|
||||
for f in "${maybeRestart[@]}" ; do
|
||||
if isStoppable "$f" \
|
||||
&& isStartable "$f" \
|
||||
&& ${systemctlPath} --quiet --user is-active "$f" \
|
||||
&& systemctl --quiet --user is-active "$f" \
|
||||
&& ! cmp --quiet \
|
||||
"$oldUserServicePath/$f" \
|
||||
"$newUserServicePath/$f" ; then
|
||||
|
@ -73,32 +73,32 @@ function systemdPostReload() {
|
|||
fi
|
||||
done
|
||||
|
||||
for f in ''${maybeStop[@]} ; do
|
||||
for f in "${maybeStop[@]}" ; do
|
||||
if isStoppable "$f" ; then
|
||||
toStop+=("$f")
|
||||
fi
|
||||
done
|
||||
|
||||
for f in ''${maybeStart[@]} ; do
|
||||
for f in "${maybeStart[@]}" ; do
|
||||
if isStartable "$f" ; then
|
||||
toStart+=("$f")
|
||||
fi
|
||||
done
|
||||
|
||||
rm -r $workDir
|
||||
rm -r "$workDir"
|
||||
|
||||
local sugg=""
|
||||
|
||||
if [[ -n "''${toRestart[@]}" ]] ; then
|
||||
sugg="''${sugg}systemctl --user restart ''${toRestart[@]}\n"
|
||||
if [[ -n "${toRestart[@]}" ]] ; then
|
||||
sugg="${sugg}systemctl --user restart ${toRestart[@]}\n"
|
||||
fi
|
||||
|
||||
if [[ -n "''${toStop[@]}" ]] ; then
|
||||
sugg="''${sugg}systemctl --user stop ''${toStop[@]}\n"
|
||||
if [[ -n "${toStop[@]}" ]] ; then
|
||||
sugg="${sugg}systemctl --user stop ${toStop[@]}\n"
|
||||
fi
|
||||
|
||||
if [[ -n "''${toStart[@]}" ]] ; then
|
||||
sugg="''${sugg}systemctl --user start ''${toStart[@]}\n"
|
||||
if [[ -n "${toStart[@]}" ]] ; then
|
||||
sugg="${sugg}systemctl --user start ${toStart[@]}\n"
|
||||
fi
|
||||
|
||||
if [[ -n "$sugg" ]] ; then
|
||||
|
@ -107,6 +107,8 @@ function systemdPostReload() {
|
|||
fi
|
||||
}
|
||||
|
||||
$DRY_RUN_CMD ${systemctlPath} --user daemon-reload
|
||||
oldGenPath="$1"
|
||||
newGenPath="$2"
|
||||
|
||||
$DRY_RUN_CMD systemctl --user daemon-reload
|
||||
systemdPostReload
|
||||
''
|
|
@ -145,14 +145,30 @@ in
|
|||
(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"] (
|
||||
if cfg.startServices then
|
||||
''
|
||||
PATH=${dirOf cfg.systemctlPath} \
|
||||
let
|
||||
autoReloadCmd = ''
|
||||
${pkgs.ruby}/bin/ruby ${./systemd-activate.rb} \
|
||||
"''${oldGenPath=}" "$newGenPath" "${servicesStartTimeoutMs}"
|
||||
'';
|
||||
|
||||
legacyReloadCmd = ''
|
||||
bash ${./systemd-activate.sh} "''${oldGenPath=}" "$newGenPath"
|
||||
'';
|
||||
in
|
||||
''
|
||||
if who | grep -q '^${config.home.username} '; then
|
||||
XDG_RUNTIME_DIR=''${XDG_RUNTIME_DIR:-/run/user/$(id -u)} \
|
||||
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