diff --git a/configs/flake-parts/default.nix b/configs/flake-parts/default.nix index 94cce650..b883190e 100644 --- a/configs/flake-parts/default.nix +++ b/configs/flake-parts/default.nix @@ -57,7 +57,6 @@ nixpkgs.overlays = lib.attrValues inputs.self.overlays; }; - defaultSystem = "x86_64-linux"; defaultOverlays = lib.attrValues inputs.self.overlays; defaultExtraArgs = { inherit (inputs) nix-colors; @@ -66,6 +65,8 @@ perSystem = { lib, system, ... }: { _module.args = { + # nixpkgs for this module should be used as less as possible especially + # for building NixOS and home-manager systems. pkgs = import inputs.nixpkgs { inherit system; overlays = lib.attrValues inputs.self.overlays ++ [ diff --git a/configs/flake-parts/home-manager.nix b/configs/flake-parts/home-manager.nix index fc6ae360..61301cbd 100644 --- a/configs/flake-parts/home-manager.nix +++ b/configs/flake-parts/home-manager.nix @@ -1,7 +1,6 @@ { inputs , lib -, defaultSystem , defaultExtraArgs , defaultNixConf @@ -9,26 +8,12 @@ }: let - homeManagerConfigs = import ../../setups/home-manager.nix { inherit lib inputs; }; - # The default config for our home-manager configurations. This is also to # be used for sharing modules among home-manager users from NixOS # configurations with `nixpkgs.useGlobalPkgs` set to `true` so avoid # setting nixpkgs-related options here. defaultHomeManagerConfig = { pkgs, config, lib, ... }: { - imports = - # Import our own custom modules from here.. - import ../../modules/home-manager { inherit lib; isInternal = true; } - - # ...plus a bunch of third-party modules. - ++ [ - inputs.nur.hmModules.nur - inputs.sops-nix.homeManagerModules.sops - inputs.nix-index-database.hmModules.nix-index - inputs.nix-colors.homeManagerModules.default - ]; - # Set some extra, yeah? _module.args = defaultExtraArgs; @@ -73,79 +58,64 @@ let home.stateVersion = lib.mkDefault "23.11"; }; - - # A function that generates a home-manager module from a given user - # metadata. - userSpecificModule = user: metadata: - let - name = metadata.username or metadata._name or user; - modules = metadata.modules or [ ]; - in - { lib, pkgs, config, ... }: { - imports = modules ++ [ - defaultHomeManagerConfig - defaultNixConf - ../home-manager/${name} - ]; - - # Don't create the user directories since they are assumed to - # be already created by a pre-installed system (which should - # already handle them). - xdg.userDirs.createDirectories = lib.mkForce false; - - # Setting the homely options. - home.username = lib.mkForce name; - home.homeDirectory = lib.mkForce (metadata.home-directory or "/home/${config.home.username}"); - - programs.home-manager.enable = lib.mkForce true; - targets.genericLinux.enable = true; - }; in { + setups.home-manager = { + configs = { + foo-dogsquared = { + systems = [ "aarch64-linux" "x86_64-linux" ]; + overlays = [ + # Neovim nightly! + inputs.neovim-nightly-overlay.overlays.default + + # Emacs unstable version! + inputs.emacs-overlay.overlays.default + + # Helix master! + inputs.helix-editor.overlays.default + + # Get all of the NUR. + inputs.nur.overlay + ]; + modules = [ + inputs.nix-colors.homeManagerModules.default + inputs.nur.hmModules.nur + ]; + }; + + plover.systems = [ "x86_64-linux" ]; + }; + + # This is to be used by the NixOS `home-manager.sharedModules` anyways. + sharedModules = + # Import our own custom modules from here.. + import ../../modules/home-manager { inherit lib; isInternal = true; } + + # ...plus a bunch of third-party modules. + ++ [ + inputs.sops-nix.homeManagerModules.sops + inputs.nix-index-database.hmModules.nix-index + + defaultHomeManagerConfig + ]; + + standaloneConfigModules = [ + defaultNixConf + + ({ config, lib, ... }: { + # Don't create the user directories since they are assumed to + # be already created by a pre-installed system (which should + # already handle them). + xdg.userDirs.createDirectories = lib.mkForce false; + + programs.home-manager.enable = lib.mkForce true; + targets.genericLinux.enable = true; + }) + ]; + }; + flake = { # Extending home-manager with my custom modules, if anyone cares. homeModules.default = import ../../modules/home-manager { inherit lib; }; - - # Put them home-manager configurations. - homeConfigurations = - let - inherit (import ../../lib/extras/flake-helpers.nix { inherit lib inputs; }) mkHome listImagesWithSystems; - in - lib.mapAttrs - (user: metadata: - mkHome { - pkgs = import inputs.${metadata.nixpkgs-channel or "nixpkgs"} { - system = metadata._system; - }; - extraModules = [ (userSpecificModule user metadata) ]; - home-manager-channel = metadata.home-manager-channel or "home-manager"; - }) - (listImagesWithSystems homeManagerConfigs); - - # Include these as part of the deploy-rs nodes because why not. - deploy.nodes = - lib.mapAttrs' - (name: value: - let - metadata = homeManagerConfigs.${name}; - username = metadata.deploy.username or name; - in - lib.nameValuePair "home-manager-${name}" { - hostname = metadata.deploy.hostname or name; - autoRollback = metadata.deploy.auto-rollback or true; - magicRollback = metadata.deploy.magic-rollback or true; - fastConnection = metadata.deploy.fast-connection or true; - remoteBuild = metadata.deploy.remote-build or false; - profiles.home = { - sshUser = metadata.deploy.ssh-user or username; - user = metadata.deploy.user or username; - path = inputs.deploy.lib.${metadata.system or defaultSystem}.activate.home-manager value; - }; - }) - inputs.self.homeConfigurations; - }; - - _module.args = { - inherit homeManagerConfigs defaultHomeManagerConfig; }; } diff --git a/configs/flake-parts/nixos.nix b/configs/flake-parts/nixos.nix index ee8145b7..3c51abac 100644 --- a/configs/flake-parts/nixos.nix +++ b/configs/flake-parts/nixos.nix @@ -1,52 +1,13 @@ { inputs , lib -, defaultSystem , defaultExtraArgs , defaultNixConf -, defaultHomeManagerConfig , ... }: let - inherit (import ../../lib/extras/flake-helpers.nix { inherit lib inputs; }) mkHost mkImage listImagesWithSystems; - - nixosConfigs = import ../../setups/nixos.nix { inherit lib inputs; }; - - # A function that generates a NixOS module setting up the baseline - # configuration for this project (or at least for this subset of NixOS - # configurations). - hostSpecificModule = host: metadata: - let - modules = metadata.modules or [ ]; - name = metadata._name or host; - in - { lib, ... }: { - imports = modules ++ [ - inputs.${metadata.home-manager-channel or "home-manager"}.nixosModules.home-manager - - defaultNixOSConfig - defaultNixConf - ../nixos/${host} - ]; - - config = lib.mkMerge [ - { - networking.hostName = lib.mkForce metadata.hostname or name; - nixpkgs.hostPlatform = metadata._system or defaultSystem; - - # The global configuration for the home-manager module. - home-manager.useUserPackages = lib.mkDefault true; - home-manager.useGlobalPkgs = lib.mkDefault true; - home-manager.sharedModules = [ defaultHomeManagerConfig ]; - } - - (lib.mkIf (metadata ? domain) - { networking.domain = lib.mkForce metadata.domain; }) - ]; - }; - # The shared configuration for the entire list of hosts for this cluster. # Take note to only set as minimal configuration as possible since we're # also using this with the stable version of nixpkgs. @@ -60,23 +21,6 @@ let XDG_STATE_HOME = "$HOME/.local/state"; }; - # Only use imports as minimally as possible with the absolute - # requirements of a host. On second thought, only on flakes with - # optional NixOS modules. - imports = - # Append with our custom NixOS modules from the modules folder. - import ../../modules/nixos { inherit lib; isInternal = true; } - - # Then, make the most with the modules from the flake inputs. Take - # note importing some modules such as home-manager are as part of the - # declarative host config so be sure to check out - # `hostSpecificModule` function as well as the declarative host setup. - ++ [ - inputs.nix-index-database.nixosModules.nix-index - inputs.sops-nix.nixosModules.sops - inputs.disko.nixosModules.disko - ]; - _module.args = defaultExtraArgs; # Find Nix files with these! Even if nix-index is already enabled, it @@ -133,80 +77,100 @@ let }; in { + setups.nixos = { + configs = { + # The main desktop. + ni = { + systems = [ "x86_64-linux" ]; + formats = null; + overlays = [ + # Neovim nightly! + inputs.neovim-nightly-overlay.overlays.default + + # Emacs unstable version! + inputs.emacs-overlay.overlays.default + + # Helix master! + inputs.helix-editor.overlays.default + + # Access to NUR. + inputs.nur.overlay + ]; + modules = [ + inputs.nur.nixosModules.nur + ]; + }; + + # A remote server. + plover = { + systems = [ "x86_64-linux" ]; + formats = null; + domain = "foodogsquared.one"; + deploy = { + hostname = "plover.foodogsquared.one"; + auto-rollback = true; + magic-rollback = true; + }; + }; + + # TODO: Remove extra newlines that are here for whatever reason. + #{{{ + void = { + systems = [ "x86_64-linux" ]; + formats = [ "vm" ]; + }; + #}}} + + # The barely customized non-graphical installer. + bootstrap = { + systems = [ "aarch64-linux" "x86_64-linux" ]; + formats = [ "install-iso" ]; + nixpkgs-branch = "nixos-unstable-small"; + }; + + # The barely customized graphical installer. + graphical-installer = { + systems = [ "aarch64-linux" "x86_64-linux" ]; + formats = [ "install-iso" ]; + }; + + # The WSL system (that is yet to be used). + winnowing = { + systems = [ "x86_64-linux" ]; + formats = null; + overlays = [ + inputs.neovim-nightly-overlay.overlays.default + ]; + modules = [ + # Well, well, well... + inputs.nixos-wsl.nixosModules.default + ]; + }; + }; + + # Only use imports as minimally as possible with the absolute + # requirements of a host. On second thought, only on flakes with + # optional NixOS modules. + sharedModules = + # Append with our custom NixOS modules from the modules folder. + import ../../modules/nixos { inherit lib; isInternal = true; } + + # Then, make the most with the modules from the flake inputs. Take + # note importing some modules such as home-manager are as part of the + # declarative host config so be sure to check out + # `hostSpecificModule` function as well as the declarative host setup. + ++ [ + inputs.nix-index-database.nixosModules.nix-index + inputs.sops-nix.nixosModules.sops + inputs.disko.nixosModules.disko + + defaultNixConf + defaultNixOSConfig + ]; + }; + flake = { # Listing my public NixOS modules if anyone cares. nixosModules.default = import ../../modules/nixos { inherit lib; }; - - # A list of NixOS configurations from the `./configs/nixos` folder starting - # from project root. It also has some sensible default configurations. - nixosConfigurations = - lib.mapAttrs - (user: metadata: - mkHost { - nixpkgs-channel = metadata.nixpkgs-channel or "nixpkgs"; - extraModules = [ (hostSpecificModule user metadata) ]; - }) - (listImagesWithSystems nixosConfigs); - - # Deploy them server configs like a lazy bum-bum. - # - # Anyways, don't forget to flush out your shell history regularly or make - # it ignored which is a more ergonomic option. - deploy.nodes = - lib.mapAttrs' - (name: value: - let - metadata = nixosConfigs.${name}; - in - lib.nameValuePair "nixos-${name}" { - hostname = metadata.deploy.hostname or name; - autoRollback = metadata.deploy.auto-rollback or true; - magicRollback = metadata.deploy.magic-rollback or true; - fastConnection = metadata.deploy.fast-connection or true; - remoteBuild = metadata.deploy.remote-build or false; - profiles.system = { - sshUser = metadata.deploy.ssh-user or "admin"; - user = "root"; - path = inputs.deploy.lib.${metadata.system or defaultSystem}.activate.nixos value; - }; - }) - inputs.self.nixosConfigurations; - }; - - perSystem = { system, lib, ... }: { - # This contains images that are meant to be built and distributed - # somewhere else including those NixOS configurations that are built as - # an ISO. - images = - let - validImages = lib.filterAttrs - (host: metadata: - metadata.format != null && (lib.elem system metadata.systems)) - nixosConfigs; - in - lib.mapAttrs' - (host: metadata: - let - name = metadata.hostname or host; - nixpkgs-channel = metadata.nixpkgs-channel or "nixpkgs"; - in - lib.nameValuePair name (mkImage { - inherit (metadata) format; - inherit nixpkgs-channel; - extraModules = [ - (hostSpecificModule host metadata) - - # Forcing the host platform set by the host (if there's any). - # Ideally, there shouldn't be. - ({ lib, ... }: { - nixpkgs.hostPlatform = lib.mkForce system; - }) - ]; - })) - validImages; - }; - - _module.args = { - inherit defaultNixOSConfig nixosConfigs; }; } diff --git a/lib/extras/flake-helpers.nix b/lib/extras/flake-helpers.nix deleted file mode 100644 index 4b92c700..00000000 --- a/lib/extras/flake-helpers.nix +++ /dev/null @@ -1,103 +0,0 @@ -# A set of functions intended for creating images. THis is meant to be imported -# for use in flake.nix and nowhere else. -{ inputs, lib }: - -let - # A function that generates a lambda suitable for `lib.extend`. - extendLib = import ./extend-lib.nix; - - # For NixOS systems. - specialArgs = { - foodogsquaredModulesPath = builtins.toString ../../modules/nixos; - }; -in -{ - # A thin wrapper around the NixOS configuration function. - mkHost = { extraModules ? [ ], nixpkgs-channel ? "nixpkgs" }: - let - nixpkgs = inputs.${nixpkgs-channel}; - - # Just to be sure, we'll use everything with the given nixpkgs' stdlib. - lib' = nixpkgs.lib.extend extendLib; - - # A modified version of `nixosSystem` from nixpkgs flake. There is a - # recent change at nixpkgs (at 039f73f134546e59ec6f1b56b4aff5b81d889f64) - # that prevents setting our own custom functions so we'll have to - # evaluate the NixOS system ourselves. - nixosSystem = args: import "${nixpkgs}/nixos/lib/eval-config.nix" args; - in - (lib'.makeOverridable nixosSystem) { - inherit specialArgs; - lib = lib'; - modules = extraModules; - - # Since we're setting it through nixpkgs.hostPlatform, we'll have to pass - # this as null. - system = null; - }; - - # A thin wrapper around the home-manager configuration function. - mkHome = { pkgs, extraModules ? [ ], home-manager-channel ? "home-manager" }: - inputs.${home-manager-channel}.lib.homeManagerConfiguration { - specialArgs = { - foodogsquaredModulesPath = builtins.toString ../../modules/home-manager; - }; - - inherit pkgs; - lib = pkgs.lib.extend extendLib; - modules = extraModules; - }; - - # A thin wrapper around the nixos-generators `nixosGenerate` function. - mkImage = { nixpkgs-channel ? "nixpkgs", extraModules ? [ ], format ? "iso" }: - let - nixpkgs = inputs.${nixpkgs-channel}; - - # A modified version of `nixosSystem` from nixpkgs flake similar to the - # one found in mkHost. - nixosSystem = args: import "${nixpkgs}/nixos/lib/eval-config.nix" args; - - image = nixosSystem { - inherit specialArgs; - lib = nixpkgs.lib.extend extendLib; - modules = extraModules ++ [ inputs.nixos-generators.nixosModules.${format} ]; - - # We're also setting this up modularly so we'll have to pass these as - # null. - system = null; - }; - in - image.config.system.build.${image.config.formatAttr}; - - # A function to modify the given table of declarative setups (i.e., hosts, - # users) to have its own system attribute and its name. - # - # If the given setup only has one system, its name will stay the same. - # Otherwise, it will be appended with the system as part of the name (e.g., - # `$NAME-$SYSTEM`). - listImagesWithSystems = data: - lib.foldlAttrs - (acc: name: metadata: - let - name' = metadata.hostname or name; - in - if lib.length metadata.systems > 1 then - acc // (lib.foldl - (images: system: images // { - "${name'}-${system}" = metadata // { - _system = system; - _name = name'; - }; - }) - { } - metadata.systems) - else - acc // { - "${name'}" = metadata // { - _system = lib.head metadata.systems; - _name = name'; - }; - }) - { } - data; -} diff --git a/modules/flake-parts/default.nix b/modules/flake-parts/default.nix index 8a30fb12..5def00ce 100644 --- a/modules/flake-parts/default.nix +++ b/modules/flake-parts/default.nix @@ -7,5 +7,7 @@ { imports = [ ./images.nix + ./deploy-rs-nodes.nix + ./setups ]; } diff --git a/modules/flake-parts/deploy-rs-nodes.nix b/modules/flake-parts/deploy-rs-nodes.nix new file mode 100644 index 00000000..430664bf --- /dev/null +++ b/modules/flake-parts/deploy-rs-nodes.nix @@ -0,0 +1,25 @@ +{ lib, flake-parts-lib, ... }: + +let + deployType = { config, lib, pkgs, ... }: { + options.nodes = lib.mkOption { + type = with lib.types; attrsOf anything; + description = '' + A set of deploy-rs nodes. + ''; + }; + }; +in +{ + options = { + flake = flake-parts-lib.mkSubmoduleOptions { + deploy = lib.mkOption { + type = with lib.types; submodule deployType; + default = { }; + description = '' + An attribute set of deploy-rs nodes + ''; + }; + }; + }; +} diff --git a/modules/flake-parts/setups/default.nix b/modules/flake-parts/setups/default.nix new file mode 100644 index 00000000..fe9a3321 --- /dev/null +++ b/modules/flake-parts/setups/default.nix @@ -0,0 +1,6 @@ +{ + imports = [ + ./nixos.nix + ./home-manager.nix + ]; +} diff --git a/modules/flake-parts/setups/home-manager.nix b/modules/flake-parts/setups/home-manager.nix new file mode 100644 index 00000000..11d5031f --- /dev/null +++ b/modules/flake-parts/setups/home-manager.nix @@ -0,0 +1,264 @@ +# This is the declarative user management converted into a flake-parts module. +{ config, lib, inputs, ... }: + +let + cfg = config.setups.home-manager; + + # A thin wrapper around the home-manager configuration function. + mkHome = { system, nixpkgs-branch ? "nixpkgs", home-manager-branch ? "home-manager", extraModules ? [ ] }: + let + pkgs = inputs.${nixpkgs-branch}.legacyPackages.${system}; + in + inputs.${home-manager-branch}.lib.homeManagerConfiguration { + extraSpecialArgs = { + foodogsquaredModulesPath = builtins.toString ../../home-manager; + }; + + inherit pkgs; + lib = pkgs.lib.extend (import ../../../lib/extras/extend-lib.nix); + modules = extraModules; + }; + + deploySettingsType = { config, lib, ... }: { + freeformType = with lib.types; attrsOf anything; + + options = { + fastConnection = lib.mkEnableOption "deploy-rs to assume the target machine is considered fast"; + autoRollback = lib.mkEnableOption "deploy-rs auto-rollback feature"; + magicRollback = lib.mkEnableOption "deploy-rs magic rollback feature"; + remoteBuild = lib.mkEnableOption "pass the build process to the remote machine"; + profiles = lib.mkOption { + type = with lib.types; functionTo (attrsOf anything); + default = os: { + sshUser = "root"; + user = "admin"; + path = inputs.deploy-rs.lib.${os.system}.activate.nixos os.config; + }; + defaultText = lib.literalExpression '' + os: { + sshUser = "root"; + user = "admin"; + path = inputs.deploy-rs.lib.''${os.system}.activate.nixos os.config; + } + ''; + description = '' + A set of profiles for the resulting deploy node. + + Since each config can result in more than one NixOS system, it has to + be a function where the passed argument is an attribute set with the + following values: + + * `name` is the attribute name from `configs`. + * `config` is the NixOS configuration itself. + * `system` is a string indicating the platform of the NixOS system. + + If unset, it will create a deploy-rs node profile called `home` + similar to those from nixops. + ''; + }; + }; + }; + + configType = { config, name, lib, ... }: { + options = { + systems = lib.mkOption { + type = with lib.types; listOf str; + default = lib.lists.take 1 config.systems; + example = [ "x86_64-linux" "aarch64-linux" ]; + description = '' + A list of platforms that the NixOS configuration is supposed to be + deployed on. + ''; + }; + + modules = lib.mkOption { + type = with lib.types; listOf raw; + default = []; + description = '' + A list of NixOS modules specific for that host. + ''; + }; + + overlays = lib.mkOption { + type = with lib.types; listOf (functionTo raw); + default = []; + example = lib.literalExpression '' + [ + inputs.neovim-nightly-overlay.overlays.default + inputs.emacs-overlay.overlays.default + ] + ''; + description = '' + A list of overlays to be applied for that host. + ''; + }; + + nixpkgs-branch = lib.mkOption { + type = lib.types.str; + default = "nixpkgs"; + example = "nixos-unstable-small"; + description = '' + The nixpkgs branch to be used for evaluating the NixOS configuration. + By default, it will use the `nixpkgs` flake input. + + ::: {.note} + This is based from your flake inputs and not somewhere else. If you + want to have support for multiple nixpkgs branch, simply add them as + a flake input. + ::: + ''; + }; + + home-manager-branch = lib.mkOption { + type = lib.types.str; + default = "home-manager"; + example = "home-manager-stable"; + description = '' + The home-manager branch to be used for the NixOS module. By default, + it will use the `home-manager` flake input. + ''; + }; + + home-directory = lib.mkOption { + type = lib.types.path; + default = "/home/${name}"; + example = "/var/home/public-user"; + description = '' + The home directory of the home-manager user. + ''; + }; + + deploy = lib.mkOption { + type = with lib.types; nullOr (submodule deploySettingsType); + default = null; + description = '' + deploy-rs settings to be passed onto the home-manager configuration node. + ''; + }; + }; + + config = { + modules = [ + ../../../configs/home-manager/${name} + + { + nixpkgs.overlays = config.overlays; + home.username = lib.mkForce name; + home.homeDirectory = lib.mkForce config.home-directory; + } + ]; + }; + }; +in +{ + options.setups.home-manager = { + sharedModules = lib.mkOption { + type = with lib.types; listOf raw; + default = []; + description = '' + A list of modules to be shared by all of the declarative home-manager + setups. + + ::: {.note} + Note this will be shared into NixOS as well through the home-manager + NixOS module. + ::: + ''; + }; + + standaloneConfigModules = lib.mkOption { + type = with lib.types; listOf raw; + default = []; + internal = true; + description = '' + A list of modules to be added alongside the shared home-manager modules + in the standalone home-manager configurations. + + This is useful for modules that are only suitable for standalone + home-manager configurations compared to home-manager configurations + used as a NixOS module. + ''; + }; + + configs = lib.mkOption { + type = with lib.types; attrsOf (submodule configType); + default = {}; + description = '' + An attribute set of metadata for the declarative home-manager setups. + ''; + example = lib.literalExpression '' + { + foo-dogsquared = { + systems = [ "aarch64-linux" "x86_64-linux" ]; + modules = [ + inputs.nur.hmModules.nur + inputs.nixvim.homeManagerModules.nixvim + ]; + overlays = [ + inputs.neovim-nightly-overlay.overlays.default + inputs.emacs-overlay.overlays.default + inputs.helix-editor.overlays.default + inputs.nur.overlay + ]; + }; + + plover.systems = [ "x86_64-linux" ]; + } + ''; + }; + }; + + config = lib.mkIf (cfg.configs != {}) { + flake = + let + # A quick data structure we can pass through multiple build pipelines. + pureHomeManagerConfigs = + let + generatePureConfigs = username: metadata: + lib.listToAttrs + (builtins.map + (system: + let + name = "${username}-${system}"; + in + lib.nameValuePair name (mkHome { + inherit (metadata) nixpkgs-branch home-manager-branch; + inherit system; + extraModules = + cfg.sharedModules + ++ cfg.standaloneConfigModules + ++ metadata.modules; + }) + ) + metadata.systems); + in + lib.mapAttrs + (hostname: metadata: + generatePureConfigs hostname metadata) + cfg.configs; + in + { + homeConfigurations = + lib.concatMapAttrs + (name: configs: + lib.mapAttrs' + (system: config: lib.nameValuePair "${name}-${system}" config) + configs) + pureHomeManagerConfigs; + + deploy.nodes = + let + validConfigs = + lib.filterAttrs + (name: _: cfg.configs.${name}.deploy != null) + pureHomeManagerConfigs; + in + lib.concatMapAttrs + (name: configs: + lib.mapAttrs' + (system: config: lib.nameValuePair "home-manager-${name}-${system}" + (cfg.configs.${name}.deploy.profiles { inherit name config system; }))) + validConfigs; + }; + }; +} diff --git a/modules/flake-parts/setups/nixos.nix b/modules/flake-parts/setups/nixos.nix new file mode 100644 index 00000000..afd71f23 --- /dev/null +++ b/modules/flake-parts/setups/nixos.nix @@ -0,0 +1,357 @@ +# This is the declarative host management converted into a flake-parts module. +# It also enforces a structure for declarative NixOS setups such as a mandatory +# inclusion of the home-manager NixOS module, a deploy-rs node, a hostname and +# an optional domain, and deploy-rs-related options so it isn't really made to +# be generic or anything like that. +{ config, lib, inputs, ... }: + +let + cfg = config.setups.nixos; + + # A thin wrapper around the NixOS configuration function. + mkHost = { extraModules ? [ ], nixpkgs-branch ? "nixpkgs", system }: + let + nixpkgs = inputs.${nixpkgs-branch}; + + # Just to be sure, we'll use everything with the given nixpkgs' stdlib. + lib' = nixpkgs.lib.extend (import ../../../lib/extras/extend-lib.nix); + + # A modified version of `nixosSystem` from nixpkgs flake. There is a + # recent change at nixpkgs (at 039f73f134546e59ec6f1b56b4aff5b81d889f64) + # that prevents setting our own custom functions so we'll have to + # evaluate the NixOS system ourselves. + nixosSystem = args: import "${nixpkgs}/nixos/lib/eval-config.nix" args; + in + (lib'.makeOverridable nixosSystem) { + specialArgs = { + foodogsquaredModulesPath = builtins.toString ../../nixos; + }; + lib = lib'; + modules = extraModules ++ [{ + nixpkgs.hostPlatform = lib.mkForce system; + }]; + + # Since we're setting it through nixpkgs.hostPlatform, we'll have to pass + # this as null. + system = null; + }; + + # A very very thin wrapper around `mkHost` to build with the given format. + mkImage = { system, nixpkgs-branch ? "nixpkgs", extraModules ? [ ], format ? "iso" }: + let + extraModules' = + extraModules ++ [ inputs.nixos-generators.nixosModules.${format} ]; + image = mkHost { + inherit nixpkgs-branch system; + extraModules = extraModules'; + }; + in + image.config.system.build.${image.config.formatAttr}; + + deployNodeType = { config, lib, ... }: { + freeformType = with lib.types; attrsOf anything; + + options = { + fastConnection = lib.mkEnableOption "deploy-rs to assume the target machine is considered fast"; + autoRollback = lib.mkEnableOption "deploy-rs auto-rollback feature"; + magicRollback = lib.mkEnableOption "deploy-rs magic rollback feature"; + remoteBuild = lib.mkEnableOption "pass the build process to the remote machine"; + profiles = lib.mkOption { + type = with lib.types; functionTo (attrsOf anything); + default = os: { + sshUser = "root"; + user = "admin"; + path = inputs.deploy.lib.${os.system}.activate.nixos os.config; + }; + defaultText = lib.literalExpression '' + os: { + sshUser = "root"; + user = "admin"; + path = inputs.deploy-rs.lib.''${os.system}.activate.nixos os.config; + } + ''; + description = '' + A set of profiles for the resulting deploy node. + + Since each config can result in more than one NixOS system, it has to + be a function where the passed argument is an attribute set with the + following values: + + * `name` is the attribute name from `configs`. + * `config` is the NixOS configuration itself. + * `system` is a string indicating the platform of the NixOS system. + + If unset, it will create a deploy-rs node profile called `system` + similar to those from nixops. + ''; + }; + }; + }; + + configType = { config, name, lib, ... }: { + options = { + systems = lib.mkOption { + type = with lib.types; listOf str; + default = lib.lists.take 1 config.systems; + defaultText = "The first system listed from `config.systems`."; + example = [ "x86_64-linux" "aarch64-linux" ]; + description = '' + A list of platforms that the NixOS configuration is supposed to be + deployed on. + ''; + }; + + formats = lib.mkOption { + type = with lib.types; nullOr (listOf str); + default = [ "iso" ]; + description = '' + The image formats to be generated from nixos-generators. When given + as `null`, it is listed as part of `nixosConfigurations` and excluded + from `images` flake output which is often the case for desktop NixOS + systems. + ''; + }; + + modules = lib.mkOption { + type = with lib.types; listOf raw; + default = []; + description = '' + A list of NixOS modules specific for that host. + ''; + }; + + overlays = lib.mkOption { + type = with lib.types; listOf (functionTo raw); + default = []; + example = lib.literalExpression '' + [ + inputs.neovim-nightly-overlay.overlays.default + inputs.emacs-overlay.overlays.default + ] + ''; + description = '' + A list of overlays to be applied for that host. + ''; + }; + + hostname = lib.mkOption { + type = lib.types.nonEmptyStr; + default = name; + example = "MyWhatNow"; + description = "The hostname of the NixOS configuration."; + }; + + domain = lib.mkOption { + type = with lib.types; nullOr nonEmptyStr; + default = null; + example = "work.example.com"; + description = "The domain of the NixOS system."; + }; + + nixpkgs-branch = lib.mkOption { + type = lib.types.str; + default = "nixpkgs"; + description = '' + The nixpkgs branch to be used for evaluating the NixOS configuration. + By default, it will use the `nixpkgs` flake input. + + ::: {.note} + This is based from your flake inputs and not somewhere else. If you + want to have support for multiple nixpkgs branch, simply add them as + a flake input. + ::: + ''; + example = "nixos-unstable-small"; + }; + + home-manager-branch = lib.mkOption { + type = lib.types.str; + default = "home-manager"; + example = "home-manager-stable"; + description = '' + The home-manager branch to be used for the NixOS module. By default, + it will use the `home-manager` flake input. + ''; + }; + + deploy = lib.mkOption { + type = with lib.types; nullOr (submodule deployNodeType); + default = null; + description = '' + deploy-rs node settings for the resulting NixOS configuration. + ''; + example = { + hostname = "work1.example.com"; + fastConnection = true; + autoRollback = true; + magicRollback = true; + remoteBuild = true; + }; + }; + }; + + config = { + modules = [ + inputs.${config.home-manager-branch}.nixosModules.home-manager + ../../../configs/nixos/${name} + + { + nixpkgs.overlays = config.overlays; + networking.hostName = lib.mkDefault config.hostname; + } + + (lib.mkIf (config.domain != null) { + networking.domain = lib.mkForce config.domain; + }) + ]; + }; + }; +in +{ + options.setups.nixos = { + sharedModules = lib.mkOption { + type = with lib.types; listOf raw; + default = []; + description = '' + A list of modules to be shared by all of the declarative NixOS setups. + ''; + }; + + configs = lib.mkOption { + type = with lib.types; attrsOf (submodule configType); + default = {}; + description = '' + An attribute set of metadata for the declarative NixOS setups. This + will then be used for related flake outputs such as + `nixosConfigurations` and `images`. + + ::: {.note} + For `nixosConfigurations` output, each of them is a pure NixOS + configuration where `nixpkgs.hostPlatform` is set and each of the + config is renamed into `$CONFIGNAME-$SYSTEM` if the host is configured + to have more than one system. + ::: + ''; + example = lib.literalExpression '' + { + desktop = { + systems = [ "x86_64-linux" "aarch64-linux" ]; + formats = null; + modules = [ + inputs.nur.nixosModules.nur + ]; + overlays = [ + # Neovim nightly! + inputs.neovim-nightly-overlay.overlays.default + + # Emacs unstable version! + inputs.emacs-overlay.overlays.default + + # Helix master! + inputs.helix-editor.overlays.default + + # Access to NUR. + inputs.nur.overlay + ]; + }; + + server = { + systems = [ "x86_64-linux" "aarch64-linux" ]; + domain = "work.example.com"; + formats = [ "do" "linode" ]; + nixpkgs-branch = "nixos-unstable-small"; + deploy = { + }; + }; + + vm = { + systems = [ "x86_64-linux" "aarch64-linux" ]; + formats = [ "vm" ]; + }; + } + ''; + }; + }; + + config = lib.mkIf (cfg.configs != {}) { + setups.nixos.sharedModules = [ + { + home-manager.useUserPackages = lib.mkDefault true; + home-manager.useGlobalPkgs = lib.mkDefault true; + home-manager.sharedModules = config.setups.home-manager.sharedModules; + } + ]; + + flake = + let + # A quick data structure we can pass through multiple build pipelines. + pureNixosConfigs = + let + validConfigs = + lib.filterAttrs (_: v: v.formats == null || v.deploy != null) cfg.configs; + + generatePureConfigs = hostname: metadata: + lib.listToAttrs + (builtins.map + (system: + lib.nameValuePair system (mkHost { + nixpkgs-branch = metadata.nixpkgs-branch; + extraModules = cfg.sharedModules ++ metadata.modules; + inherit system; + }) + ) + metadata.systems); + in + lib.mapAttrs + (hostname: metadata: + generatePureConfigs hostname metadata) + validConfigs; + in + { + nixosConfigurations = + lib.concatMapAttrs + (name: configs: + lib.mapAttrs' + (system: config: lib.nameValuePair "${name}-${system}" config) + configs) + pureNixosConfigs; + + deploy.nodes = + let + validConfigs = + lib.filterAttrs + (name: _: cfg.configs.${name}.deploy != null) + pureNixosConfigs; + in + lib.concatMapAttrs + (name: configs: + lib.mapAttrs' + (system: config: lib.nameValuePair "nixos-${name}-${system}" + (cfg.configs.${name}.deploy.profiles { inherit name config system; })) + configs) + validConfigs; + }; + + perSystem = { system, lib, ... }: { + images = + let + validImages = lib.filterAttrs + (host: metadata: + metadata.formats != null && (lib.elem system metadata.systems)) + cfg.configs; + in + lib.mapAttrs' + (host: metadata: + let + name = metadata.hostname or host; + nixpkgs-channel = metadata.nixpkgs-channel or "nixpkgs"; + in + lib.nameValuePair name (mkImage { + inherit (metadata) format; + inherit nixpkgs-channel system; + extraModules = cfg.sharedModules ++ metadata.modules; + })) + validImages; + }; + }; +} diff --git a/setups/home-manager.nix b/setups/home-manager.nix deleted file mode 100644 index 1c9adad4..00000000 --- a/setups/home-manager.nix +++ /dev/null @@ -1,59 +0,0 @@ -/* - This is project data for deploying home-manager users with this flake. Each - of the users defined here should correspond to one of the home-manager users - at `./configs/home-manager/`. - - Schema: - - * systems - A list of host platforms to be deployed. When given no systems, it will - be deployed with `x86_64-linux`. - * modules - A list of home-manager modules to be included. Take note there is a - baseline configuration defined at `flake.nix`. You should add modules - very minimally here such as additional overlays, modules, and so forth. - * home-manager-channel - The home-manager branch to be used. By default, it uses the - `home-manager` flake input which follows the `home-manager-unstable` - input. - * nixpkgs-channel - The nixpkgs branch to be included. By default, it uses the `nixpkgs` - flake input which follows the `nixos-unstable` input. - * deploy - An attribute set of options for deploy-rs nodes. - * username - The username of the home-manager user. By default, it will use the - attribute name. - * home-directory - The home directory of the home-manager user. By default, it is - set to "/home/${username}". -*/ - -{ lib, inputs }: - -{ - foo-dogsquared = { - systems = [ "aarch64-linux" "x86_64-linux" ]; - modules = [ - inputs.nur.hmModules.nur - - ({ config, ... }: { - nixpkgs.overlays = [ - # Neovim nightly! - inputs.neovim-nightly-overlay.overlays.default - - # Emacs unstable version! - inputs.emacs-overlay.overlays.default - - # Helix master! - inputs.helix-editor.overlays.default - - # Get all of the NUR. - inputs.nur.overlay - ]; - }) - ]; - }; - - plover.systems = [ "x86_64-linux" ]; -} diff --git a/setups/nixos.nix b/setups/nixos.nix deleted file mode 100644 index 2d429e41..00000000 --- a/setups/nixos.nix +++ /dev/null @@ -1,111 +0,0 @@ -/* - This is a custom data for this project where it lists the images found in - this flake. This can range from NixOS configurations intended to be deployed - for servers and desktops to installers. - - The data is then used for the image creation functions found in `flake.nix`. - Each of the entry should correspond to one of the hosts in `./configs/nixos/` - directory. - - Schema: - - * systems - A list of systems that is expected to be deployed. This is required and - will have a default list of "x86_64-linux" when no system is given. - * format - The image format to be generated from nixos-generators. You can give it - as `null` when not intended to be listed as part of the images which is - often the case for desktop NixOS systems. - * modules - A list of extra NixOS modules to be passed. You'll want to see the - baseline host configuration defined in `flake.nix`. - * deploy - An attribute set of arguments similar to the `deploy` CLI. When this - attribute is present, it will be assumed as part of NixOS configurations - even with `format = null` which is often the case for bare metal NixOS - servers that also has a suitable image format for deployment. - * hostname - The hostname of the host. By default, it assumes the hostname being given - from the attribute name. - * domain - The domain set for the host. Normally given to server systems. -*/ -{ lib, inputs }: - -{ - # The main desktop. - ni = { - systems = [ "x86_64-linux" ]; - format = null; - modules = [ - inputs.nur.nixosModules.nur - - ({ config, ... }: { - nixpkgs.overlays = [ - # Neovim nightly! - inputs.neovim-nightly-overlay.overlays.default - - # Emacs unstable version! - inputs.emacs-overlay.overlays.default - - # Helix master! - inputs.helix-editor.overlays.default - - # Access to NUR. - inputs.nur.overlay - ]; - }) - ]; - }; - - # A remote server. - plover = { - systems = [ "x86_64-linux" ]; - format = null; - domain = "foodogsquared.one"; - deploy = { - hostname = "plover.foodogsquared.one"; - auto-rollback = true; - magic-rollback = true; - }; - }; - - # TODO: Remove extra newlines that are here for whatever reason. - #{{{ - void = { - systems = [ "x86_64-linux" ]; - format = "vm"; - }; - #}}} - - # The barely customized non-graphical installer. - bootstrap = { - systems = [ "aarch64-linux" "x86_64-linux" ]; - format = "install-iso"; - nixpkgs-channel = "nixos-unstable-small"; - }; - - # The barely customized graphical installer. - graphical-installer = { - systems = [ "aarch64-linux" "x86_64-linux" ]; - format = "install-iso"; - }; - - # The WSL system (that is yet to be used). - winnowing = { - systems = [ "x86_64-linux" ]; - format = null; - modules = [ - # Well, well, well... - inputs.nixos-wsl.nixosModules.default - - ({ config, ... }: { - nixpkgs.overlays = [ - # Make the most of it. - inputs.neovim-nightly-overlay.overlays.default - ]; - }) - ]; - }; -} -# vim:foldmethod=marker