From 5c5f3fb65e847a7452fcffac8ee3e8f66bec61d6 Mon Sep 17 00:00:00 2001 From: _cry64 Date: Thu, 12 Feb 2026 11:04:02 +1000 Subject: [PATCH] major refactoring argument self is now provided via recursion a naive implementation of host groups is added --- cerulean/nexus/nexus.nix | 275 +++++++++++++++++++++++++-------------- cerulean/nexus/nodes.nix | 3 +- flake.nix | 10 +- 3 files changed, 183 insertions(+), 105 deletions(-) diff --git a/cerulean/nexus/nexus.nix b/cerulean/nexus/nexus.nix index 29c7ad4..e322a57 100644 --- a/cerulean/nexus/nexus.nix +++ b/cerulean/nexus/nexus.nix @@ -23,8 +23,12 @@ }: let inherit (builtins) + attrNames + concatStringsSep elem + getAttr isAttrs + isFunction mapAttrs pathExists typeOf @@ -37,7 +41,7 @@ templateNexus = let inherit - (nt.types) + (nt.naive.terminal) Terminal ; @@ -47,112 +51,185 @@ Ensure `nexus.${path}` exists under your call to `cerulean.mkNexus`. ''); in { - groups = missing "an list of all valid node group names." "groups"; + groups = Terminal {}; overlays = []; nodes = Terminal {}; }; + parseGroups = groups: let + validGroup = g: + isAttrs g + || throw '' + Cerulean Nexus groups must be provided as attribute sets, got "${typeOf g}" instead! + Ensure all the `groups` definitions are attribute sets under your call to `cerulean.mkNexus`. + ''; + delegate = parent: g: + g + |> mapAttrs (name: value: + assert validGroup value; + (delegate g value) + // { + _name = name; + _parent = parent; + }); + in + assert validGroup groups; + delegate null groups; + parseNexus = nexus: - if ! isAttrs nexus - then - abort '' - Cerulean Nexus config must be provided as an attribute set, got "${typeOf nexus}" instead! - Ensure all the `nexus` declaration is an attribute set under your call to `cerulean.mkNexus`. - '' - else nt.projectOnto templateNexus nexus; + assert isAttrs nexus + || abort '' + Cerulean Nexus config must be provided as an attribute set, got "${typeOf nexus}" instead! + Ensure all the `nexus` declaration is an attribute set under your call to `cerulean.mkNexus`. + ''; let + base = nt.projectOnto templateNexus nexus; + in + # XXX: TODO: create a different version of nt.projectOnto that can actually + # XXX: TODO: handle applying a transformation to the result of each datapoint + base + // { + groups = parseGroups base.groups; + }; - mkNexus' = root: nexus': let - nexus = parseNexus nexus'; - in rec { - nixosConfigurations = mapNodes nexus.nodes ( - nodeName: node: - lib.nixosSystem { - system = node.system; - modules = let - host' = root + "/hosts/${nodeName}"; - host = - if pathExists host' - then host' - else host' + ".nix"; - in - [../nixos-module host] ++ node.extraModules; - - # nix passes these to every single module - specialArgs = let - pkgConfig = - { - inherit (node) system; - # XXX: WARNING: TODO: i've stopped caring - # XXX: WARNING: TODO: just figure out a better solution to pkgConfig - config.allowUnfree = true; - overlays = self.overlays ++ nexus.overlays ++ node.overlays; - } - // node.extraPkgConfig; - in - node.specialArgs - // { - pkgs = import nixpkgs pkgConfig; - upkgs = import nixpkgs-unstable pkgConfig; - }; - } + parseDecl = outputsBuilder: let + decl = ( + if isFunction outputsBuilder + then outputsBuilder final # provide `self` + else + assert (isAttrs outputsBuilder) + || abort '' + Cerulean declaration must be provided as an attribute set, got "${typeOf outputsBuilder}" instead! + Ensure your declaration is an attribute set or function under your call to `cerulean.mkNexus`. + ''; outputsBuilder ); - deploy.nodes = mapNodes nexus.nodes (nodeName: node: let - inherit - (node.deploy) - activationTimeout - autoRollback - confirmTimeout - interactiveSudo - magicRollback - remoteBuild - ssh - sudo - user - ; - - nixosFor = system: deploy-rs.lib.${system}.activate.nixos; - in { - hostname = ssh.host; - - profilesOrder = ["default"]; # profiles priority - profiles.default = { - path = nixosFor node.system nixosConfigurations.${nodeName}; - - user = user; - sudo = sudo; - interactiveSudo = interactiveSudo; - - fastConnection = false; - - autoRollback = autoRollback; - magicRollback = magicRollback; - activationTimeout = activationTimeout; - confirmTimeout = confirmTimeout; - - remoteBuild = remoteBuild; - sshUser = ssh.user; - sshOpts = - ssh.opts - ++ ( - if elem "-p" ssh.opts - then [] - else ["-p" (toString ssh.port)] - ) - ++ ( - if elem "-A" ssh.opts - then [] - else ["-A"] - ); + final = + decl + // { + nexus = parseNexus (decl.nexus or {}); }; - }); - - checks = mapAttrs (system: deployLib: deployLib.deployChecks deploy) deploy-rs.lib; - }; -in { - mkNexus = root: outputs': let - autogen = mkNexus' root (outputs'.nexus or {}); - outputs = removeAttrs outputs' ["nexus"]; in - autogen // outputs; # XXX: TODO: replace this with a deep merge + final; + + # XXX: TODO: create a function in NixTypes that handles this instead + findImport = path: + if pathExists path + then path + else path + ".nix"; +in { + mkNexus = root: outputsBuilder: let + decl = parseDecl outputsBuilder; + + inherit + (decl) + nexus + ; + customOutputs = removeAttrs decl ["nexus"]; + + outputs = rec { + nixosConfigurations = mapNodes nexus.nodes ( + nodeName: node: + lib.nixosSystem { + system = node.system; + modules = let + host = findImport (root + "/hosts/${nodeName}"); + # XXX: TODO: don't use a naive type for this (ie _name property) + # XXX: TODO: i really need NixTypes to be stable and use that instead + groups = + node.groups + |> map (group: + assert group ? _name + || throw (let + got = + if ! isAttrs group + then toString group + else + group + |> attrNames + |> map (name: "${name} = <${typeOf (getAttr name group)}>;") + |> concatStringsSep " " + |> (x: "{ ${x} }"); + in '' + Cerulean Nexus node "${nodeName}" is a member of a nonexistent group. + Got "${got}" of primitive type "${typeOf group}". + NOTE: Groups can be accessed via `self.groups.PATH.TO.YOUR.GROUP` + ''); + findImport (root + "/groups/${group._name}")); + in + [../nixos-module host] ++ groups ++ node.extraModules; + + # nix passes these to every single module + specialArgs = let + pkgConfig = + { + inherit (node) system; + # XXX: WARNING: TODO: i've stopped caring + # XXX: WARNING: TODO: just figure out a better solution to pkgConfig + config.allowUnfree = true; + overlays = self.overlays ++ nexus.overlays ++ node.overlays; + } + // node.extraPkgConfig; + in + node.specialArgs + // { + pkgs = import nixpkgs pkgConfig; + upkgs = import nixpkgs-unstable pkgConfig; + }; + } + ); + + deploy.nodes = mapNodes nexus.nodes (nodeName: node: let + inherit + (node.deploy) + activationTimeout + autoRollback + confirmTimeout + interactiveSudo + magicRollback + remoteBuild + ssh + sudo + user + ; + + nixosFor = system: deploy-rs.lib.${system}.activate.nixos; + in { + hostname = ssh.host; + + profilesOrder = ["default"]; # profiles priority + profiles.default = { + path = nixosFor node.system nixosConfigurations.${nodeName}; + + user = user; + sudo = sudo; + interactiveSudo = interactiveSudo; + + fastConnection = false; + + autoRollback = autoRollback; + magicRollback = magicRollback; + activationTimeout = activationTimeout; + confirmTimeout = confirmTimeout; + + remoteBuild = remoteBuild; + sshUser = ssh.user; + sshOpts = + ssh.opts + ++ ( + if elem "-p" ssh.opts + then [] + else ["-p" (toString ssh.port)] + ) + ++ ( + if elem "-A" ssh.opts + then [] + else ["-A"] + ); + }; + }); + + checks = mapAttrs (system: deployLib: deployLib.deployChecks deploy) deploy-rs.lib; + }; + in + outputs // customOutputs; } diff --git a/cerulean/nexus/nodes.nix b/cerulean/nexus/nodes.nix index b5432de..25a1d02 100644 --- a/cerulean/nexus/nodes.nix +++ b/cerulean/nexus/nodes.nix @@ -22,11 +22,12 @@ in rec { # abstract node instance that stores all default values templateNode = name: system: let inherit - (nt.types) + (nt.naive.terminal) Terminal ; in { system = "x86_64-linux"; # sane default (i hope...) + groups = []; extraModules = []; specialArgs = Terminal {}; overlays = []; diff --git a/flake.nix b/flake.nix index 99d7f68..94f4b33 100644 --- a/flake.nix +++ b/flake.nix @@ -31,9 +31,9 @@ ... } @ inputs: import ./cerulean - <| inputs - // { - inherit (nixpkgs) lib; - inherit (nt) mix; - }; + (inputs + // { + inherit (nixpkgs) lib; + inherit (nt) mix; + }); }