refactor(flake)!: use the module system for configurations' meta.nix

- Move over `auto.confirmations.confirmations`' default value to a
  separate module (clearing out the associated `TODO`)
- Create a module type for the `meta` of each `configurationType`,
  which streamlines the definition and checking thereof
- Pass `meta` instead of just `host` to `predicate`, `mkHost` and
  `mkDeployNode`
- Move the `host != "__template__"` logic inside the default value for
  `meta`'s `enable` option, leaving it (`enable`) as the main filtering
  factor in the `configurations`' `predicate`s
- Combine `auto.configurations.result{Configurations,DeployNodes}` into
          `auto.configurations.result.{configurations,deployNodes}`
This commit is contained in:
reo101 2024-09-05 23:37:49 +03:00
parent 9c709598d1
commit 137cab1aed
Signed by: reo101
GPG key ID: 675AA7EF13964ACB
3 changed files with 384 additions and 277 deletions

View file

@ -0,0 +1,215 @@
{ lib, config, self, inputs, withSystem, ... }:
let
inherit (config.lib)
and
hasFiles
hasDirectories
configuration-type-to-outputs-modules;
in
let
# `pkgs` with flake's overlays
# NOTE: done here to avoid infinite recursion
pkgs' = system:
(withSystem system ({ pkgs, ... }: pkgs)).extend
(final: prev: inputs.self.packages.${system});
genUsers = configurationFiles:
lib.pipe configurationFiles [
(cf: cf."home" or { })
builtins.attrNames
(builtins.map
(lib.strings.removeSuffix ".nix"))
];
homeManagerModule = { root, meta, users ? null }: {
home-manager = {
# Use same `pkgs` instance as system (i.e. carry over overlays)
useGlobalPkgs = true;
# Do not keep packages in ${HOME}
useUserPackages = true;
# Default import all of our exported `home-manager` modules
sharedModules = builtins.attrValues config.flake.${configuration-type-to-outputs-modules "home-manager"};
# Pass in `inputs`, `hostname` and `meta`
extraSpecialArgs = {
inherit inputs;
inherit meta;
};
} // (if users == null then {
# nixOnDroid
config = "${root}/home.nix";
} else {
# Not nixOnDroid
users =
lib.attrsets.genAttrs
users
(user: import "${root}/home/${user}.nix");
});
};
mkNixosHost = args @ { root, meta, users }: inputs.nixpkgs.lib.nixosSystem {
inherit (meta) system;
pkgs = pkgs' meta.system;
modules = [
# Main configuration
"${root}/configuration.nix"
# Home Manager
inputs.home-manager.nixosModules.home-manager
(homeManagerModule args)
# (r)agenix && agenix-rekey
inputs.ragenix.nixosModules.default
inputs.agenix-rekey.nixosModules.default
(lib.optionalAttrs (meta ? pubkey) {
age.rekey.hostPubkey = meta.pubkey;
})
# nix-topology
inputs.nix-topology.nixosModules.default
# Sane default `networking.hostName`
{
networking.hostName = lib.mkDefault meta.hostname;
}
# TODO: lib.optionals
] ++ (builtins.attrValues config.flake.${configuration-type-to-outputs-modules "nixos"});
specialArgs = {
inherit inputs;
inherit meta;
};
};
mkNixOnDroidHost = args @ { root, meta }: inputs.nix-on-droid.lib.nixOnDroidConfiguration {
# NOTE: inferred by `pkgs.system`
# inherit system;
pkgs = pkgs' meta.system;
modules = [
# Main configuration
"${root}/configuration.nix"
# Home Manager
(homeManagerModule args)
] ++ (builtins.attrValues config.flake.${configuration-type-to-outputs-modules "nix-on-droid"});
extraSpecialArgs = {
inherit inputs;
inherit meta;
};
home-manager-path = inputs.home-manager.outPath;
};
mkNixDarwinHost = args @ { root, meta, users }: inputs.nix-darwin.lib.darwinSystem {
inherit (meta) system;
pkgs = pkgs' meta.system;
modules = [
# Main configuration
"${root}/configuration.nix"
# Home Manager
inputs.home-manager.darwinModules.home-manager
(homeManagerModule args)
] ++ (builtins.attrValues config.flake.${configuration-type-to-outputs-modules "nix-darwin"});
specialArgs = {
inherit inputs;
inherit meta;
};
};
mkHomeManagerHost = args @ { root, meta }: inputs.home-manager.lib.homeManagerConfiguration {
inherit (meta) system;
pkgs = pkgs' meta.system;
modules = [
"${root}/home.nix"
] ++ (builtins.attrValues config.flake.${configuration-type-to-outputs-modules "home-manager"});
extraSpecialArgs = {
inherit inputs;
inherit meta;
};
};
in
{
auto.configurations.configurationTypes = lib.mkDefault {
nixos = {
predicate = ({ root, meta, configurationFiles, ... }:
and [
meta.enable
(hasFiles
[ "configuration.nix" ]
configurationFiles)
]);
mkHost = ({ root, meta, configurationFiles, ... }:
mkNixosHost {
inherit root;
inherit meta;
users = genUsers configurationFiles;
});
mkDeployNode = ({ root, meta, configuration }:
{
inherit (meta.deploy) hostname;
profiles.system = meta.deploy // {
path = inputs.deploy-rs.lib.${meta.system}.activate."nixos" configuration;
};
});
};
nix-on-droid = {
predicate = ({ root, meta, configurationFiles, ... }:
and [
meta.enable
(hasFiles
[ "configuration.nix" "home.nix" ]
configurationFiles)
]);
mkHost = ({ root, meta, configurationFiles, ... }:
mkNixOnDroidHost {
inherit root;
inherit meta;
});
};
nix-darwin = {
hostsName = "darwinHosts";
configurationsName = "darwinConfigurations";
predicate = ({ root, meta, configurationFiles, ... }:
and [
meta.enable
(hasFiles
[ "configuration.nix" ]
configurationFiles)
(hasDirectories
[ "home" ]
configurationFiles)
]);
mkHost = ({ root, meta, configurationFiles, ... }:
mkNixDarwinHost {
inherit root;
inherit meta;
users = genUsers configurationFiles;
});
mkDeployNode = ({ root, meta, configuration }:
{
inherit (meta.deploy) hostname;
profiles.system = meta.deploy // {
path = inputs.deploy-rs.lib.${meta.system}.activate."darwin" configuration;
};
});
};
home-manager = {
hostsName = "homeHosts";
configurationsName = "homeConfigurations";
predicate = ({ root, meta, configurationFiles, ... }:
and [
meta.enable
(hasFiles
[ "home.nix" ]
configurationFiles)
]);
mkHost = ({ root, meta, configurationFiles, ... }:
mkHomeManagerHost {
inherit root;
inherit meta;
});
};
};
}

