diff --git a/docs/installation.adoc b/docs/installation.adoc new file mode 100644 index 000000000..60f03c8e6 --- /dev/null +++ b/docs/installation.adoc @@ -0,0 +1,355 @@ +[[ch-installation]] +== Installing Home Manager + +:nix-darwin: https://github.com/LnL7/nix-darwin/ +:nixos-wiki-flakes: https://nixos.wiki/wiki/Flakes + +Home Manager can be used in three primary ways: + +1. Using the standalone `home-manager` tool. For platforms other than +NixOS and Darwin, this is the only available choice. It is also +recommended for people on NixOS or Darwin that want to manage their +home directory independently of the system as a whole. See +<> for instructions on how to perform this +installation. + +2. As a module within a NixOS system configuration. This allows the +user profiles to be built together with the system when running +`nixos-rebuild`. See <> for a description of +this setup. + +3. As a module within a {nix-darwin}[nix-darwin] system configuration. +This allows the user profiles to be built together with the system +when running `darwin-rebuild`. See <> +for a description of this setup. + +[NOTE] +In this chapter we describe how to install Home Manager in the +standard way using channels. If you prefer to use +{nixos-wiki-flakes}[Nix Flakes] then please see the instructions in +<>. + +[[sec-install-standalone]] +=== Standalone installation + +:nix-allowed-users: https://nixos.org/nix/manual/#conf-allowed-users +:nixos-allowed-users: https://nixos.org/manual/nixos/stable/options.html#opt-nix.settings.allowed-users +:bash: https://www.gnu.org/software/bash/ +:zsh: http://zsh.sourceforge.net/ +:fish: https://fishshell.com +:plugin-foreign-env: https://github.com/oh-my-fish/plugin-foreign-env +:babelfish: https://github.com/bouk/babelfish + +1. Make sure you have a working Nix installation. Specifically, make +sure that your user is able to build and install Nix packages. For +example, you should be able to successfully run a command like +`nix-instantiate '' -A hello` without having to switch to the +root user. For a multi-user install of Nix this means that your user +must be covered by the {nix-allowed-users}[`allowed-users`] Nix +option. On NixOS you can control this option using the +{nixos-allowed-users}[`nix.settings.allowed-users`] system option. + +2. Add the appropriate Home Manager channel. If you are following +Nixpkgs master or an unstable channel you can run ++ +[source,console] +---- +$ nix-channel --add https://github.com/nix-community/home-manager/archive/master.tar.gz home-manager +$ nix-channel --update +---- ++ +and if you follow a Nixpkgs version 23.05 channel you can run ++ +[source,console] +---- +$ nix-channel --add https://github.com/nix-community/home-manager/archive/release-23.05.tar.gz home-manager +$ nix-channel --update +---- + +3. Run the Home Manager installation command and create the first Home +Manager generation: ++ +[source,console] +$ nix-shell '' -A install ++ +Once finished, Home Manager should be active and available in your +user environment. + +4. If you do not plan on having Home Manager manage your shell +configuration then you must source the ++ +[source,bash] +$HOME/.nix-profile/etc/profile.d/hm-session-vars.sh ++ +file in your shell configuration. Alternatively source ++ +[source,bash] +/etc/profiles/per-user/$USER/etc/profile.d/hm-session-vars.sh ++ +when managing home configuration together with system configuration. ++ +This file can be sourced directly by POSIX.2-like shells such as +{bash}[Bash] or {zsh}[Z shell]. {fish}[Fish] users can use utilities +such as {plugin-foreign-env}[foreign-env] or {babelfish}[babelfish]. ++ +For example, if you use Bash then add ++ +[source,bash] +---- +. "$HOME/.nix-profile/etc/profile.d/hm-session-vars.sh" +---- ++ +to your `~/.profile` file. + +If instead of using channels you want to run Home Manager from a Git +checkout of the repository then you can use the +<> option to specify the absolute path +to the repository. + +Once installed you can see <> for a more detailed +description of Home Manager and how to use it. + +[[sec-install-nixos-module]] +=== NixOS module + +Home Manager provides a NixOS module that allows you to prepare user +environments directly 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. + +To make the NixOS module available for use you must `import` it into +your system configuration. This is most conveniently done by adding a +Home Manager channel to the root user. For example, if you are +following Nixpkgs master or an unstable channel, you can run + +[source,console] +---- +$ sudo nix-channel --add https://github.com/nix-community/home-manager/archive/master.tar.gz home-manager +$ sudo nix-channel --update +---- + +and if you follow a Nixpkgs version 23.05 channel, you can run + +[source,console] +---- +$ sudo nix-channel --add https://github.com/nix-community/home-manager/archive/release-23.05.tar.gz home-manager +$ sudo nix-channel --update +---- + +It is then possible to add + +[source,nix] +imports = [ ]; + +to your system `configuration.nix` file, which will introduce a new +NixOS option called `home-manager.users` whose type is an attribute +set that maps user names to Home Manager configurations. + +For example, a NixOS configuration may include the lines + +[source,nix] +---- +users.users.eve.isNormalUser = true; +home-manager.users.eve = { pkgs, ... }: { + home.packages = [ pkgs.atool pkgs.httpie ]; + programs.bash.enable = true; + + # The state version is required and should stay at the version you + # originally installed. + home.stateVersion = "23.05"; +}; +---- + +and after a `sudo nixos-rebuild switch` the user eve's environment should +include a basic Bash configuration and the packages atool and httpie. + +[NOTE] +==== +If after a `nixos-rebuild switch` you *don't* have the environment +you expect, you can take a look at home-manager's output with +[source,bash] +journalctl SYSLOG_IDENTIFIER=hm-activate-$USER +==== + +If you do not plan on having Home Manager manage your shell +configuration then you must add either + +[source,bash] +---- +. "$HOME/.nix-profile/etc/profile.d/hm-session-vars.sh" +---- + +or + +[source,bash] +---- +. "/etc/profiles/per-user/$USER/etc/profile.d/hm-session-vars.sh" +---- + +to your shell configuration, depending on whether +<> is enabled. This file can +be sourced directly by POSIX.2-like shells such as {bash}[Bash] or +{zsh}[Z shell]. {fish}[Fish] users can use utilities such as +{plugin-foreign-env}[foreign-env] or {babelfish}[babelfish]. + +[NOTE] +==== +By default packages will be installed to `$HOME/.nix-profile` but they +can be installed to `/etc/profiles` if + +[source,nix] +home-manager.useUserPackages = true; + +is added to the system configuration. This is necessary if, for +example, you wish to use `nixos-rebuildĀ build-vm`. This option may +become the default value in the future. +==== + +[NOTE] +==== +By default, Home Manager uses a private `pkgs` instance that is +configured via the `home-manager.users..nixpkgs` options. To +instead use the global `pkgs` that is configured via the system level +`nixpkgs` options, set + +[source,nix] +home-manager.useGlobalPkgs = true; + +This saves an extra Nixpkgs evaluation, adds consistency, and removes +the dependency on `NIX_PATH`, which is otherwise used for importing +Nixpkgs. +==== + +[NOTE] +==== +Home Manager will pass `osConfig` as a module argument to any modules +you create. This contains the system's NixOS configuration. + +[source,nix] +{ lib, pkgs, osConfig, ... }: +==== + +Once installed you can see <> for a more detailed +description of Home Manager and how to use it. + +[[sec-install-nix-darwin-module]] +=== nix-darwin module + +Home Manager provides a module that allows you to prepare user +environments directly from the {nix-darwin}[nix-darwin] configuration +file, which often is more convenient than using the `home-manager` +tool. + +To make the NixOS module available for use you must `import` it into +your system configuration. This is most conveniently done by adding a +Home Manager channel. For example, if you are following Nixpkgs master +or an unstable channel, you can run + +[source,console] +---- +$ nix-channel --add https://github.com/nix-community/home-manager/archive/master.tar.gz home-manager +$ nix-channel --update +---- + +and if you follow a Nixpkgs version 23.05 channel, you can run + +[source,console] +---- +$ nix-channel --add https://github.com/nix-community/home-manager/archive/release-23.05.tar.gz home-manager +$ nix-channel --update +---- + +It is then possible to add + +[source,nix] +imports = [ ]; + +to your nix-darwin `configuration.nix` file, which will introduce a +new NixOS option called `home-manager` whose type is an attribute set +that maps user names to Home Manager configurations. + +For example, a nix-darwin configuration may include the lines + +[source,nix] +---- +users.users.eve = { + name = "eve"; + home = "/Users/eve"; +} +home-manager.users.eve = { pkgs, ... }: { + home.packages = [ pkgs.atool pkgs.httpie ]; + programs.bash.enable = true; + + # The state version is required and should stay at the version you + # originally installed. + home.stateVersion = "23.05"; +}; +---- + +and after a `darwin-rebuild switch` the user eve's environment +should include a basic Bash configuration and the packages atool and +httpie. + +If you do not plan on having Home Manager manage your shell +configuration then you must add either + +[source,bash] +---- +. "$HOME/.nix-profile/etc/profile.d/hm-session-vars.sh" +---- + +or + +[source,bash] +---- +. "/etc/profiles/per-user/$USER/etc/profile.d/hm-session-vars.sh" +---- + +to your shell configuration, depending on whether +<> is enabled. This file +can be sourced directly by POSIX.2-like shells such as {bash}[Bash] or +{zsh}[Z shell]. {fish}[Fish] users can use utilities such as +{plugin-foreign-env}[foreign-env] or {babelfish}[babelfish]. + +[NOTE] +==== +By default user packages will not be ignored in favor of +`environment.systemPackages`, but they will be installed to +`/etc/profiles/per-user/$USERNAME` if + +[source,nix] +home-manager.useUserPackages = true; + +is added to the nix-darwin configuration. This option may become the +default value in the future. +==== + +[NOTE] +==== +By default, Home Manager uses a private `pkgs` instance that is +configured via the `home-manager.users..nixpkgs` options. To +instead use the global `pkgs` that is configured via the system level +`nixpkgs` options, set + +[source,nix] +home-manager.useGlobalPkgs = true; + +This saves an extra Nixpkgs evaluation, adds consistency, and removes +the dependency on `NIX_PATH`, which is otherwise used for importing +Nixpkgs. +==== + +[NOTE] +==== +Home Manager will pass `osConfig` as a module argument to any modules +you create. This contains the system's nix-darwin configuration. + +[source,nix] +{ lib, pkgs, osConfig, ... }: +==== + +Once installed you can see <> for a more detailed +description of Home Manager and how to use it. + diff --git a/modules/programs/ssh.nix b/modules/programs/ssh.nix index e062b2324..5dff919d1 100644 --- a/modules/programs/ssh.nix +++ b/modules/programs/ssh.nix @@ -493,6 +493,23 @@ in { for more information. ''; }; + + configPath = mkOption { + type = types.path; + internal = true; + description = '' + Path to the ssh configuration. + ''; + }; + + internallyManaged = mkOption { + type = types.bool; + default = true; + internal = true; + description = '' + Whether to link .ssh/config to programs.ssh.configPath + ''; + }; }; config = mkIf cfg.enable { @@ -518,16 +535,17 @@ in { home.packages = optional (cfg.package != null) cfg.package; - home.file.".ssh/config".text = let - sortedMatchBlocks = hm.dag.topoSort cfg.matchBlocks; - sortedMatchBlocksStr = builtins.toJSON sortedMatchBlocks; - matchBlocks = if sortedMatchBlocks ? result then - sortedMatchBlocks.result - else - abort "Dependency cycle in SSH match blocks: ${sortedMatchBlocksStr}"; - in '' - ${concatStringsSep "\n" - ((mapAttrsToList (n: v: "${n} ${v}") cfg.extraOptionOverrides) + home.file.".ssh/config".source = mkIf cfg.internallyManaged cfg.configPath; + + programs.ssh.configPath = + let + sortedMatchBlocks = hm.dag.topoSort cfg.matchBlocks; + sortedMatchBlocksStr = builtins.toJSON sortedMatchBlocks; + matchBlocks = + sortedMatchBlocks.result or abort "Dependency cycle in SSH match blocks: ${sortedMatchBlocksStr}"; + in pkgs.writeText "ssh_config" '' + ${concatStringsSep "\n" ( + (mapAttrsToList (n: v: "${n} ${v}") cfg.extraOptionOverrides) ++ (optional (cfg.includes != [ ]) '' Include ${concatStringsSep " " cfg.includes} '') ++ (map (block: matchBlockStr block.name block.data) matchBlocks))} diff --git a/nixos/default.nix b/nixos/default.nix index 95d5943fc..b090c4815 100644 --- a/nixos/default.nix +++ b/nixos/default.nix @@ -5,14 +5,26 @@ with lib; let cfg = config.home-manager; - - serviceEnvironment = optionalAttrs (cfg.backupFileExtension != null) { - HOME_MANAGER_BACKUP_EXT = cfg.backupFileExtension; - } // optionalAttrs cfg.verbose { VERBOSE = "1"; }; + baseService = username: { + Type = "oneshot"; + RemainAfterExit = "yes"; + TimeoutStartSec = "5m"; + SyslogIdentifier = "hm-activate-${username}"; + }; + baseUnit = username: { + description = "Home Manager environment for ${username}"; + stopIfChanged = false; + environment = optionalAttrs (cfg.backupFileExtension != null) { + HOME_MANAGER_BACKUP_EXT = cfg.backupFileExtension; + } // optionalAttrs cfg.verbose { VERBOSE = "1"; }; + serviceConfig = baseService username; + }; + hmDropIn = "/share/systemd/user/home-manager.service.d"; in { imports = [ ./common.nix ]; - + options.home-manager.useUserService = mkEnableOption + "activation on each user login instead of every user together on system boot"; config = mkMerge [ { home-manager = { @@ -26,63 +38,107 @@ in { # Inherit glibcLocales setting from NixOS. i18n.glibcLocales = lib.mkDefault config.i18n.glibcLocales; + + # .ssh/config needs to exists before login to let ssh login as that user + programs.ssh.internallyManaged = lib.mkDefault (!cfg.useUserService); }]; }; + + systemd.services = mapAttrs' (_: + { home, programs, ... }: + let inherit (home) username homeDirectory; + in nameValuePair "ssh_config-${utils.escapeSystemdPath username}" { + enable = with programs.ssh; enable && !internallyManaged; + description = "Linking ${username}' ssh config"; + wantedBy = [ "multi-user.target" ]; + before = [ "systemd-user-sessions.service" ]; + + unitConfig.RequiresMountsFor = homeDirectory; + stopIfChanged = false; + serviceConfig = (baseService username) // { + User = username; + ExecStart = [ + "${pkgs.coreutils}/bin/mkdir -p ${homeDirectory}/.ssh" + "${pkgs.coreutils}/bin/ln -s ${programs.ssh.configPath} ${homeDirectory}/.ssh/config" + ]; + }; + }) cfg.users; } - (mkIf (cfg.users != { }) { + (mkIf (cfg.users != { } && !cfg.useUserService) { systemd.services = mapAttrs' (_: usercfg: - let username = usercfg.home.username; - in nameValuePair ("home-manager-${utils.escapeSystemdPath username}") { - description = "Home Manager environment for ${username}"; + let inherit (usercfg.home) username homeDirectory activationPackage; + in nameValuePair "home-manager-${utils.escapeSystemdPath username}" + (attrsets.recursiveUpdate (baseUnit username) { wantedBy = [ "multi-user.target" ]; wants = [ "nix-daemon.socket" ]; after = [ "nix-daemon.socket" ]; before = [ "systemd-user-sessions.service" ]; - environment = serviceEnvironment; + unitConfig.RequiresMountsFor = homeDirectory; - unitConfig = { RequiresMountsFor = usercfg.home.homeDirectory; }; + serviceConfig.User = username; + serviceConfig.ExecStart = let + systemctl = + "XDG_RUNTIME_DIR=\${XDG_RUNTIME_DIR:-/run/user/$UID} systemctl"; - stopIfChanged = false; + sed = "${pkgs.gnused}/bin/sed"; - serviceConfig = { - User = usercfg.home.username; - Type = "oneshot"; - RemainAfterExit = "yes"; - TimeoutStartSec = "5m"; - SyslogIdentifier = "hm-activate-${username}"; + exportedSystemdVariables = concatStringsSep "|" [ + "DBUS_SESSION_BUS_ADDRESS" + "DISPLAY" + "WAYLAND_DISPLAY" + "XAUTHORITY" + "XDG_RUNTIME_DIR" + ]; - ExecStart = let - systemctl = - "XDG_RUNTIME_DIR=\${XDG_RUNTIME_DIR:-/run/user/$UID} systemctl"; + setupEnv = pkgs.writeScript "hm-setup-env" '' + #! ${pkgs.runtimeShell} -el - sed = "${pkgs.gnused}/bin/sed"; + # The activation script is run by a login shell to make sure + # that the user is given a sane environment. + # If the user is logged in, import variables from their current + # session environment. + eval "$( + ${systemctl} --user show-environment 2> /dev/null \ + | ${sed} -En '/^(${exportedSystemdVariables})=/s/^/export /p' + )" - exportedSystemdVariables = concatStringsSep "|" [ - "DBUS_SESSION_BUS_ADDRESS" - "DISPLAY" - "WAYLAND_DISPLAY" - "XAUTHORITY" - "XDG_RUNTIME_DIR" - ]; + exec "$1/activate" + ''; + in "${setupEnv} ${activationPackage}"; + })) cfg.users; + }) + (mkIf (cfg.users != { } && cfg.useUserService) { + systemd.user.services.home-manager = (baseUnit "%u") // { + wantedBy = [ "default.target" ]; - setupEnv = pkgs.writeScript "hm-setup-env" '' - #! ${pkgs.runtimeShell} -el + # user units cannot depend on system units + # TODO: Insert in the script logic for waiting on the nix socket via dbus + # like https://github.com/mogorman/systemd-lock-handler + # wants = [ "nix-daemon.socket" ]; + # after = [ "nix-daemon.socket" ]; - # The activation script is run by a login shell to make sure - # that the user is given a sane environment. - # If the user is logged in, import variables from their current - # session environment. - eval "$( - ${systemctl} --user show-environment 2> /dev/null \ - | ${sed} -En '/^(${exportedSystemdVariables})=/s/^/export /p' - )" + unitConfig.RequiresMountsFor = "%h"; + # no ExecStart= is defined for any user that has not defined + # config.home-manager.users.${username} + # this will be overridden by the below drop-in + }; - exec "$1/activate" - ''; - in "${setupEnv} ${usercfg.home.activationPackage}"; - }; + users.users = mapAttrs (_: + { home, ... }: { + # unit files are taken from $XDG_DATA_DIRS too + # but are loaded after units from /etc + # we write a drop in so that it will take precedence + # over the above unit declaration + packages = [ + (pkgs.writeTextDir "${hmDropIn}/10-user-activation.conf" '' + [Service] + ExecStart=${home.activationPackage}/activate + '') + ]; }) cfg.users; + environment.pathsToLink = [ hmDropIn ]; }) ]; } +