From 17ef1c563d6dfd2e55dfbd1fbbfe46abc088ef0a Mon Sep 17 00:00:00 2001 From: Jake Hamilton Date: Mon, 10 Apr 2023 15:59:57 -0700 Subject: [PATCH 01/11] wip: faulty first attempt at home-manager integration --- lib/flake/default.nix | 29 ++++- lib/home/default.nix | 214 +++++++++++++++++++++++++++++++ lib/home/nix-registry-module.nix | 11 ++ lib/module/default.nix | 6 +- lib/system/default.nix | 74 ++++++++--- modules/darwin/home/default.nix | 33 +++++ modules/home/os/default.nix | 28 ++++ modules/nixos/home/default.nix | 33 +++++ 8 files changed, 400 insertions(+), 28 deletions(-) create mode 100644 lib/home/default.nix create mode 100644 lib/home/nix-registry-module.nix create mode 100644 modules/darwin/home/default.nix create mode 100644 modules/home/os/default.nix create mode 100644 modules/nixos/home/default.nix diff --git a/lib/flake/default.nix b/lib/flake/default.nix index e4af712..979b3e2 100644 --- a/lib/flake/default.nix +++ b/lib/flake/default.nix @@ -72,15 +72,30 @@ rec { package-namespace = full-flake-options.package-namespace or "internal"; custom-flake-options = flake.without-snowfall-options full-flake-options; alias = full-flake-options.alias or { }; - systems = snowfall-lib.system.create-systems (full-flake-options.systems or { }); - hosts = snowfall-lib.attrs.merge-shallow [ (full-flake-options.systems.hosts or { }) systems ]; + homes = snowfall-lib.home.create-homes (full-flake-options.homes or { }); + systems = snowfall-lib.system.create-systems { + systems = (full-flake-options.systems or { }); + homes = (full-flake-options.homes or { }); + }; + hosts = snowfall-lib.attrs.merge-shallow [ (full-flake-options.systems.hosts or { }) systems homes ]; templates = snowfall-lib.template.create-templates { overrides = (full-flake-options.templates or { }); alias = alias.templates or { }; }; - modules = snowfall-lib.module.create-modules { - overrides = (full-flake-options.modules or { }); - alias = alias.modules or { }; + nixos-modules = snowfall-lib.module.create-modules { + src = snowfall-lib.fs.get-snowfall-file "modules/nixos"; + overrides = (full-flake-options.modules.nixos or { }); + alias = alias.modules.nixos or { }; + }; + darwin-modules = snowfall-lib.module.create-modules { + src = snowfall-lib.fs.get-snowfall-file "modules/darwin"; + overrides = (full-flake-options.modules.darwin or { }); + alias = alias.modules.darwin or { }; + }; + home-modules = snowfall-lib.module.create-modules { + src = snowfall-lib.fs.get-snowfall-file "modules/home"; + overrides = (full-flake-options.modules.home or { }); + alias = alias.modules.home or { }; }; overlays = snowfall-lib.overlay.create-overlays { inherit package-namespace; @@ -120,7 +135,9 @@ rec { lib = snowfall-lib.internal.user-lib; inputs = snowfall-lib.flake.without-src user-inputs; - nixosModules = modules; + nixosModules = nixos-modules; + darwinModules = darwin-modules; + homeModules = home-modules; channelsConfig = full-flake-options.channels-config or { }; diff --git a/lib/home/default.nix b/lib/home/default.nix new file mode 100644 index 0000000..a956e39 --- /dev/null +++ b/lib/home/default.nix @@ -0,0 +1,214 @@ +{ core-inputs, user-inputs, snowfall-lib }: + +let + inherit (core-inputs.nixpkgs.lib) assertMsg foldl head tail concatMap optionalAttrs mkIf filterAttrs mapAttrs' mkMerge mapAttrsToList; + + user-homes-root = snowfall-lib.fs.get-snowfall-file "homes"; + user-modules-root = snowfall-lib.fs.get-snowfall-file "modules"; +in +{ + home = rec { + # Modules in home-manager expect `hm` to be available directly on `lib` itself. + home-lib = snowfall-lib.internal.system-lib.extend (final: prev: + # @NOTE(jakehamilton): This order is important, this library's extend and other utilities must write + # _over_ the original `system-lib`. + snowfall-lib.internal.system-lib + // prev + // { + hm = snowfall-lib.internal.system-lib.home-manager.hm; + }); + + split-user-and-host = target: + let + raw-name-parts = builtins.split "@" target; + name-parts = builtins.filter builtins.isString raw-name-parts; + + user = builtins.elemAt name-parts 0; + host = + if builtins.length name-parts > 1 then + builtins.elemAt name-parts 1 + else + ""; + in + { + inherit user host; + }; + + + create-home = + { path + , name ? builtins.unsafeDiscardStringContext (snowfall-lib.system.get-inferred-system-name path) + , modules ? [ ] + , specialArgs ? { } + , channelName ? "nixpkgs" + , system ? "x86_64-linux" + }: + let + user-metadata = split-user-and-host name; + + # @NOTE(jakehamilton): home-manager has trouble with `pkgs` recursion if it isn't passed in here. + pkgs = user-inputs.self.pkgs.${system}.${channelName} // { lib = home-lib; }; + lib = home-lib; + in + assert assertMsg (user-inputs ? home-manager) "In order to create home-manager configurations, you must include `home-manager` as a flake input."; + assert assertMsg (user-metadata.host != "") "Snowfall Lib homes must be named with the format: user@system"; + { + inherit channelName system; + + output = "homeConfigurations"; + + modules = [ path ] ++ modules; + + specialArgs = { + inherit name; + inherit (user-metadata) user host; + + format = "home"; + + inputs = snowfall-lib.flake.without-src user-inputs; + + # @NOTE(jakehamilton): home-manager has trouble with `pkgs` recursion if it isn't passed in here. + inherit pkgs lib; + }; + + builder = args: + user-inputs.home-manager.lib.homeManagerConfiguration + ((builtins.removeAttrs args [ "system" "specialArgs" ]) // { + inherit pkgs lib; + + modules = args.modules ++ [ + (module-args: import ./nix-registry-module.nix (module-args // { + inherit user-inputs core-inputs; + })) + ]; + + extraSpecialArgs = specialArgs // args.specialArgs; + }); + }; + + get-target-homes-metadata = target: + let + homes = snowfall-lib.fs.get-directories target; + existing-homes = builtins.filter (home: builtins.pathExists "${home}/default.nix") homes; + create-home-metadata = path: { + path = "${path}/default.nix"; + # We are building flake outputs based on file contents. Nix doesn't like this + # so we have to explicitly discard the string's path context to allow us to + # use the name as a variable. + name = builtins.unsafeDiscardStringContext (builtins.baseNameOf path); + # We are building flake outputs based on file contents. Nix doesn't like this + # so we have to explicitly discard the string's path context to allow us to + # use the name as a variable. + system = builtins.unsafeDiscardStringContext (builtins.baseNameOf target); + }; + home-configurations = builtins.map create-home-metadata existing-homes; + in + home-configurations; + + # Create all available homes. + # Type: Attrs -> Attrs + # Usage: create-homes { users."my-user@my-system".specialArgs.x = true; modules = [ my-shared-module ]; } + # result: { "my-user@my-system" = ; } + create-homes = homes: + let + targets = snowfall-lib.fs.get-directories user-homes-root; + target-homes-metadata = concatMap get-target-homes-metadata targets; + + user-home-modules = snowfall-lib.module.create-modules { + src = "${user-modules-root}/home"; + }; + + user-home-modules-list = builtins.attrValues user-home-modules; + + create-home' = home-metadata: + let + inherit (home-metadata) name; + overrides = homes.users.${name} or { }; + in + { + "${name}" = create-home (overrides // home-metadata // { + modules = user-home-modules-list ++ (homes.users.${name}.modules or [ ]) ++ (homes.modules or [ ]); + }); + }; + + created-homes = foldl (homes: home-metadata: homes // (create-home' home-metadata)) { } target-homes-metadata; + in + created-homes; + + create-home-system-modules = users: + let + created-users = create-homes users; + extra-special-args-module = + args@{ config + , pkgs + , system ? pkgs.system + , target ? system + , format ? "home" + , host ? "" + , virtual ? (snowfall-lib.system.is-virtual target) + , systems ? { } + , ... + }: + { + _file = "virtual:snowfallorg/home/extra-special-args"; + + config = { + home-manager.extraSpecialArgs = { + inherit system target format virtual systems host; + + lib = home-lib; + # pkgs = user-inputs.self.pkgs.${system}.nixpkgs; + + inputs = snowfall-lib.flake.without-src user-inputs; + }; + }; + }; + system-modules = builtins.map + (name: + let + created-user = created-users.${name}; + user-module = head created-user.modules; + other-modules = tail created-user.modules; + user-name = created-user.specialArgs.user; + in + args@{ config + , pkgs + , host ? "" + , ... + }: + let + host-matches = created-user.specialArgs.host == host; + + wrapped-user-module = home-args: + let + modified-args = args // { + inherit (created-user.specialArgs) user; + }; + user-module-result = (import user-module (home-args // modified-args)) // { + _file = user-module; + }; + in + user-module-result; + in + { + _file = "virtual:snowfallorg/home/user/${name}"; + + imports = + if snowfall-lib.system.is-darwin created-user.system then + [ ../../modules/darwin/home/default.nix ] + else + [ ../../modules/nixos/home/default.nix ]; + + config = mkIf host-matches { + home-manager = { + users.${user-name} = wrapped-user-module args; + sharedModules = other-modules; + }; + }; + } + ) + (builtins.attrNames created-users); + in + [ extra-special-args-module ] ++ system-modules; + }; +} diff --git a/lib/home/nix-registry-module.nix b/lib/home/nix-registry-module.nix new file mode 100644 index 0000000..c15f9e2 --- /dev/null +++ b/lib/home/nix-registry-module.nix @@ -0,0 +1,11 @@ +# This code is adapted from flake-utils-plus: +# https://github.com/gytis-ivaskevicius/flake-utils-plus/blob/2bf0f91643c2e5ae38c1b26893ac2927ac9bd82a/lib/options.nix +{ lib, config, user-inputs, core-inputs, ... }: + +{ + disabledModules = [ + # The module from flake-utils-plus only works on NixOS and nix-darwin. For home-manager + # to build, this module needs to be disabled. + "${core-inputs.flake-utils-plus}/lib/options.nix" + ]; +} diff --git a/lib/module/default.nix b/lib/module/default.nix index cb10acf..7b647cb 100644 --- a/lib/module/default.nix +++ b/lib/module/default.nix @@ -16,7 +16,7 @@ in # Usage: create-modules { src = ./my-modules; overrides = { inherit another-module; }; alias = { default = "another-module" }; } # result: { another-module = ...; my-module = ...; default = ...; } create-modules = - { src ? user-modules-root + { src ? "${user-modules-root}/nixos" , overrides ? { } , alias ? { } }: @@ -36,7 +36,9 @@ in modules-metadata = builtins.map create-module-metadata user-modules; merge-modules = modules: metadata: modules // { - ${metadata.name} = args: + # @NOTE(jakehamilton): home-manager *requires* modules to specify named arguments or it will not + # pass values in. For this reason we must specify things like `pkgs` as a named attribute. + ${metadata.name} = args@{ pkgs, ... }: let system = args.system or args.pkgs.system; target = args.target or system; diff --git a/lib/system/default.nix b/lib/system/default.nix index b2a5d1e..9df24f9 100644 --- a/lib/system/default.nix +++ b/lib/system/default.nix @@ -5,21 +5,25 @@ let inherit (builtins) dirOf baseNameOf; - inherit (core-inputs.nixpkgs.lib) assertMsg fix hasInfix concatMap foldl; + inherit (core-inputs.nixpkgs.lib) assertMsg fix hasInfix concatMap foldl optionals singleton; virtual-systems = import ./virtual-systems.nix; user-systems-root = snowfall-lib.fs.get-snowfall-file "systems"; user-modules-root = snowfall-lib.fs.get-snowfall-file "modules"; - - get-inferred-system-name = path: - if snowfall-lib.path.has-file-extension "nix" path then - snowfall-lib.path.get-parent-directory path - else - baseNameOf path; in { system = rec { + # Get the name of a system based on its file path. + # Type: Path -> String + # Usage: get-inferred-system-name "/systems/my-system/default.nix" + # result: "my-system" + get-inferred-system-name = path: + if snowfall-lib.path.has-file-extension "nix" path then + snowfall-lib.path.get-parent-directory path + else + baseNameOf path; + # Check whether a named system is macOS. # Type: String -> Bool # Usage: is-darwin "x86_64-linux" @@ -85,7 +89,7 @@ in let virtual-system-type = get-virtual-system-type target; virtual-system-builder = args: - assert (assertMsg (user-inputs ? nixos-generators) "In order to create virtual systems, you must include `nixos-generators` as a flake input."); + assert assertMsg (user-inputs ? nixos-generators) "In order to create virtual systems, you must include `nixos-generators` as a flake input."; user-inputs.nixos-generators.nixosGenerate (args // { format = virtual-system-type; @@ -94,12 +98,13 @@ in }; }); darwin-system-builder = args: - assert (assertMsg (user-inputs ? darwin) "In order to create virtual systems, you must include `darwin` as a flake input."); - user-inputs.darwin.lib.darwinSystem ((builtins.removeAttrs args [ "system" ]) // { - specialArgs = args.specialArgs // { - format = "darwin"; - }; - }); + assert assertMsg (user-inputs ? darwin) "In order to create virtual systems, you must include `darwin` as a flake input."; + user-inputs.darwin.lib.darwinSystem + ((builtins.removeAttrs args [ "system" ]) // { + specialArgs = args.specialArgs // { + format = "darwin"; + }; + }); linux-system-builder = args: core-inputs.nixpkgs.lib.nixosSystem (args // { @@ -158,17 +163,26 @@ in , builder ? get-system-builder target , output ? get-system-output target , systems ? { } + , homes ? { } }: let lib = snowfall-lib.internal.system-lib; + home-system-modules = snowfall-lib.home.create-home-system-modules homes; + home-manager-module = + if is-darwin system then + user-inputs.home-manager.darwinModules.home-manager + else + user-inputs.home-manager.nixosModules.home-manager; + home-manager-modules = [ home-manager-module ] ++ home-system-modules; in { inherit channelName system builder output; - modules = [ path ] ++ modules; + modules = [ path ] ++ modules ++ (optionals (user-inputs ? home-manager) home-manager-modules); specialArgs = specialArgs // { - inherit target system name systems lib; + inherit target system systems lib; + host = name; virtual = (get-virtual-system-type target) != ""; inputs = snowfall-lib.flake.without-src user-inputs; @@ -177,21 +191,41 @@ in # Create all available systems. # Type: Attrs -> Attrs - # Usage: create-systems { hosts.my-host.specialArgs.x = true; modules = [ my-shared-module ]; } + # Usage: create-systems { hosts.my-host.specialArgs.x = true; modules.nixos = [ my-shared-module ]; } # result: { my-host = ; } - create-systems = systems: + create-systems = { systems ? { }, homes ? { } }: let targets = snowfall-lib.fs.get-directories user-systems-root; target-systems-metadata = concatMap get-target-systems-metadata targets; - user-modules = snowfall-lib.fs.get-default-nix-files-recursive user-modules-root; + user-nixos-modules = snowfall-lib.module.create-modules { + src = "${user-modules-root}/nixos"; + }; + user-darwin-modules = snowfall-lib.module.create-modules { + src = "${user-modules-root}/darwin"; + }; + nixos-modules = systems.modules.nixos or [ ]; + darwin-modules = systems.modules.darwin or [ ]; + create-system' = created-systems: system-metadata: let overrides = systems.hosts.${system-metadata.name} or { }; + user-modules = + if is-darwin system-metadata.target then + user-darwin-modules + else + user-nixos-modules; + user-modules-list = builtins.attrValues user-modules; + system-modules = + if is-darwin system-metadata.target then + darwin-modules + else + nixos-modules; in { ${system-metadata.name} = create-system (overrides // system-metadata // { systems = created-systems; - modules = user-modules ++ (overrides.modules or [ ]) ++ (systems.modules or [ ]); + modules = user-modules-list ++ (overrides.modules or [ ]) ++ system-modules; + inherit homes; }); }; created-systems = fix (created-systems: diff --git a/modules/darwin/home/default.nix b/modules/darwin/home/default.nix new file mode 100644 index 0000000..e9cd0a8 --- /dev/null +++ b/modules/darwin/home/default.nix @@ -0,0 +1,33 @@ +{ lib, config, ... }: + +let + inherit (lib) types mkOption mkMerge mapAttrsToList; + + home-submodule = { name, ... }: { + options = { + proxy = mkOption { + type = types.attrs; + default = { }; + description = "Configuration to be proxied to the home-manager configuration for `home-manager.users.`."; + }; + }; + }; + + cfg = config.snowfallorg; +in +{ + options.snowfallorg = { + home = mkOption { + type = types.attrsOf (types.submodule home-submodule); + default = { }; + description = "Options for configuring home environments."; + }; + }; + + config = mkMerge + (mapAttrsToList + (name: value: { + home-manager.users.${name} = value.proxy; + }) + (cfg.home)); +} diff --git a/modules/home/os/default.nix b/modules/home/os/default.nix new file mode 100644 index 0000000..2da065f --- /dev/null +++ b/modules/home/os/default.nix @@ -0,0 +1,28 @@ +{ lib, osConfig ? { }, ... }: + +let + inherit (lib) types mkOption; + + home-submodule = { name, ... }: { + options = { + proxy = mkOption { + type = types.attrs; + default = { }; + description = "Configuration to be proxied to the home-manager configuration for `home-manager.users.`."; + }; + }; + }; +in +{ + options.snowfallorg = { + home = mkOption { + type = types.attrsOf (types.submodule home-submodule); + default = { }; + description = "Options for configuring home environments."; + }; + }; + + config = { + # snowfallorg.home = osConfig.snowfallorg.home or { }; + }; +} diff --git a/modules/nixos/home/default.nix b/modules/nixos/home/default.nix new file mode 100644 index 0000000..e9cd0a8 --- /dev/null +++ b/modules/nixos/home/default.nix @@ -0,0 +1,33 @@ +{ lib, config, ... }: + +let + inherit (lib) types mkOption mkMerge mapAttrsToList; + + home-submodule = { name, ... }: { + options = { + proxy = mkOption { + type = types.attrs; + default = { }; + description = "Configuration to be proxied to the home-manager configuration for `home-manager.users.`."; + }; + }; + }; + + cfg = config.snowfallorg; +in +{ + options.snowfallorg = { + home = mkOption { + type = types.attrsOf (types.submodule home-submodule); + default = { }; + description = "Options for configuring home environments."; + }; + }; + + config = mkMerge + (mapAttrsToList + (name: value: { + home-manager.users.${name} = value.proxy; + }) + (cfg.home)); +} From 4d6fdba39012848c267ce58aa59a315fb2623dbc Mon Sep 17 00:00:00 2001 From: Jake Hamilton Date: Mon, 10 Apr 2023 16:50:47 -0700 Subject: [PATCH 02/11] wip: better home-manager integration --- flake.nix | 4 +++ lib/home/default.nix | 48 ++++++++++++++++++++------------- modules/darwin/home/default.nix | 33 ----------------------- modules/home/os/default.nix | 28 ------------------- modules/home/user/default.nix | 23 ++++++++++++++++ modules/nixos/home/default.nix | 33 ----------------------- 6 files changed, 57 insertions(+), 112 deletions(-) delete mode 100644 modules/darwin/home/default.nix delete mode 100644 modules/home/os/default.nix create mode 100644 modules/home/user/default.nix delete mode 100644 modules/nixos/home/default.nix diff --git a/flake.nix b/flake.nix index 49ba302..a023d2a 100644 --- a/flake.nix +++ b/flake.nix @@ -38,5 +38,9 @@ in { inherit mkLib mkFlake; + + homeModules = { + user = ./user/default.nix; + }; }; } diff --git a/lib/home/default.nix b/lib/home/default.nix index a956e39..d4e0ad3 100644 --- a/lib/home/default.nix +++ b/lib/home/default.nix @@ -1,7 +1,7 @@ { core-inputs, user-inputs, snowfall-lib }: let - inherit (core-inputs.nixpkgs.lib) assertMsg foldl head tail concatMap optionalAttrs mkIf filterAttrs mapAttrs' mkMerge mapAttrsToList; + inherit (core-inputs.nixpkgs.lib) assertMsg foldl head tail concatMap optionalAttrs mkIf filterAttrs mapAttrs' mkMerge mapAttrsToList optionals mkDefault; user-homes-root = snowfall-lib.fs.get-snowfall-file "homes"; user-modules-root = snowfall-lib.fs.get-snowfall-file "modules"; @@ -157,7 +157,6 @@ in inherit system target format virtual systems host; lib = home-lib; - # pkgs = user-inputs.self.pkgs.${system}.nixpkgs; inputs = snowfall-lib.flake.without-src user-inputs; }; @@ -179,30 +178,43 @@ in let host-matches = created-user.specialArgs.host == host; - wrapped-user-module = home-args: + # @NOTE(jakehamilton): We *must* specify named attributes here in order + # for home-manager to provide them. + wrapped-user-module = home-args@{ pkgs, lib, ... }: let - modified-args = args // { - inherit (created-user.specialArgs) user; - }; - user-module-result = (import user-module (home-args // modified-args)) // { - _file = user-module; - }; + user-module-result = import user-module home-args; + user-imports = + if user-module-result ? imports then + user-module-result.imports + else + [ ]; + user-config = + if user-module-result ? config then + user-module-result.config + else + builtins.removeAttrs user-module-result [ "imports" "options" "_file" ]; in - user-module-result; + { + _file = builtins.toString user-module; + imports = user-imports; + + config = mkMerge [ + user-config + ({ + snowfallorg.user.name = mkDefault created-user.specialArgs.user; + }) + ]; + }; in { _file = "virtual:snowfallorg/home/user/${name}"; - imports = - if snowfall-lib.system.is-darwin created-user.system then - [ ../../modules/darwin/home/default.nix ] - else - [ ../../modules/nixos/home/default.nix ]; - config = mkIf host-matches { home-manager = { - users.${user-name} = wrapped-user-module args; - sharedModules = other-modules; + users.${user-name} = wrapped-user-module; + sharedModules = other-modules ++ [ + ../../modules/home/user/default.nix + ]; }; }; } diff --git a/modules/darwin/home/default.nix b/modules/darwin/home/default.nix deleted file mode 100644 index e9cd0a8..0000000 --- a/modules/darwin/home/default.nix +++ /dev/null @@ -1,33 +0,0 @@ -{ lib, config, ... }: - -let - inherit (lib) types mkOption mkMerge mapAttrsToList; - - home-submodule = { name, ... }: { - options = { - proxy = mkOption { - type = types.attrs; - default = { }; - description = "Configuration to be proxied to the home-manager configuration for `home-manager.users.`."; - }; - }; - }; - - cfg = config.snowfallorg; -in -{ - options.snowfallorg = { - home = mkOption { - type = types.attrsOf (types.submodule home-submodule); - default = { }; - description = "Options for configuring home environments."; - }; - }; - - config = mkMerge - (mapAttrsToList - (name: value: { - home-manager.users.${name} = value.proxy; - }) - (cfg.home)); -} diff --git a/modules/home/os/default.nix b/modules/home/os/default.nix deleted file mode 100644 index 2da065f..0000000 --- a/modules/home/os/default.nix +++ /dev/null @@ -1,28 +0,0 @@ -{ lib, osConfig ? { }, ... }: - -let - inherit (lib) types mkOption; - - home-submodule = { name, ... }: { - options = { - proxy = mkOption { - type = types.attrs; - default = { }; - description = "Configuration to be proxied to the home-manager configuration for `home-manager.users.`."; - }; - }; - }; -in -{ - options.snowfallorg = { - home = mkOption { - type = types.attrsOf (types.submodule home-submodule); - default = { }; - description = "Options for configuring home environments."; - }; - }; - - config = { - # snowfallorg.home = osConfig.snowfallorg.home or { }; - }; -} diff --git a/modules/home/user/default.nix b/modules/home/user/default.nix new file mode 100644 index 0000000..559edc5 --- /dev/null +++ b/modules/home/user/default.nix @@ -0,0 +1,23 @@ +{ lib, options, ... }: + +let + inherit (lib) types mkOption mkIf; + + cfg = options.snowfallorg; +in + # (builtins.trace (cfg.user.name or "no name")) +{ + options.snowfallorg = { + user = { + name = mkOption { + type = types.str; + description = "The user's name."; + }; + }; + }; + + # config = mkIf ((cfg.user.name or null) != null) { + # @TODO(jakehamilton): Get user home directory from osConfig if + # it exists. + # }; +} diff --git a/modules/nixos/home/default.nix b/modules/nixos/home/default.nix deleted file mode 100644 index e9cd0a8..0000000 --- a/modules/nixos/home/default.nix +++ /dev/null @@ -1,33 +0,0 @@ -{ lib, config, ... }: - -let - inherit (lib) types mkOption mkMerge mapAttrsToList; - - home-submodule = { name, ... }: { - options = { - proxy = mkOption { - type = types.attrs; - default = { }; - description = "Configuration to be proxied to the home-manager configuration for `home-manager.users.`."; - }; - }; - }; - - cfg = config.snowfallorg; -in -{ - options.snowfallorg = { - home = mkOption { - type = types.attrsOf (types.submodule home-submodule); - default = { }; - description = "Options for configuring home environments."; - }; - }; - - config = mkMerge - (mapAttrsToList - (name: value: { - home-manager.users.${name} = value.proxy; - }) - (cfg.home)); -} From 9e4d35969942c0d64b7554c783076da72217c5ef Mon Sep 17 00:00:00 2001 From: Jake Hamilton Date: Sat, 27 May 2023 12:24:20 -0700 Subject: [PATCH 03/11] wip: home-manager support --- lib/home/default.nix | 20 +++++++++++++------- lib/system/default.nix | 5 ++++- modules/darwin/home/default.nix | 31 +++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 8 deletions(-) create mode 100644 modules/darwin/home/default.nix diff --git a/lib/home/default.nix b/lib/home/default.nix index d4e0ad3..3f64fe1 100644 --- a/lib/home/default.nix +++ b/lib/home/default.nix @@ -1,7 +1,7 @@ { core-inputs, user-inputs, snowfall-lib }: let - inherit (core-inputs.nixpkgs.lib) assertMsg foldl head tail concatMap optionalAttrs mkIf filterAttrs mapAttrs' mkMerge mapAttrsToList optionals mkDefault; + inherit (core-inputs.nixpkgs.lib) assertMsg foldl head tail concatMap optionalAttrs mkIf filterAttrs mapAttrs' mkMerge mapAttrsToList optionals mkDefault mkAliasDefinitions; user-homes-root = snowfall-lib.fs.get-snowfall-file "homes"; user-modules-root = snowfall-lib.fs.get-snowfall-file "modules"; @@ -57,7 +57,10 @@ in output = "homeConfigurations"; - modules = [ path ] ++ modules; + modules = [ + path + ../../modules/home/user/default.nix + ] ++ modules; specialArgs = { inherit name; @@ -80,6 +83,9 @@ in (module-args: import ./nix-registry-module.nix (module-args // { inherit user-inputs core-inputs; })) + ({ + snowfallorg.user.name = mkDefault user-metadata.user; + }) ]; extraSpecialArgs = specialArgs // args.specialArgs; @@ -180,7 +186,7 @@ in # @NOTE(jakehamilton): We *must* specify named attributes here in order # for home-manager to provide them. - wrapped-user-module = home-args@{ pkgs, lib, ... }: + wrapped-user-module = home-args@{ pkgs, lib, osConfig ? {}, ... }: let user-module-result = import user-module home-args; user-imports = @@ -193,6 +199,7 @@ in user-module-result.config else builtins.removeAttrs user-module-result [ "imports" "options" "_file" ]; + user = created-user.specialArgs.user; in { _file = builtins.toString user-module; @@ -201,8 +208,9 @@ in config = mkMerge [ user-config ({ - snowfallorg.user.name = mkDefault created-user.specialArgs.user; + snowfallorg.user.name = mkDefault user; }) + (osConfig.snowfallorg.home.resolvedHomes.${user} or {}) ]; }; in @@ -212,9 +220,7 @@ in config = mkIf host-matches { home-manager = { users.${user-name} = wrapped-user-module; - sharedModules = other-modules ++ [ - ../../modules/home/user/default.nix - ]; + sharedModules = other-modules; }; }; } diff --git a/lib/system/default.nix b/lib/system/default.nix index 9df24f9..296bf3e 100644 --- a/lib/system/default.nix +++ b/lib/system/default.nix @@ -100,10 +100,13 @@ in darwin-system-builder = args: assert assertMsg (user-inputs ? darwin) "In order to create virtual systems, you must include `darwin` as a flake input."; user-inputs.darwin.lib.darwinSystem - ((builtins.removeAttrs args [ "system" ]) // { + ((builtins.removeAttrs args [ "system" "modules" ]) // { specialArgs = args.specialArgs // { format = "darwin"; }; + modules = args.modules ++ [ + ../../modules/darwin/home/default.nix + ]; }); linux-system-builder = args: core-inputs.nixpkgs.lib.nixosSystem diff --git a/modules/darwin/home/default.nix b/modules/darwin/home/default.nix new file mode 100644 index 0000000..7466dd8 --- /dev/null +++ b/modules/darwin/home/default.nix @@ -0,0 +1,31 @@ +{ lib, options, ... }: + +let + inherit (lib) types mkOption mkIf mkMerge mkAliasDefinitions; + + cfg = options.snowfallorg; +in +{ + options.snowfallorg = { + home = mkOption { + description = "Configuration for home-manager."; + type = types.attrsOf (types.submodule ({ name, ... }: { + options.config = { + type = types.attrs; + default = { }; + }; + })); + }; + + resolvedHomes = mkOption { + type = types.attrs; + default = { }; + }; + }; + + config = mkMerge (builtins.map + (name: { + snowfallorg.resolvedHomes.${name} = mkAliasDefinitions options.snowfallorg.home.${name}.config; + }) + (builtins.attrNames cfg.home)); +} From f85f831b3366e65b5d04388fff4639a7770e611d Mon Sep 17 00:00:00 2001 From: Jake Hamilton Date: Sat, 27 May 2023 21:29:41 -0700 Subject: [PATCH 04/11] feat: home-manager support --- README.md | 216 ++++++++++++++++++++++++++++++-- flake.nix | 10 +- lib/home/default.nix | 25 +++- lib/system/default.nix | 8 +- modules/darwin/home/default.nix | 31 ----- modules/darwin/user/default.nix | 71 +++++++++++ modules/home/user/default.nix | 43 +++++-- modules/nixos/user/default.nix | 84 +++++++++++++ 8 files changed, 432 insertions(+), 56 deletions(-) delete mode 100644 modules/darwin/home/default.nix create mode 100644 modules/darwin/user/default.nix create mode 100644 modules/nixos/user/default.nix diff --git a/README.md b/README.md index 6ea7ca0..7769dcd 100644 --- a/README.md +++ b/README.md @@ -37,12 +37,13 @@ cd config 2. Create a new flake with one of the templates from [@snowfallorg/templates](https://github.com/snowfallorg/templates). -| Name | Description | -| --------- | ------------------------------------------------- | -| `system` | A NixOS system and modules ready to modify. | -| `package` | A Nix Flake that exports packages and an overlay. | -| `module` | A Nix Flake that exports NixOS modules. | -| `lib` | A Nix Flake that exports a custom `lib` | +| Name | Description | +| --------- | ---------------------------------------------------- | +| `system` | A NixOS system and modules ready to modify. | +| `package` | A Nix Flake that exports packages and an overlay. | +| `module` | A Nix Flake that exports NixOS modules. | +| `lib` | A Nix Flake that exports a custom `lib` | +| `empty` | A basic Nix Flake for you to customize from scratch. | ```bash # For example, to use the system template. @@ -195,12 +196,20 @@ snowfall-root/ │ ├─ modules/ (optional modules) │ │ -│ │ Any (nestable) directory name. The name of the directory will be the -│ │ name of the module. -│ └─ **/ +│ │ A directory named after the `platform` type that will be used for modules within. +│ │ +│ │ Supported platforms are: +│ │ - nixos +│ │ - darwin +│ │ - home +│ └─ / │ │ -│ │ A NixOS module. -│ └─ default.nix +│ │ Any (nestable) directory name. The name of the directory will be the +│ │ name of the module. +│ └─ **/ +│ │ +│ │ A NixOS module. +│ └─ default.nix │ ├─ overlays/ (optional overlays) │ │ @@ -246,6 +255,37 @@ snowfall-root/ │ │ │ │ A NixOS module for your system's configuration. │ └─ default.nix +│ +├─ homes/ (optional homes configurations) +│ │ +│ │ A directory named after the `home` type that will be used for all homes within. +│ │ +│ │ The architecture is any supported architecture of NixPkgs, for example: +│ │ - x86_64 +│ │ - aarch64 +│ │ - i686 +│ │ +│ │ The format is any supported NixPkgs format *or* a format provided by either nix-darwin +│ │ or nixos-generators. However, in order to build systems with nix-darwin or nixos-generators, +│ │ you must add `darwin` and `nixos-generators` inputs to your flake respectively. Here +│ │ are some example formats: +│ │ - linux +│ │ - darwin +│ │ - iso +│ │ - install-iso +│ │ - do +│ │ - vmware +│ │ +│ │ With the architecture and format together (joined by a hyphen), you get the name of the +│ │ directory for the home type. +│ └─ -/ +│ │ +│ │ A directory that contains a single home's configuration. The directory name +│ │ will be the name of the home. +│ └─ / +│ │ +│ │ A NixOS module for your home's configuration. +│ └─ default.nix ``` #### Default Flake @@ -526,6 +566,48 @@ type. See the following table for a list of supported formats from NixOS Generat | vm-nogui | Same as vm, but without a GUI | | vmware | VMWare image (VMDK) | +#### Home Manager + +Snowfall Lib supports configuring [Home Manager](https://github.com/nix-community/home-manager) +for both standalone use and for use as a module with NixOS or nix-darwin. To use this feature, +your flake must include `home-manager` as an input. + +```nix +{ + description = "My Flake"; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-22.11"; + + snowfall-lib = { + url = "github:snowfallorg/lib"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + # In order to use Home Manager. + home-manager = { + url = "github:nix-community/home-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = inputs: + # This is an example and in your actual flake you can use `snowfall-lib.mkFlake` + # directly unless you explicitly need a feature of `lib`. + let + lib = inputs.snowfall-lib.mkLib { + # You must pass in both your flake's inputs and the root directory of + # your flake. + inherit inputs; + src = ./.; + }; + in + # No additional configuration is required to use this feature, you only + # have to add home-manager to your flake inputs. + lib.mkFlake { }; +} +``` + ### `lib.snowfall.flake` Helpers related to Nix flakes. @@ -1162,6 +1244,24 @@ Result: "iso" ``` +#### `lib.snowfall.system.get-inferred-system-name` + +Get the name of a system based on its file path. + +Type: `Path -> String` + +Usage: + +```nix +get-inferred-system-name "/systems/my-system/default.nix" +``` + +Result: + +```nix +"my-system" +``` + #### `lib.snowfall.system.get-target-systems-metadata` Get structured data about all systems for a given target. @@ -1261,7 +1361,7 @@ Type: `Attrs -> Attrs` Usage: ```nix -create-systems { hosts.my-host.specialArgs.x = true; modules = [ my-shared-module ]; } +create-systems { hosts.my-host.specialArgs.x = true; modules.nixos = [ my-shared-module ]; } ``` Result: @@ -1270,6 +1370,98 @@ Result: { my-host = ; } ``` +### `lib.snowfall.home` + +#### `lib.snowfall.home.split-user-and-host` + +Get the user and host from a combined string. + +Type: `String -> Attrs` + +Usage: + +```nix +split-user-and-host "myuser@myhost" +``` + +Result: + +```nix +{ user = "myuser"; host = "myhost"; } +``` + +#### `lib.snowfall.home.create-home` + +Create a home. + +Type: `Attrs -> Attrs` + +Usage: + +```nix +create-home { path = ./homes/my-home; } +``` + +Result: + +```nix + +``` + +#### `lib.snowfall.home.create-homes` + +Create all available homes. + +Type: `Attrs -> Attrs` + +Usage: + +```nix +create-homes { users."my-user@my-system".specialArgs.x = true; modules = [ my-shared-module ]; } +``` + +Result: + +```nix +{ "my-user@my-system" = ; } +``` + +#### `lib.snowfall.home.get-target-homes-metadata` + +Get structured data about all homes for a given target. + +Type: `String -> [Attrs]` + +Usage: + +```nix +get-target-homes-metadata ./homes +``` + +Result: + +```nix +[ { system = "x86_64-linux"; name = "my-home"; path = "/homes/x86_64-linux/my-home";} ] +``` + +#### `lib.snowfall.home.create-home-system-modules` + +Create system modules for home-manager integration. + +Type: `Attrs -> [Module]` + +Usage: + +```nix +create-home-system-modules { users."my-user@my-system".specialArgs.x = true; modules = [ my-shared-module ]; } +``` + +Result: + +```nix +[Module] +``` + ### `lib.snowfall.package` Utilities for working with flake packages. diff --git a/flake.nix b/flake.nix index a023d2a..9a5fda2 100644 --- a/flake.nix +++ b/flake.nix @@ -39,8 +39,16 @@ { inherit mkLib mkFlake; + nixosModules = { + user = ./modules/nixos/user/default.nix; + }; + + darwinModules = { + user = ./modules/darwin/user/default.nix; + }; + homeModules = { - user = ./user/default.nix; + user = ./modules/home/user/default.nix; }; }; } diff --git a/lib/home/default.nix b/lib/home/default.nix index 3f64fe1..59bb4e4 100644 --- a/lib/home/default.nix +++ b/lib/home/default.nix @@ -18,6 +18,10 @@ in hm = snowfall-lib.internal.system-lib.home-manager.hm; }); + # Get the user and host from a combined string. + # Type: String -> Attrs + # Usage: split-user-and-host "myuser@myhost" + # result: { user = "myuser"; host = "myhost"; } split-user-and-host = target: let raw-name-parts = builtins.split "@" target; @@ -35,6 +39,10 @@ in }; + # Create a home. + # Type: Attrs -> Attrs + # Usage: create-home { path = ./homes/my-home; } + # result: create-home = { path , name ? builtins.unsafeDiscardStringContext (snowfall-lib.system.get-inferred-system-name path) @@ -92,6 +100,10 @@ in }); }; + # Get structured data about all homes for a given target. + # Type: String -> [Attrs] + # Usage: get-target-homes-metadata ./homes + # result: [ { system = "x86_64-linux"; name = "my-home"; path = "/homes/x86_64-linux/my-home";} ] get-target-homes-metadata = target: let homes = snowfall-lib.fs.get-directories target; @@ -141,6 +153,10 @@ in in created-homes; + # Create system modules for home-manager integration. + # Type: Attrs -> [Module] + # Usage: create-home-system-modules { users."my-user@my-system".specialArgs.x = true; modules = [ my-shared-module ]; } + # result: [Module] create-home-system-modules = users: let created-users = create-homes users; @@ -186,10 +202,10 @@ in # @NOTE(jakehamilton): We *must* specify named attributes here in order # for home-manager to provide them. - wrapped-user-module = home-args@{ pkgs, lib, osConfig ? {}, ... }: + wrapped-user-module = home-args@{ pkgs, lib, osConfig ? { }, ... }: let user-module-result = import user-module home-args; - user-imports = + user-imports = if user-module-result ? imports then user-module-result.imports else @@ -210,7 +226,7 @@ in ({ snowfallorg.user.name = mkDefault user; }) - (osConfig.snowfallorg.home.resolvedHomes.${user} or {}) + (osConfig.snowfallorg.home.resolved-homes.${user} or { }) ]; }; in @@ -218,6 +234,9 @@ in _file = "virtual:snowfallorg/home/user/${name}"; config = mkIf host-matches { + # Initialize user information. + snowfallorg.user.${user-name} = { }; + home-manager = { users.${user-name} = wrapped-user-module; sharedModules = other-modules; diff --git a/lib/system/default.nix b/lib/system/default.nix index 296bf3e..cccf5c5 100644 --- a/lib/system/default.nix +++ b/lib/system/default.nix @@ -96,6 +96,9 @@ in specialArgs = args.specialArgs // { format = virtual-system-type; }; + modules = args.modules ++ [ + ../../modules/nixos/user/default.nix + ]; }); darwin-system-builder = args: assert assertMsg (user-inputs ? darwin) "In order to create virtual systems, you must include `darwin` as a flake input."; @@ -105,7 +108,7 @@ in format = "darwin"; }; modules = args.modules ++ [ - ../../modules/darwin/home/default.nix + ../../modules/darwin/user/default.nix ]; }); linux-system-builder = args: @@ -114,6 +117,9 @@ in specialArgs = args.specialArgs // { format = "linux"; }; + modules = args.modules ++ [ + ../../modules/nixos/user/default.nix + ]; }); in if virtual-system-type != "" then diff --git a/modules/darwin/home/default.nix b/modules/darwin/home/default.nix deleted file mode 100644 index 7466dd8..0000000 --- a/modules/darwin/home/default.nix +++ /dev/null @@ -1,31 +0,0 @@ -{ lib, options, ... }: - -let - inherit (lib) types mkOption mkIf mkMerge mkAliasDefinitions; - - cfg = options.snowfallorg; -in -{ - options.snowfallorg = { - home = mkOption { - description = "Configuration for home-manager."; - type = types.attrsOf (types.submodule ({ name, ... }: { - options.config = { - type = types.attrs; - default = { }; - }; - })); - }; - - resolvedHomes = mkOption { - type = types.attrs; - default = { }; - }; - }; - - config = mkMerge (builtins.map - (name: { - snowfallorg.resolvedHomes.${name} = mkAliasDefinitions options.snowfallorg.home.${name}.config; - }) - (builtins.attrNames cfg.home)); -} diff --git a/modules/darwin/user/default.nix b/modules/darwin/user/default.nix new file mode 100644 index 0000000..13046cc --- /dev/null +++ b/modules/darwin/user/default.nix @@ -0,0 +1,71 @@ +{ pkgs, lib, options, config, ... }: + +let + inherit (lib) types mkOption mkDefault foldl optionalAttrs; + + cfg = config.snowfallorg; + + user-names = builtins.attrNames cfg.user; + + create-system-users = system-users: name: + let + user = cfg.user.${name}; + in + system-users // (optionalAttrs user.create { + ${name} = { + home = mkDefault user.home.path; + isHidden = mkDefault false; + }; + }); + + create-resolved-home = resolved-homes: name: + let + user = cfg.user.${name}; + in + resolved-homes // { + ${name} = user.home.config; + }; +in +{ + options.snowfallorg = { + user = mkOption { + description = "User configuration."; + default = { }; + type = types.attrsOf (types.submodule ({ name, ... }: { + options = { + create = mkOption { + description = "Whether to create the user automatically."; + type = types.bool; + default = true; + }; + + home = { + path = mkOption { + type = types.str; + default = "/Users/${name}"; + }; + + config = mkOption { + type = types.attrs; + default = { }; + }; + }; + }; + })); + }; + + resolved-homes = mkOption { + type = types.attrs; + default = { }; + internal = true; + }; + }; + + config = { + users.users = (foldl (create-system-users) { } (user-names)); + + snowfallorg = { + resolved-homes = (foldl (create-resolved-home) { } (user-names)); + }; + }; +} diff --git a/modules/home/user/default.nix b/modules/home/user/default.nix index 559edc5..3cc9cba 100644 --- a/modules/home/user/default.nix +++ b/modules/home/user/default.nix @@ -1,23 +1,50 @@ -{ lib, options, ... }: +inputs@{ pkgs, lib, options, config, ... }: let - inherit (lib) types mkOption mkIf; + inherit (lib) types mkOption mkIf mkDefault; - cfg = options.snowfallorg; + cfg = config.snowfallorg; + + # @NOTE(jakehamilton): The module system chokes if it finds `osConfig` named in the module arguments + # when being used in standalone home-manager. To remedy this, we have to refer to the arguments set directly. + os-user-home = inputs.osConfig.users.users.${cfg.name}.home or null; + + default-home-directory = + if (os-user-home != null) then + os-user-home + else if pkgs.stdenv.isDarwin then + "/Users/${cfg.user.name}" + else + "/home/${cfg.user.name}"; in - # (builtins.trace (cfg.user.name or "no name")) { options.snowfallorg = { user = { + enable = mkOption { + type = types.bool; + default = true; + description = "Whether to configure the user."; + }; + name = mkOption { type = types.str; description = "The user's name."; }; + + home = { + directory = mkOption { + type = types.str; + description = "The user's home directory."; + default = default-home-directory; + }; + }; }; }; - # config = mkIf ((cfg.user.name or null) != null) { - # @TODO(jakehamilton): Get user home directory from osConfig if - # it exists. - # }; + config = mkIf cfg.user.enable { + home = { + username = mkIf (cfg.user.name or null != null) (mkDefault cfg.user.name); + homeDirectory = mkIf (cfg.user.name or null != null) (mkDefault cfg.user.home.directory); + }; + }; } diff --git a/modules/nixos/user/default.nix b/modules/nixos/user/default.nix new file mode 100644 index 0000000..81410a4 --- /dev/null +++ b/modules/nixos/user/default.nix @@ -0,0 +1,84 @@ +{ pkgs, lib, options, config, ... }: + +let + inherit (lib) types mkOption mkDefault foldl optionalAttrs optional; + + cfg = config.snowfallorg; + + user-names = builtins.attrNames cfg.user; + + create-system-users = system-users: name: + let + user = cfg.user.${name}; + in + system-users // (optionalAttrs user.create { + ${name} = { + isNormalUser = mkDefault true; + + name = mkDefault cfg.name; + + home = mkDefault user.home.path; + group = mkDefault "users"; + + extraGroups = (builtins.trace user.admin) optional user.admin "wheel"; + }; + }); + + create-resolved-home = resolved-homes: name: + let + user = cfg.user.${name}; + in + resolved-homes // { + ${name} = user.home.config; + }; +in +(builtins.trace "hello") +{ + options.snowfallorg = { + user = mkOption { + description = "User configuration."; + default = { }; + type = types.attrsOf (types.submodule ({ name, ... }: { + options = { + create = mkOption { + description = "Whether to create the user automatically."; + type = types.bool; + default = true; + }; + + admin = mkOption { + description = "Whether the user should be added to the wheel group."; + type = types.bool; + default = true; + }; + + home = { + path = mkOption { + type = types.str; + default = "/home/${name}"; + }; + + config = mkOption { + type = types.attrs; + default = { }; + }; + }; + }; + })); + }; + + resolved-homes = mkOption { + type = types.attrs; + default = { }; + internal = true; + }; + }; + + config = { + users.users = (foldl (create-system-users) { } (user-names)); + + snowfallorg = { + resolved-homes = (foldl (create-resolved-home) { } (user-names)); + }; + }; +} From 37950e37a2c6d6ecb8f673a5b8a3c63dc7d1b557 Mon Sep 17 00:00:00 2001 From: Jake Hamilton Date: Sat, 27 May 2023 21:32:31 -0700 Subject: [PATCH 05/11] fix: remove trace --- modules/nixos/user/default.nix | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/nixos/user/default.nix b/modules/nixos/user/default.nix index 81410a4..490a923 100644 --- a/modules/nixos/user/default.nix +++ b/modules/nixos/user/default.nix @@ -32,7 +32,6 @@ let ${name} = user.home.config; }; in -(builtins.trace "hello") { options.snowfallorg = { user = mkOption { From 165d8bb203a6a00670659f03c021e9c580f3f575 Mon Sep 17 00:00:00 2001 From: Jake Hamilton Date: Sun, 28 May 2023 12:20:05 -0700 Subject: [PATCH 06/11] fix: use updated resolved-homes config value --- lib/home/default.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/home/default.nix b/lib/home/default.nix index 59bb4e4..1526351 100644 --- a/lib/home/default.nix +++ b/lib/home/default.nix @@ -226,7 +226,7 @@ in ({ snowfallorg.user.name = mkDefault user; }) - (osConfig.snowfallorg.home.resolved-homes.${user} or { }) + (osConfig.snowfallorg.resolved-homes.${user} or { }) ]; }; in From cfaa78937e55ab3d84632cd240efb9c9bd95280e Mon Sep 17 00:00:00 2001 From: Jake Hamilton Date: Sun, 25 Jun 2023 18:58:14 -0700 Subject: [PATCH 07/11] fix: rework home-manager modules to allow option merging --- lib/home/default.nix | 123 +++++++++++++++++++++++--------- modules/darwin/user/default.nix | 55 ++++++++------ modules/home/user/default.nix | 6 +- modules/nixos/user/default.nix | 51 +++++++------ 4 files changed, 154 insertions(+), 81 deletions(-) diff --git a/lib/home/default.nix b/lib/home/default.nix index 1526351..035d87b 100644 --- a/lib/home/default.nix +++ b/lib/home/default.nix @@ -1,7 +1,26 @@ { core-inputs, user-inputs, snowfall-lib }: let - inherit (core-inputs.nixpkgs.lib) assertMsg foldl head tail concatMap optionalAttrs mkIf filterAttrs mapAttrs' mkMerge mapAttrsToList optionals mkDefault mkAliasDefinitions; + inherit (core-inputs.nixpkgs.lib) + assertMsg + foldl + head + tail + concatMap + optionalAttrs + optional + mkIf + filterAttrs + mapAttrs' + mkMerge + mapAttrsToList + optionals + mkDefault + traceSeqN + mkAliasDefinitions + mkAliasAndWrapDefinitions + mkOption + types; user-homes-root = snowfall-lib.fs.get-snowfall-file "homes"; user-modules-root = snowfall-lib.fs.get-snowfall-file "modules"; @@ -136,7 +155,11 @@ in src = "${user-modules-root}/home"; }; - user-home-modules-list = builtins.attrValues user-home-modules; + user-home-modules-list = mapAttrsToList + (module-path: module: args@{ pkgs, ... }: (module args) // { + _file = "${user-homes-root}/${module-path}/default.nix"; + }) + user-home-modules; create-home' = home-metadata: let @@ -160,6 +183,30 @@ in create-home-system-modules = users: let created-users = create-homes users; + user-home-modules = snowfall-lib.module.create-modules { + src = "${user-modules-root}/home"; + }; + + shared-modules = mapAttrsToList + (module-path: module: { + _file = "${user-modules-root}/home/${module-path}/default.nix"; + + config = { + home-manager.sharedModules = [ module ]; + }; + }) + user-home-modules; + + snowfall-user-home-module = { + _file = "virtual:snowfallorg/modules/home/user/default.nix"; + + config = { + home-manager.sharedModules = [ + ../../modules/home/user/default.nix + ]; + }; + }; + extra-special-args-module = args@{ config , pkgs @@ -184,15 +231,17 @@ in }; }; }; + system-modules = builtins.map (name: let created-user = created-users.${name}; user-module = head created-user.modules; - other-modules = tail created-user.modules; + other-modules = users.users.${name}.modules or [ ]; user-name = created-user.specialArgs.user; in args@{ config + , options , pkgs , host ? "" , ... @@ -200,52 +249,56 @@ in let host-matches = created-user.specialArgs.host == host; - # @NOTE(jakehamilton): We *must* specify named attributes here in order - # for home-manager to provide them. - wrapped-user-module = home-args@{ pkgs, lib, osConfig ? { }, ... }: - let - user-module-result = import user-module home-args; - user-imports = - if user-module-result ? imports then - user-module-result.imports - else - [ ]; - user-config = - if user-module-result ? config then - user-module-result.config - else - builtins.removeAttrs user-module-result [ "imports" "options" "_file" ]; - user = created-user.specialArgs.user; - in - { - _file = builtins.toString user-module; - imports = user-imports; + # @NOTE(jakehamilton): To conform to the config structure of home-manager, we have to + # remap the options coming from `snowfallorg.user..home.config` since `mkAliasDefinitions` + # does not let us target options within a submodule. + wrap-user-options = user-option: + if (user-option ? "_type") && user-option._type == "merge" then + user-option // { + contents = builtins.map + (merge-entry: + merge-entry.${user-name}.home.config or { } + ) + user-option.contents; + } + else + (builtins.trace '' + ============= + Snowfall Lib: + Option value for `snowfallorg.user.${user-name}` was not detected to be merged. - config = mkMerge [ - user-config - ({ - snowfallorg.user.name = mkDefault user; - }) - (osConfig.snowfallorg.resolved-homes.${user} or { }) - ]; - }; + Please report the issue on GitHub with a link to your configuration so we can debug the problem: + https://github.com/snowfallorg/lib/issues/new + ============= + '') + user-option; in { _file = "virtual:snowfallorg/home/user/${name}"; config = mkIf host-matches { # Initialize user information. - snowfallorg.user.${user-name} = { }; + snowfallorg.user.${user-name}.home.config = { + snowfallorg.user.name = mkDefault user-name; + }; home-manager = { - users.${user-name} = wrapped-user-module; - sharedModules = other-modules; + users.${user-name} = mkAliasAndWrapDefinitions wrap-user-options options.snowfallorg.user; + + # sharedModules = other-modules ++ optional config.snowfallorg.user.${user-name}.home.enable wrapped-user-module; + sharedModules = other-modules ++ optional config.snowfallorg.user.${user-name}.home.enable user-module; }; }; } ) (builtins.attrNames created-users); in - [ extra-special-args-module ] ++ system-modules; + [ + extra-special-args-module + snowfall-user-home-module + ] + ++ (users.modules or [ ]) + ++ shared-modules + ++ system-modules; }; } diff --git a/modules/darwin/user/default.nix b/modules/darwin/user/default.nix index 13046cc..28f0931 100644 --- a/modules/darwin/user/default.nix +++ b/modules/darwin/user/default.nix @@ -1,4 +1,4 @@ -{ pkgs, lib, options, config, ... }: +{ pkgs, lib, options, config, inputs, ... }: let inherit (lib) types mkOption mkDefault foldl optionalAttrs; @@ -17,14 +17,6 @@ let isHidden = mkDefault false; }; }); - - create-resolved-home = resolved-homes: name: - let - user = cfg.user.${name}; - in - resolved-homes // { - ${name} = user.home.config; - }; in { options.snowfallorg = { @@ -40,32 +32,51 @@ in }; home = { + enable = mkOption { + type = types.bool; + default = true; + }; + path = mkOption { type = types.str; default = "/Users/${name}"; }; config = mkOption { - type = types.attrs; - default = { }; + # HM-compatible options taken from: + # https://github.com/nix-community/home-manager/blob/0ee5ab611dc1fbb5180bd7d88d2aeb7841a4d179/nixos/common.nix#L14 + type = types.submoduleWith { + specialArgs = { + osConfig = config; + modulesPath = "${inputs.home-manager}/modules"; + } // config.home-manager.extraSpecialArgs; + modules = [ + ({ lib, modulesPath, ... }: { + imports = import "${modulesPath}/modules.nix" { + inherit pkgs lib; + useNixpkgsModule = !config.home-manager.useGlobalPkgs; + }; + + config = { + submoduleSupport.enable = true; + submoduleSupport.externalPackageInstall = cfg.useUserPackages; + + home.username = config.users.users.${name}.name; + home.homeDirectory = config.users.users.${name}.home; + + nix.package = config.nix.package; + }; + }) + ] ++ config.home-manager.sharedModules; + }; }; }; }; })); }; - - resolved-homes = mkOption { - type = types.attrs; - default = { }; - internal = true; - }; }; config = { - users.users = (foldl (create-system-users) { } (user-names)); - - snowfallorg = { - resolved-homes = (foldl (create-resolved-home) { } (user-names)); - }; + users.users = (foldl create-system-users { } (user-names)); }; } diff --git a/modules/home/user/default.nix b/modules/home/user/default.nix index 3cc9cba..b81fc7e 100644 --- a/modules/home/user/default.nix +++ b/modules/home/user/default.nix @@ -9,6 +9,8 @@ let # when being used in standalone home-manager. To remedy this, we have to refer to the arguments set directly. os-user-home = inputs.osConfig.users.users.${cfg.name}.home or null; + has-user-name = (cfg.user.name or null) != null; + default-home-directory = if (os-user-home != null) then os-user-home @@ -43,8 +45,8 @@ in config = mkIf cfg.user.enable { home = { - username = mkIf (cfg.user.name or null != null) (mkDefault cfg.user.name); - homeDirectory = mkIf (cfg.user.name or null != null) (mkDefault cfg.user.home.directory); + username = mkIf has-user-name (mkDefault cfg.user.name); + homeDirectory = mkIf has-user-name (mkDefault cfg.user.home.directory); }; }; } diff --git a/modules/nixos/user/default.nix b/modules/nixos/user/default.nix index 490a923..a4d12b3 100644 --- a/modules/nixos/user/default.nix +++ b/modules/nixos/user/default.nix @@ -1,4 +1,4 @@ -{ pkgs, lib, options, config, ... }: +{ pkgs, lib, options, config, inputs, ... }: let inherit (lib) types mkOption mkDefault foldl optionalAttrs optional; @@ -15,22 +15,15 @@ let ${name} = { isNormalUser = mkDefault true; - name = mkDefault cfg.name; + name = mkDefault name; home = mkDefault user.home.path; group = mkDefault "users"; - extraGroups = (builtins.trace user.admin) optional user.admin "wheel"; + extraGroups = optional user.admin "wheel"; }; }); - create-resolved-home = resolved-homes: name: - let - user = cfg.user.${name}; - in - resolved-homes // { - ${name} = user.home.config; - }; in { options.snowfallorg = { @@ -58,26 +51,40 @@ in }; config = mkOption { - type = types.attrs; - default = { }; + # HM-compatible options taken from: + # https://github.com/nix-community/home-manager/blob/0ee5ab611dc1fbb5180bd7d88d2aeb7841a4d179/nixos/common.nix#L14 + type = types.submoduleWith { + specialArgs = { + osConfig = config; + modulesPath = "${inputs.home-manager}/modules"; + } // config.home-manager.extraSpecialArgs; + modules = [ + ({ lib, modulesPath, ... }: { + imports = import "${modulesPath}/modules.nix" { + inherit pkgs lib; + useNixpkgsModule = !config.home-manager.useGlobalPkgs; + }; + + config = { + submoduleSupport.enable = true; + submoduleSupport.externalPackageInstall = cfg.useUserPackages; + + home.username = config.users.users.${name}.name; + home.homeDirectory = config.users.users.${name}.home; + + nix.package = config.nix.package; + }; + }) + ] ++ config.home-manager.sharedModules; + }; }; }; }; })); }; - - resolved-homes = mkOption { - type = types.attrs; - default = { }; - internal = true; - }; }; config = { users.users = (foldl (create-system-users) { } (user-names)); - - snowfallorg = { - resolved-homes = (foldl (create-resolved-home) { } (user-names)); - }; }; } From 880d907905481fd5360534ba8b2fa11e90cb078c Mon Sep 17 00:00:00 2001 From: Jake Hamilton Date: Sat, 1 Jul 2023 12:22:29 -0700 Subject: [PATCH 08/11] fix: only enable matched user homes --- lib/home/default.nix | 6 ++++-- modules/home/user/default.nix | 2 +- modules/nixos/user/default.nix | 5 +++++ 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/home/default.nix b/lib/home/default.nix index 035d87b..6a56fb8 100644 --- a/lib/home/default.nix +++ b/lib/home/default.nix @@ -16,7 +16,6 @@ let mapAttrsToList optionals mkDefault - traceSeqN mkAliasDefinitions mkAliasAndWrapDefinitions mkOption @@ -279,7 +278,10 @@ in config = mkIf host-matches { # Initialize user information. snowfallorg.user.${user-name}.home.config = { - snowfallorg.user.name = mkDefault user-name; + snowfallorg.user = { + enable = true; + name = mkDefault user-name; + }; }; home-manager = { diff --git a/modules/home/user/default.nix b/modules/home/user/default.nix index b81fc7e..7c7c93a 100644 --- a/modules/home/user/default.nix +++ b/modules/home/user/default.nix @@ -24,7 +24,7 @@ in user = { enable = mkOption { type = types.bool; - default = true; + default = false; description = "Whether to configure the user."; }; diff --git a/modules/nixos/user/default.nix b/modules/nixos/user/default.nix index a4d12b3..757997d 100644 --- a/modules/nixos/user/default.nix +++ b/modules/nixos/user/default.nix @@ -45,6 +45,11 @@ in }; home = { + enable = mkOption { + type = types.bool; + default = true; + }; + path = mkOption { type = types.str; default = "/home/${name}"; From 7d7546913d79a3376ac4ea1d505fc7808a564a91 Mon Sep 17 00:00:00 2001 From: Jake Hamilton Date: Sat, 1 Jul 2023 17:00:35 -0700 Subject: [PATCH 09/11] fix: pass through snowfall configuration to mkLib --- flake.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flake.nix b/flake.nix index 9a5fda2..e0deb3d 100644 --- a/flake.nix +++ b/flake.nix @@ -27,10 +27,10 @@ # A convenience wrapper to create the library and then call `lib.mkFlake`. # Usage: mkFlake { inherit inputs; src = ./.; ... } # result: - mkFlake = flake-and-lib-options@{ inputs, src, ... }: + mkFlake = flake-and-lib-options@{ inputs, src, snowfall ? { }, ... }: let lib = mkLib { - inherit inputs src; + inherit inputs src snowfall; }; flake-options = builtins.removeAttrs flake-and-lib-options [ "inputs" "src" ]; in From 66e133580374d96700cde9396865ba996dfd2a78 Mon Sep 17 00:00:00 2001 From: Jake Hamilton Date: Sat, 1 Jul 2023 17:44:12 -0700 Subject: [PATCH 10/11] fix: pass snowfall-config through to modules --- lib/attrs/default.nix | 1 + lib/default.nix | 7 ++++++- lib/flake/default.nix | 1 + lib/fp/default.nix | 1 + lib/fs/default.nix | 3 ++- lib/home/default.nix | 6 +++++- lib/internal/default.nix | 1 + lib/module/default.nix | 1 + lib/overlay/default.nix | 1 + lib/package/default.nix | 1 + lib/path/default.nix | 1 + lib/shell/default.nix | 1 + lib/system/default.nix | 1 + lib/template/default.nix | 1 + 14 files changed, 24 insertions(+), 3 deletions(-) diff --git a/lib/attrs/default.nix b/lib/attrs/default.nix index 9896757..c7a171f 100644 --- a/lib/attrs/default.nix +++ b/lib/attrs/default.nix @@ -1,6 +1,7 @@ { core-inputs , user-inputs , snowfall-lib +, snowfall-config }: let diff --git a/lib/default.nix b/lib/default.nix index ee1c6e8..05f0c3d 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -6,6 +6,11 @@ core-inputs: user-options: let + raw-snowfall-config = user-options.snowfall or { }; + snowfall-config = raw-snowfall-config // { + root = raw-snowfall-config.root or user-options.src; + }; + user-inputs = user-options.inputs // { src = user-options.src; }; inherit (core-inputs.nixpkgs.lib) assertMsg fix filterAttrs mergeAttrs fold recursiveUpdate callPackageWith; @@ -60,7 +65,7 @@ let snowfall-lib = fix (snowfall-lib: let attrs = { - inherit snowfall-lib core-inputs user-inputs; + inherit snowfall-lib snowfall-config core-inputs user-inputs; }; libs = builtins.map (dir: import "${snowfall-lib-root}/${dir}" attrs) diff --git a/lib/flake/default.nix b/lib/flake/default.nix index 979b3e2..26909ae 100644 --- a/lib/flake/default.nix +++ b/lib/flake/default.nix @@ -1,6 +1,7 @@ { core-inputs , user-inputs , snowfall-lib +, snowfall-config }: let diff --git a/lib/fp/default.nix b/lib/fp/default.nix index 78da0ee..63e0826 100644 --- a/lib/fp/default.nix +++ b/lib/fp/default.nix @@ -1,6 +1,7 @@ { core-inputs , user-inputs , snowfall-lib +, snowfall-config }: let diff --git a/lib/fs/default.nix b/lib/fs/default.nix index 3791a81..54cbe64 100644 --- a/lib/fs/default.nix +++ b/lib/fs/default.nix @@ -1,6 +1,7 @@ { core-inputs , user-inputs , snowfall-lib +, snowfall-config }: let @@ -31,7 +32,7 @@ in # Type: Path -> Path # Usage: get-snowfall-file "systems" # result: "/user-source/snowfall-dir/systems" - get-snowfall-file = path: "${user-inputs.snowfall.root or user-inputs.src}/${path}"; + get-snowfall-file = path: "${snowfall-config.root}/${path}"; # Get a file path relative to the this flake. # Type: Path -> Path diff --git a/lib/home/default.nix b/lib/home/default.nix index 6a56fb8..c2ea41b 100644 --- a/lib/home/default.nix +++ b/lib/home/default.nix @@ -1,4 +1,8 @@ -{ core-inputs, user-inputs, snowfall-lib }: +{ core-inputs +, user-inputs +, snowfall-lib +, snowfall-config +}: let inherit (core-inputs.nixpkgs.lib) diff --git a/lib/internal/default.nix b/lib/internal/default.nix index dfa4a78..40aa8b3 100644 --- a/lib/internal/default.nix +++ b/lib/internal/default.nix @@ -1,6 +1,7 @@ { core-inputs , user-inputs , snowfall-lib +, snowfall-config }: let diff --git a/lib/module/default.nix b/lib/module/default.nix index 7b647cb..6ef087a 100644 --- a/lib/module/default.nix +++ b/lib/module/default.nix @@ -1,6 +1,7 @@ { core-inputs , user-inputs , snowfall-lib +, snowfall-config }: let diff --git a/lib/overlay/default.nix b/lib/overlay/default.nix index cc8dccd..be2ea5d 100644 --- a/lib/overlay/default.nix +++ b/lib/overlay/default.nix @@ -1,6 +1,7 @@ { core-inputs , user-inputs , snowfall-lib +, snowfall-config }: let diff --git a/lib/package/default.nix b/lib/package/default.nix index 3968ecd..b77b51c 100644 --- a/lib/package/default.nix +++ b/lib/package/default.nix @@ -1,6 +1,7 @@ { core-inputs , user-inputs , snowfall-lib +, snowfall-config }: let diff --git a/lib/path/default.nix b/lib/path/default.nix index 216f194..602f42f 100644 --- a/lib/path/default.nix +++ b/lib/path/default.nix @@ -1,6 +1,7 @@ { core-inputs , user-inputs , snowfall-lib +, snowfall-config }: let diff --git a/lib/shell/default.nix b/lib/shell/default.nix index aecd0e2..4eb5aec 100644 --- a/lib/shell/default.nix +++ b/lib/shell/default.nix @@ -1,6 +1,7 @@ { core-inputs , user-inputs , snowfall-lib +, snowfall-config }: let diff --git a/lib/system/default.nix b/lib/system/default.nix index cccf5c5..6d9bfa5 100644 --- a/lib/system/default.nix +++ b/lib/system/default.nix @@ -1,6 +1,7 @@ { core-inputs , user-inputs , snowfall-lib +, snowfall-config }: let diff --git a/lib/template/default.nix b/lib/template/default.nix index 3e04626..6527c3a 100644 --- a/lib/template/default.nix +++ b/lib/template/default.nix @@ -1,6 +1,7 @@ { core-inputs , user-inputs , snowfall-lib +, snowfall-config }: let From 6b09a4b7b53c3c2531b6955a1d5e041fa10330f2 Mon Sep 17 00:00:00 2001 From: Jake Hamilton Date: Sat, 1 Jul 2023 18:47:47 -0700 Subject: [PATCH 11/11] fix: do not pass snowfall config through to flake output --- lib/flake/default.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/flake/default.nix b/lib/flake/default.nix index 26909ae..15de25a 100644 --- a/lib/flake/default.nix +++ b/lib/flake/default.nix @@ -48,6 +48,7 @@ rec { "templates" "package-namespace" "alias" + "snowfall" ]; # Transform an attribute set of inputs into an attribute set where