View file

@ -9,136 +9,11 @@ let
kebabToCamel kebabToCamel
configuration-type-to-outputs-modules; configuration-type-to-outputs-modules;
in in
let
# Configuration helpers
# `pkgs` with flake's overlays
# NOTE: done here to avoid infinite recursion
pkgs' = system:
(withSystem system ({ pkgs, ... }: pkgs)).extend
(final: prev: inputs.self.packages.${system});
genUsers = configurationFiles:
lib.pipe configurationFiles [
(cf: cf."home" or { })
builtins.attrNames
(builtins.map
(lib.strings.removeSuffix ".nix"))
];
homeManagerModule = { root, meta, users ? null }: {
home-manager = {
# Use same `pkgs` instance as system (i.e. carry over overlays)
useGlobalPkgs = true;
# Do not keep packages in ${HOME}
useUserPackages = true;
# Default import all of our exported `home-manager` modules
sharedModules = builtins.attrValues config.flake.${configuration-type-to-outputs-modules "home-manager"};
# Pass in `inputs`, `hostname` and `meta`
extraSpecialArgs = {
inherit inputs;
inherit meta;
};
} // (if users == null then {
# nixOnDroid
config = "${root}/home.nix";
} else {
# Not nixOnDroid
users =
lib.attrsets.genAttrs
users
(user: import "${root}/home/${user}.nix");
});
};
mkNixosHost = args @ { root, meta, users }: inputs.nixpkgs.lib.nixosSystem {
inherit (meta) system;
pkgs = pkgs' meta.system;
modules = [
# Main configuration
"${root}/configuration.nix"
# Home Manager
inputs.home-manager.nixosModules.home-manager
(homeManagerModule args)
# (r)agenix && agenix-rekey
inputs.ragenix.nixosModules.default
inputs.agenix-rekey.nixosModules.default
(lib.optionalAttrs (meta ? pubkey) {
age.rekey.hostPubkey = meta.pubkey;
})
# nix-topology
inputs.nix-topology.nixosModules.default
# Sane default `networking.hostName`
{
networking.hostName = lib.mkDefault meta.hostname;
}
# TODO: lib.optionals
] ++ (builtins.attrValues config.flake.${configuration-type-to-outputs-modules "nixos"});
specialArgs = {
inherit inputs;
inherit meta;
};
};
mkNixOnDroidHost = args @ { root, meta }: inputs.nix-on-droid.lib.nixOnDroidConfiguration {
# NOTE: inferred by `pkgs.system`
# inherit system;
pkgs = pkgs' meta.system;
modules = [
# Main configuration
"${root}/configuration.nix"
# Home Manager
(homeManagerModule args)
] ++ (builtins.attrValues config.flake.${configuration-type-to-outputs-modules "nix-on-droid"});
extraSpecialArgs = {
inherit inputs;
inherit meta;
};
home-manager-path = inputs.home-manager.outPath;
};
mkNixDarwinHost = args @ { root, meta, users }: inputs.nix-darwin.lib.darwinSystem {
inherit (meta) system;
pkgs = pkgs' meta.system;
modules = [
# Main configuration
"${root}/configuration.nix"
# Home Manager
inputs.home-manager.darwinModules.home-manager
(homeManagerModule args)
# # Set `nixpkgs.hostPlatform`
# {
# nixpkgs.hostPlatform = system;
# }
] ++ (builtins.attrValues config.flake.${configuration-type-to-outputs-modules "nix-darwin"});
specialArgs = {
inherit inputs;
inherit meta;
};
};
mkHomeManagerHost = args @ { root, meta }: inputs.home-manager.lib.homeManagerConfiguration {
inherit (meta) system;
pkgs = pkgs' meta.system;
modules = [
"${root}/home.nix"
] ++ (builtins.attrValues config.flake.${configuration-type-to-outputs-modules "home-manager"});
extraSpecialArgs = {
inherit inputs;
inherit meta;
};
};
in
{ {
imports = [
./default-generators.nix
];
options = let options = let
inherit (lib) types; inherit (lib) types;
in { in {
@ -253,13 +128,17 @@ in
default = null; default = null;
# TODO: update # TODO: update
example = /* nix */ '' example = /* nix */ ''
args @ { root, host, meta, configuration }: { root, host, meta, configuration }: {
inputs.deploy-rs.''${meta.system}.activate.nixos configuration; inherit (meta.deploy) hostname;
profiles.system = meta.deploy // {
path = inputs.deploy-rs.lib.''${meta.system}.activate."nixos" configuration;
};
}
''; '';
}; };
resultConfigurations = lib.mkOption { result = lib.mkOption {
description = '' description = ''
The resulting automatic configurations The resulting automatic host && deploy-rs configurations
''; '';
# TODO: specify # TODO: specify
type = types.unspecified; type = types.unspecified;
@ -272,14 +151,28 @@ in
let let
root = "${dir}/${host}"; root = "${dir}/${host}";
meta-path = "${root}/meta.nix"; meta-path = "${root}/meta.nix";
meta = import meta-path; meta = (lib.evalModules {
deploy-config = meta.deploy or null; class = "meta";
modules = [
(if builtins.pathExists meta-path
then import meta-path
else {})
./meta-module.nix
{
config = {
enable = lib.mkDefault (host != "__template__");
hostname = lib.mkDefault host;
};
}
];
}).config;
deploy-config = meta.deploy;
has-mkDeployNode = mkDeployNode != null; has-mkDeployNode = mkDeployNode != null;
has-deploy-config = builtins.pathExists meta-path && deploy-config != null; has-deploy-config = deploy-config != null;
configuration-args = { inherit root host configurationFiles; }; configuration-args = { inherit root meta configurationFiles; };
valid = predicate configuration-args; valid = predicate configuration-args;
configuration = mkHost configuration-args; configuration = mkHost configuration-args;
deploy-args = { inherit root host meta configuration; }; deploy-args = { inherit root meta configuration; };
deploy = mkDeployNode deploy-args; deploy = mkDeployNode deploy-args;
in in
lib.optionalAttrs valid { lib.optionalAttrs valid {
@ -292,146 +185,37 @@ in
]; ];
}; };
}; };
config = {};
})); }));
# TODO: put in a more visible place default = {};
};
result = lib.mkOption {
readOnly = true;
default = { default = {
nixos = { configurations =
predicate = ({ root, host, configurationFiles, ... }: let lib.pipe configurationTypes [
meta = import "${root}/meta.nix"; (lib.mapAttrs'
in (configurationType: configurationTypeConfig:
and [ lib.nameValuePair
(! (host == "__template__")) configurationTypeConfig.configurationsName
(hasFiles (lib.mapAttrs
[ "configuration.nix" "meta.nix" ] (host: { configuration, ... }:
configurationFiles) configuration)
(meta.enable or true) configurationTypeConfig.result)))
]); ];
mkHost = ({ root, host, configurationFiles, ... }: let deployNodes =
meta = import "${root}/meta.nix" // { lib.pipe configurationTypes [
hostname = host;
};
in
mkNixosHost {
inherit root;
inherit meta;
users = genUsers configurationFiles;
});
mkDeployNode = ({ root, host, meta, configuration }:
{
inherit (meta.deploy) hostname;
profiles.system = meta.deploy // {
path = inputs.deploy-rs.lib.${meta.system}.activate."nixos" configuration;
};
});
};
nix-on-droid = {
predicate = ({ root, host, configurationFiles, ... }: let
meta = import "${root}/meta.nix";
in
and [
(! (host == "__template__"))
(hasFiles
[ "configuration.nix" "home.nix" "meta.nix" ]
configurationFiles)
(meta.enable or true)
]);
mkHost = ({ root, host, configurationFiles, ... }: let
meta = import "${root}/meta.nix" // {
hostname = host;
};
in
mkNixOnDroidHost {
inherit root;
inherit meta;
});
};
nix-darwin = {
hostsName = "darwinHosts";
configurationsName = "darwinConfigurations";
predicate = ({ root, host, configurationFiles, ... }: let
meta = import "${root}/meta.nix";
in
and [
(! (host == "__template__"))
(hasFiles
[ "configuration.nix" "meta.nix" ]
configurationFiles)
(hasDirectories
[ "home" ]
configurationFiles)
(meta.enable or true)
]);
mkHost = ({ root, host, configurationFiles, ... }: let
meta = import "${root}/meta.nix" // {
hostname = host;
};
in
mkNixDarwinHost {
inherit root;
inherit meta;
users = genUsers configurationFiles;
});
mkDeployNode = ({ root, host, meta, configuration }:
{
inherit (meta.deploy) hostname;
profiles.system = meta.deploy // {
path = inputs.deploy-rs.lib.${meta.system}.activate."darwin" configuration;
};
});
};
home-manager = {
hostsName = "homeHosts";
configurationsName = "homeConfigurations";
predicate = ({ root, host, configurationFiles, ... }: let
meta = import "${root}/meta.nix";
in
and [
(! (host == "__template__"))
(hasFiles
[ "home.nix" "meta.nix" ]
configurationFiles)
(meta.enable or true)
]);
mkHost = ({ root, host, configurationFiles, ... }: let
meta = import "${root}/meta.nix" // {
hostname = host;
};
in
mkHomeManagerHost {
inherit root;
inherit meta;
});
};
};
};
resultConfigurations = lib.mkOption {
readOnly = true;
default = lib.pipe configurationTypes [
(lib.mapAttrs'
(configurationType: configurationTypeConfig:
lib.nameValuePair
configurationTypeConfig.configurationsName
(lib.mapAttrs
(host: { configuration, ... }:
configuration)
configurationTypeConfig.resultConfigurations)))
];
};
resultDeployNodes = lib.mkOption {
readOnly = true;
default = lib.pipe configurationTypes [
(lib.concatMapAttrs
(configurationType: configurationTypeConfig:
(lib.concatMapAttrs (lib.concatMapAttrs
(host: { deploy ? null, ... }: (configurationType: configurationTypeConfig:
lib.optionalAttrs (lib.concatMapAttrs
(deploy != null) (host: { deploy ? null, ... }:
{ lib.optionalAttrs
${host} = deploy; (deploy != null)
}) {
configurationTypeConfig.resultConfigurations))) ${host} = deploy;
]; })
configurationTypeConfig.result)))
];
};
}; };
}; };
}); });
@ -441,9 +225,9 @@ in
config = { config = {
flake = let flake = let
configurations = config.auto.configurations.resultConfigurations; configurations = config.auto.configurations.result.configurations;
deployNodes = { deployNodes = {
deploy.nodes = config.auto.configurations.resultDeployNodes; deploy.nodes = config.auto.configurations.result.deployNodes;
}; };
deployChecks = { deployChecks = {
checks = checks =

View file

@ -0,0 +1,108 @@
{ config, lib, ... }: let
inherit (lib) types;
in {
options = {
enable = lib.mkOption {
description = "Whether to enable this host's configuration";
type = types.bool;
default = true;
};
hostname = lib.mkOption {
description = "Hostname of the machine";
type = types.str;
# NOTE: set as a config-side default outside
# default = host;
};
system = lib.mkOption {
description = "The `system` of the host";
type = types.str;
};
pubkey = lib.mkOption {
description = "The host SSH key, used for encrypting agenix secrets";
type = types.nullOr types.str;
default = null;
};
deploy = lib.mkOption {
type = types.nullOr (types.submodule (deploySubmodule: {
options = {
hostname = lib.mkOption {
description = ''
This is the hostname by which you'll refer to this machine using reploy-rs
'';
type = types.str;
default = config.hostname;
};
sshUser = lib.mkOption {
description = ''
This is the user that deploy-rs will use when connecting.
This will default to your own username if not specified anywhere
'';
type = types.nullOr types.str;
default = null;
};
user = lib.mkOption {
description = ''
This is the user that the profile will be deployed to (will use sudo if not the same as above).
If `sshUser` is specified, this will be the default (though it will _not_ default to your own username)
'';
type = types.str;
default = "root";
};
sudo = lib.mkOption {
description = ''
Which sudo command to use. Must accept at least two arguments:
the user name to execute commands as and the rest is the command to execute
'';
type = types.str;
default = "sudo -u";
};
sshOpts = lib.mkOption {
description = ''
This is an optional list of arguments that will be passed to SSH.
'';
type = types.listOf types.str;
default = [];
};
fastConnection = lib.mkOption {
description = ''
Fast connection to the node. If this is true, copy the whole closure instead of letting the node substitute.
'';
type = types.bool;
default = false;
};
autoRollback = lib.mkOption {
description = ''
If the previous profile should be re-activated if activation fails.
'';
type = types.bool;
default = true;
};
magicRollback = lib.mkOption {
description = ''
See the earlier section about Magic Rollback for more information.
'';
type = types.bool;
default = true;
};
tempPath = lib.mkOption {
description = ''
The path which deploy-rs will use for temporary files, this is currently only used by `magicRollback` to create an inotify watcher in for confirmations
(if `magicRollback` is in use, this _must_ be writable by `user`)
'';
type = types.str;
default = "/tmp";
};
remoteBuild = lib.mkOption {
description = ''
Build the derivation on the target system
Will also fetch all external dependencies from the target system's substituters.
'';
type = types.bool;
default = false;
};
};
}));
default = null;
};
};
}