Compare commits

...

9 commits

Author SHA1 Message Date
087f679e67 add modifiable homeManager 2026-02-18 23:56:05 +10:00
d5211287bd TEMP: use base 2026-02-18 20:13:43 +10:00
18e1a2c890 don't add pkgs to _module.args 2026-02-18 20:13:36 +10:00
232837d34a update TODO 2026-02-18 20:13:05 +10:00
f34c2fba39 rename base -> decl (easily confusable) 2026-02-18 20:12:58 +10:00
870bbb1f37 i cried 2026-02-18 17:24:28 +10:00
0c5387fd61 nixpkgs.channels disregards contextName 2026-02-17 17:27:31 +10:00
d85a6b963f restruct args into specialArgs._cerulean 2026-02-17 17:10:43 +10:00
59d1028e19 add CHANGELOG.md 2026-02-17 12:41:53 +10:00
9 changed files with 190 additions and 142 deletions

25
CHANGELOG.md Normal file
View file

@ -0,0 +1,25 @@
# Changelog
## v0.2.0-alpha
Initial "stable" release. Cerulean is currently usable and supports:
1. local & remote deployment configuration
2. nixos/homemanager module-level support for any number of nixpkg branches
3. use of the [nix-systems standard](https://github.com/nix-systems/nix-systems), the introduction of the `snow/flake` standard, and the introduction of the `nixpkgs.nix` standard module.
4. hierarchical groups for NixOS hosts via `snow.nix`
This is still a alpha-build of Cerulean. Everything will break in the future as I change the internals a bunch. I'll aim to write documentation in future cause currently there's no guide.
## v0.2.1-alpha
Minor patches
- cerulean no longer has a `inputs.nixpkgs-unstable` (the `nixpkgs.nix` is the new alternative)
- `home-manager.nixosModules.default` and `microvm.nixosModules.microvm` are added as default modules
- fixed `groups.all` not being added to nodes with `groups = []`
## v0.2.2-alpha
Minor patches
- fixed `nexus.groups.all` not added to empty `nexus.nodes.*.groups` declarations
- fixed bad propagation of inputs
- forced system architecture to be specified per node
- cerulean no longer depends on `nixpkgs`, `base` package set should be set instead
- rename `extraModules` -> `modules`
- rename `specialArgs` -> `args`

10
TODO.md
View file

@ -1,3 +1,7 @@
- [ ] base should automatically be set as the default (dont do anything with the default)
- [ ] try to remove common foot guns, ie abort if the user provides the home-manager or microvm nixosModules
since cerulean ALREADY provides these
- [ ] deploy port should default to the first port given to `services.openssh` - [ ] deploy port should default to the first port given to `services.openssh`
- [ ] use the Nix module system instead of projectOnto for `cerulean.mkNexus` - [ ] use the Nix module system instead of projectOnto for `cerulean.mkNexus`
@ -23,8 +27,8 @@
- [ ] allow multiple privesc methods, the standard is pam_ssh_agent_auth - [ ] allow multiple privesc methods, the standard is pam_ssh_agent_auth
## Low Priority ## Low Priority
- [ ] rename extraModules to modules? - [X] rename extraModules to modules?
- [ ] rename specialArgs to args? - [X] rename specialArgs to args?
- [ ] make an extension to the nix module system (different to mix) - [ ] make an extension to the nix module system (different to mix)
that allows transformations (ie a stop post config, ie outputs, which that allows transformations (ie a stop post config, ie outputs, which
@ -40,6 +44,8 @@
- [ ] rewrite the ceru cli in rust - [ ] rewrite the ceru cli in rust
- [ ] make `ceru` do local and remote deployments - [ ] make `ceru` do local and remote deployments
- [ ] support `legacyImports`
```nix ```nix
# REF: foxora # REF: foxora
vms = { vms = {

View file

@ -20,6 +20,7 @@
}: let }: let
inherit inherit
(builtins) (builtins)
all
attrNames attrNames
concatLists concatLists
concatStringsSep concatStringsSep
@ -53,6 +54,7 @@
base = null; base = null;
modules = []; modules = [];
args = Terminal {}; args = Terminal {};
homeManager = null;
groups = Terminal {}; groups = Terminal {};
nodes = Terminal {}; nodes = Terminal {};
@ -92,13 +94,13 @@
Cerulean Nexus config must be provided as an attribute set, got "${typeOf nexus}" instead! Cerulean Nexus config must be provided as an attribute set, got "${typeOf nexus}" instead!
Ensure the `nexus` declaration is an attribute set under your call to `cerulean.mkNexus`. Ensure the `nexus` declaration is an attribute set under your call to `cerulean.mkNexus`.
''; let ''; let
base = nt.projectOnto templateNexus nexus; decl = nt.projectOnto templateNexus nexus;
in in
# XXX: TODO: create a different version of nt.projectOnto that can actually # XXX: TODO: create a different version of nt.projectOnto that can actually
# XXX: TODO: handle applying a transformation to the result of each datapoint # XXX: TODO: handle applying a transformation to the result of each datapoint
base decl
// { // {
groups = parseGroupDecl base.groups; groups = parseGroupDecl decl.groups;
}; };
parseDecl = outputsBuilder: let parseDecl = outputsBuilder: let
@ -184,29 +186,48 @@ in {
outputs = rec { outputs = rec {
nixosConfigurations = mapNodes nexus ( nixosConfigurations = mapNodes nexus (
{ {
base,
lib, lib,
nodeName, nodeName,
node, node,
... ...
}: let }: let
nixosDecl = lib.nixosSystem rec { nixosDecl = let
system = node.system; homeManager =
specialArgs = if node.homeManager != null
nexus.args then node.homeManager
// node.args else nexus.homeManager;
// {
inherit root specialArgs; userArgs = nexus.args // node.args;
ceruleanArgs = {
inherit root base;
inherit (node) system; inherit (node) system;
_deploy-rs = inputs.deploy-rs; _cerulean = {
inherit inputs userArgs ceruleanArgs homeManager;
specialArgs = userArgs // ceruleanArgs;
}; };
};
specialArgs = assert (userArgs
|> attrNames
|> all (argName:
! ceruleanArgs ? argName
|| abort ''
`specialArgs` are like super important to Cerulean my love... </3
But `args.${argName}` is a reserved argument name :(
''));
ceruleanArgs._cerulean.specialArgs;
in
lib.nixosSystem {
inherit (node) system;
inherit specialArgs;
modules = modules =
[ [
self.nixosModules.default self.nixosModules.default
(findImport (root + "/hosts/${nodeName}")) (findImport (root + "/hosts/${nodeName}"))
inputs.home-manager.nixosModules.default
# inputs.microvm.nixosModules.microvm # inputs.microvm.nixosModules.microvm
] ]
++ (homeManager.nixosModules.default or [])
++ (getGroupModules root nodeName node) ++ (getGroupModules root nodeName node)
++ node.modules ++ node.modules
++ nexus.modules; ++ nexus.modules;

View file

@ -38,6 +38,8 @@ in rec {
modules = []; modules = [];
args = Terminal {}; args = Terminal {};
homeManager = null;
base = null; base = null;
deploy = { deploy = {
@ -93,7 +95,7 @@ in rec {
''; '';
in in
f { f {
inherit nodeName node; inherit nodeName node base;
lib = base.lib; inherit (base) lib;
}); });
} }

View file

@ -14,19 +14,23 @@
{ {
root, root,
system, system,
_deploy-rs, _cerulean,
... ...
} @ args: { } @ args: {
imports = [ imports =
[
# user configuration # user configuration
(import (root + "/nixpkgs.nix")) (import (root + "/nixpkgs.nix"))
# options declarations # options declarations
(import ./nixpkgs.nix (args // {contextName = "hosts";})) (import ./nixpkgs.nix (args // {contextName = "hosts";}))
]
++ (
if _cerulean.homeManager != null
then [./home-manager.nix]
else []
);
./home-manager.nix environment.systemPackages = with _cerulean.inputs; [
]; deploy-rs.packages.${system}.default
environment.systemPackages = [
_deploy-rs.packages.${system}.default
]; ];
} }

View file

@ -13,10 +13,9 @@
# limitations under the License. # limitations under the License.
{ {
root, root,
system,
config, config,
lib, lib,
specialArgs, _cerulean,
... ...
} @ args: let } @ args: let
inherit inherit
@ -26,25 +25,21 @@
pathExists pathExists
; ;
in { in {
config = {
home-manager = { home-manager = {
users = users =
config.users.users config.users.users
|> attrNames |> attrNames
|> filter (x: pathExists (root + "/homes/${x}")) |> filter (x: pathExists (root + "/homes/${x}"))
|> (x: lib.genAttrs x (y: import (root + "/homes/${y}"))); |> (x:
lib.genAttrs x (y:
import (root + "/homes/${y}")));
extraSpecialArgs = {inherit root system;} // (specialArgs.inputs or {}); extraSpecialArgs = _cerulean.specialArgs;
sharedModules = [ sharedModules = [
# user configuration # user configuration
(import (root + "/nixpkgs.nix")) (import (root + "/nixpkgs.nix"))
# options declarations # options declarations
(import ./nixpkgs.nix (args // {contextName = "homes";})) (import ./nixpkgs.nix (args // {contextName = "homes";}))
]; ];
# disable home-manager trying anything fancy
# we control the pkgs now!!
# useGlobalPkgs = true;
};
}; };
} }

View file

@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
{ {
base,
lib, lib,
system, system,
config, config,
@ -26,11 +27,10 @@
cfg = config.nixpkgs.channels; cfg = config.nixpkgs.channels;
in { in {
options.nixpkgs.channels = lib.mkOption { options.nixpkgs.channels = lib.mkOption {
type = lib.types.attrsOf (lib.types.attrs); type = lib.types.attrs;
default = {}; default = {};
description = "Declare package repositories per module context (nixos, home-manager, etc)"; description = "Declare package repositories";
example = { example = {
"homes" = {
"pkgs" = { "pkgs" = {
source = "inputs.nixpkgs"; source = "inputs.nixpkgs";
system = "x86-64-linux"; system = "x86-64-linux";
@ -43,57 +43,50 @@ in {
source = "inputs.nixpkgs-unstable"; source = "inputs.nixpkgs-unstable";
system = "x86-64-linux"; system = "x86-64-linux";
config = { config = {
allowUnfree = true; allowUnfree = false;
allowBroken = false; allowBroken = true;
};
}; };
}; };
}; };
}; };
config = let config = let
# TODO: use lib.types.submodule to restrict what options
# TODO: can be given to `nixpkgs.channels.${moduleName}.${name}`
decl =
cfg.${contextName} or cfg.default;
repos = repos =
decl cfg
|> (xs: removeAttrs xs ["default"])
|> mapAttrs ( |> mapAttrs (
name: args: name: args:
lib.mkForce ( lib.mkForce (
assert args ? source assert args ? source
|| abort '' || abort ''
${toString ./.} `nixpkgs.channels.${name}` missing required attribute "source"
`nixpkgs.channels.${contextName}.${name}` missing required attribute "source"
''; '';
((removeAttrs args ["source"]) import args.source ({inherit system;} // (removeAttrs args ["source"]))
// {inherit system;})
|> import args.source
) )
); );
in {
# NOTE: _module.args is a special option that allows us to
# NOTE: set extend specialArgs from inside the modules.
_module.args = repos;
nixpkgs = let # XXX: TODO: would it work to use `base` instead of having default?
defaultPkgs = defaultPkgs =
decl.default or (throw '' cfg.default or (throw ''
Your `nixpkgs.nix` file does not declare a default package source. Your `nixpkgs.nix` file does not declare a default package source.
Ensure you set `nixpkgs.channels.*.default = ...;` Ensure you set `nixpkgs.channels.*.default = ...;`
''); '');
in in {
# NOTE: _module.args is a special option that allows us to
# NOTE: set extend specialArgs from inside the modules.
# WARNING: pkgs is a reserved specialArg
_module.args = removeAttrs repos ["pkgs"];
nixpkgs =
if contextName == "hosts" if contextName == "hosts"
then { then {
flake.source = lib.mkOverride 200 defaultPkgs.source; flake.source = lib.mkOverride 200 base; # DEBUG: temp while getting base to work
config = lib.mkOverride 200 defaultPkgs.config; overlays = lib.mkOverride 200 (defaultPkgs.overlays or {});
config = lib.mkOverride 200 (defaultPkgs.config or {});
} }
else if contextName == "homes" else if contextName == "homes"
then { then {
# XXX: XXX: XXX: OH OH OH OMG, its because aurora never defines pkgs
config = lib.mkOverride 200 (defaultPkgs.config or {}); config = lib.mkOverride 200 (defaultPkgs.config or {});
# XXX: WARNING: TODO: modify options so overlays must always be given as the correct type
overlays = lib.mkOverride 200 (defaultPkgs.overlays or []); overlays = lib.mkOverride 200 (defaultPkgs.overlays or []);
} }
else {}; else {};

81
flake.lock generated
View file

@ -3,7 +3,9 @@
"deploy-rs": { "deploy-rs": {
"inputs": { "inputs": {
"flake-compat": "flake-compat", "flake-compat": "flake-compat",
"nixpkgs": "nixpkgs", "nixpkgs": [
"nixpkgs"
],
"utils": "utils" "utils": "utils"
}, },
"locked": { "locked": {
@ -58,6 +60,27 @@
"type": "github" "type": "github"
} }
}, },
"microvm": {
"inputs": {
"nixpkgs": [
"nixpkgs"
],
"spectrum": "spectrum"
},
"locked": {
"lastModified": 1771365290,
"narHash": "sha256-1XJOslVyF7yzf6yd/yl1VjGLywsbtwmQh3X1LuJcLI4=",
"owner": "microvm-nix",
"repo": "microvm.nix",
"rev": "789c90b164b55b4379e7a94af8b9c01489024c18",
"type": "github"
},
"original": {
"owner": "microvm-nix",
"repo": "microvm.nix",
"type": "github"
}
},
"nix-github-actions": { "nix-github-actions": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [
@ -105,38 +128,6 @@
} }
}, },
"nixpkgs": { "nixpkgs": {
"locked": {
"lastModified": 1743014863,
"narHash": "sha256-jAIUqsiN2r3hCuHji80U7NNEafpIMBXiwKlSrjWMlpg=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "bd3bac8bfb542dbde7ffffb6987a1a1f9d41699f",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-unstable": {
"locked": {
"lastModified": 1768305791,
"narHash": "sha256-AIdl6WAn9aymeaH/NvBj0H9qM+XuAuYbGMZaP0zcXAQ=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "1412caf7bf9e660f2f962917c14b1ea1c3bc695e",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": { "locked": {
"lastModified": 1768323494, "lastModified": 1768323494,
"narHash": "sha256-yBXJLE6WCtrGo7LKiB6NOt6nisBEEkguC/lq/rP3zRQ=", "narHash": "sha256-yBXJLE6WCtrGo7LKiB6NOt6nisBEEkguC/lq/rP3zRQ=",
@ -152,7 +143,7 @@
"type": "github" "type": "github"
} }
}, },
"nixpkgs_3": { "nixpkgs_2": {
"locked": { "locked": {
"lastModified": 1767313136, "lastModified": 1767313136,
"narHash": "sha256-16KkgfdYqjaeRGBaYsNrhPRRENs0qzkQVUooNHtoy2w=", "narHash": "sha256-16KkgfdYqjaeRGBaYsNrhPRRENs0qzkQVUooNHtoy2w=",
@ -171,7 +162,7 @@
"nt": { "nt": {
"inputs": { "inputs": {
"nix-unit": "nix-unit", "nix-unit": "nix-unit",
"nixpkgs": "nixpkgs_3", "nixpkgs": "nixpkgs_2",
"systems": "systems_2" "systems": "systems_2"
}, },
"locked": { "locked": {
@ -191,12 +182,28 @@
"root": { "root": {
"inputs": { "inputs": {
"deploy-rs": "deploy-rs", "deploy-rs": "deploy-rs",
"nixpkgs": "nixpkgs_2", "microvm": "microvm",
"nixpkgs-unstable": "nixpkgs-unstable", "nixpkgs": "nixpkgs",
"nt": "nt", "nt": "nt",
"systems": "systems_3" "systems": "systems_3"
} }
}, },
"spectrum": {
"flake": false,
"locked": {
"lastModified": 1759482047,
"narHash": "sha256-H1wiXRQHxxPyMMlP39ce3ROKCwI5/tUn36P8x6dFiiQ=",
"ref": "refs/heads/main",
"rev": "c5d5786d3dc938af0b279c542d1e43bce381b4b9",
"revCount": 996,
"type": "git",
"url": "https://spectrum-os.org/git/spectrum"
},
"original": {
"type": "git",
"url": "https://spectrum-os.org/git/spectrum"
}
},
"systems": { "systems": {
"locked": { "locked": {
"lastModified": 1681028828, "lastModified": 1681028828,

View file

@ -23,11 +23,6 @@
nt.url = "github:cry128/nt"; nt.url = "github:cry128/nt";
home-manager = {
url = "github:nix-community/home-manager/release-25.11";
inputs.nixpkgs.follows = "nixpkgs";
};
deploy-rs = { deploy-rs = {
url = "github:serokell/deploy-rs"; url = "github:serokell/deploy-rs";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";