Compare commits
No commits in common. "main" and "v0.2.2-alpha" have entirely different histories.
main
...
v0.2.2-alp
31 changed files with 673 additions and 1060 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,2 +1 @@
|
||||||
/hidden
|
/hidden
|
||||||
target/
|
|
||||||
|
|
|
||||||
33
CHANGELOG.md
33
CHANGELOG.md
|
|
@ -1,33 +0,0 @@
|
||||||
# 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`
|
|
||||||
|
|
||||||
## v0.2.3-alpha
|
|
||||||
>[!TODO]
|
|
||||||
> I've been too focused on upcoming changes...
|
|
||||||
|
|
||||||
## v0.2.4-alpha
|
|
||||||
- `homeManager` flake reference may now be specified in snowflake
|
|
||||||
- ``
|
|
||||||
26
NOTICE
26
NOTICE
|
|
@ -1,26 +0,0 @@
|
||||||
Cerulean (https://dobutterfliescry.net/cerulean)
|
|
||||||
Copyright 2025-2026 _cry64 (Emile Clark-Boman)
|
|
||||||
|
|
||||||
This product includes software developed by
|
|
||||||
_cry64 (Emile Clark-Boman) (https://github.com/cry128/nt) under the MIT license.
|
|
||||||
Copyright 2025-2026 _cry64 (Emile Clark-Boman)
|
|
||||||
|
|
||||||
This product includes software developed by
|
|
||||||
Eelco Dolstra and the Nixpkgs/NixOS contributors under the MIT license.
|
|
||||||
Copyright 2003-2026 Eelco Dolstra and the Nixpkgs/NixOS contributors
|
|
||||||
|
|
||||||
This product includes software developed by
|
|
||||||
the Home Manager contributors (https://github.com/nix-community/home-manager) under the MIT license.
|
|
||||||
Copyright 2017-2026 Home Manager contributors
|
|
||||||
|
|
||||||
This product includes software developed by
|
|
||||||
Serokell (https://serokell.io) under the MPL-2.0 license.
|
|
||||||
Copyright 2020-2026 Serokell
|
|
||||||
|
|
||||||
This product includes software developed by
|
|
||||||
nix-systems (https://github.com/nix-systems) under the MIT license.
|
|
||||||
Copyright 2023 nix-systems
|
|
||||||
|
|
||||||
This product includes software developed by
|
|
||||||
Astro (https://github.com/astro) and the MicroVM.nix contributors (https://github.com/microvm-nix/microvm.nix) under the MIT license.
|
|
||||||
Copyright 2021 Astro, and MicroVM.nix contributors
|
|
||||||
31
TODO.md
31
TODO.md
|
|
@ -1,17 +1,6 @@
|
||||||
## Next
|
|
||||||
- [ ] formalize how the snow flake system compiles outputs, this would remove the need for `mapNodes`
|
|
||||||
- [ ] groups should allow you to set node configuration defaults
|
|
||||||
|
|
||||||
- [ ] add `options.experimental` for snowflake
|
|
||||||
- [ ] add `legacyImports` support
|
|
||||||
|
|
||||||
- [ ] support hs system per dir, ie hosts/<name>/overlays or hosts/<name>/nixpkgs.nix
|
|
||||||
|
|
||||||
## Queued
|
|
||||||
- [ ] per node home configuration is a lil jank rn
|
|
||||||
|
|
||||||
- [ ] 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`
|
||||||
- [ ] create an alternative to nixos-install called cerulean-install that
|
- [ ] create an alternative to nixos-install called cerulean-install that
|
||||||
allows people to easily bootstrap new machines (and host it on dobutterfliescry.net)
|
allows people to easily bootstrap new machines (and host it on dobutterfliescry.net)
|
||||||
|
|
||||||
|
|
@ -25,19 +14,31 @@
|
||||||
|
|
||||||
- [ ] go through all flake inputs (recursively) and ENSURE we remove all duplicates by using follows!!
|
- [ ] go through all flake inputs (recursively) and ENSURE we remove all duplicates by using follows!!
|
||||||
|
|
||||||
|
- [X] rename nixos-modules/ to nixos/
|
||||||
|
- [X] ensure all machines are in groups.all by default
|
||||||
|
|
||||||
|
- [X] fix nixpkgs.nix not working (default not respected)
|
||||||
|
- [X] remove dependence on nixpkgs
|
||||||
|
|
||||||
- [ ] 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?
|
||||||
|
- [ ] 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
|
||||||
it then returns instead of config)
|
it then returns instead of config)
|
||||||
- [ ] support `legacyImports` (?)
|
|
||||||
|
|
||||||
|
- [ ] what if we automated the process of replacing windows with Nix??
|
||||||
|
then push this to nixos-anywhere or nix-infect lmaooo
|
||||||
|
|
||||||
- [ ] patch microvm so that acpi=off https://github.com/microvm-nix/microvm.nix/commit/b59a26962bb324cc0a134756a323f3e164409b72
|
- [ ] patch microvm so that acpi=off https://github.com/microvm-nix/microvm.nix/commit/b59a26962bb324cc0a134756a323f3e164409b72
|
||||||
cause otherwise 2GB causes a failure
|
cause otherwise 2GB causes a failure
|
||||||
|
|
||||||
- [ ] write the cerulean cli
|
- [ ] rewrite the ceru cli in rust
|
||||||
|
- [ ] make `ceru` do local and remote deployments
|
||||||
|
|
||||||
```nix
|
```nix
|
||||||
# REF: foxora
|
# REF: foxora
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Copyright 2025-2026 _cry64 (Emile Clark-Boman)
|
# Copyright 2025 Emile Clark-Boman
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Copyright 2025-2026 _cry64 (Emile Clark-Boman)
|
# Copyright 2025 Emile Clark-Boman
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Copyright 2025-2026 _cry64 (Emile Clark-Boman)
|
# Copyright 2025 Emile Clark-Boman
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Copyright 2025-2026 _cry64 (Emile Clark-Boman)
|
# Copyright 2025 Emile Clark-Boman
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Copyright 2025-2026 _cry64 (Emile Clark-Boman)
|
# Copyright 2026 Emile Clark-Boman
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Copyright 2025-2026 _cry64 (Emile Clark-Boman)
|
# Copyright 2025 Emile Clark-Boman
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Copyright 2025-2026 _cry64 (Emile Clark-Boman)
|
# Copyright 2025 Emile Clark-Boman
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright 2025-2026 _cry64 (Emile Clark-Boman)
|
# Copyright 2025 Emile Clark-Boman
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
|
@ -17,23 +17,20 @@
|
||||||
...
|
...
|
||||||
} @ args:
|
} @ args:
|
||||||
mix.newMixture args (mixture: {
|
mix.newMixture args (mixture: {
|
||||||
submods.public = [
|
includes.public = [
|
||||||
./snow
|
./nexus
|
||||||
];
|
];
|
||||||
|
|
||||||
version = "0.2.5-alpha";
|
version = "0.2.2";
|
||||||
|
|
||||||
# WARNING: legacy
|
nixosModules = rec {
|
||||||
mkFlake = mixture.snow.flake;
|
default = cerulean;
|
||||||
|
cerulean = ./nixos;
|
||||||
|
};
|
||||||
|
|
||||||
overlays = [
|
overlays = [
|
||||||
# build deploy-rs as a package not from the flake input,
|
# build deploy-rs as a package not from the flake input,
|
||||||
# hence we can rely on a nixpkg binary cache.
|
# hence we can rely on a nixpkg binary cache.
|
||||||
inputs.deploy-rs.overlays.default
|
inputs.deploy-rs.overlays.default
|
||||||
];
|
];
|
||||||
|
|
||||||
nixosModules = rec {
|
|
||||||
default = cerulean;
|
|
||||||
cerulean = ./nixos;
|
|
||||||
};
|
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
# Copyright 2025-2026 _cry64 (Emile Clark-Boman)
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
{
|
|
||||||
username,
|
|
||||||
lib,
|
|
||||||
...
|
|
||||||
}: {
|
|
||||||
# NOTE: you can access the system configuration via the `osConfig` arg
|
|
||||||
|
|
||||||
# WARNING: required for home-manager to work
|
|
||||||
programs.home-manager.enable = true; # user must apply lib.mkForce
|
|
||||||
# Nicely reload systemd units when changing configs
|
|
||||||
systemd.user.startServices = lib.mkDefault "sd-switch";
|
|
||||||
|
|
||||||
home = {
|
|
||||||
username = lib.mkDefault username;
|
|
||||||
homeDirectory = lib.mkDefault "/home/${username}";
|
|
||||||
|
|
||||||
sessionVariables = {
|
|
||||||
NIX_SHELL_PRESERVE_PROMPT = lib.mkDefault 1;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright 2025-2026 _cry64 (Emile Clark-Boman)
|
# Copyright 2025 Emile Clark-Boman
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
|
@ -11,13 +11,10 @@
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
# 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.
|
||||||
{
|
{mix, ...} @ args:
|
||||||
root,
|
mix.newMixture args (mixture: {
|
||||||
snow,
|
includes.public = [
|
||||||
...
|
./nodes.nix
|
||||||
}: {
|
./nexus.nix
|
||||||
imports = [
|
|
||||||
./nodes
|
|
||||||
(snow.findImport /${root}/snow)
|
|
||||||
];
|
];
|
||||||
}
|
})
|
||||||
276
cerulean/nexus/nexus.nix
Normal file
276
cerulean/nexus/nexus.nix
Normal file
|
|
@ -0,0 +1,276 @@
|
||||||
|
# Copyright 2025 Emile Clark-Boman
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
{
|
||||||
|
self,
|
||||||
|
this,
|
||||||
|
nt,
|
||||||
|
inputs,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
|
inherit
|
||||||
|
(builtins)
|
||||||
|
attrNames
|
||||||
|
concatLists
|
||||||
|
concatStringsSep
|
||||||
|
elem
|
||||||
|
filter
|
||||||
|
getAttr
|
||||||
|
isAttrs
|
||||||
|
isFunction
|
||||||
|
isList
|
||||||
|
mapAttrs
|
||||||
|
pathExists
|
||||||
|
typeOf
|
||||||
|
;
|
||||||
|
|
||||||
|
inherit
|
||||||
|
(this)
|
||||||
|
mapNodes
|
||||||
|
;
|
||||||
|
|
||||||
|
inherit
|
||||||
|
(nt)
|
||||||
|
findImport
|
||||||
|
;
|
||||||
|
|
||||||
|
templateNexus = let
|
||||||
|
inherit
|
||||||
|
(nt.naive.terminal)
|
||||||
|
Terminal
|
||||||
|
;
|
||||||
|
in {
|
||||||
|
base = null;
|
||||||
|
modules = [];
|
||||||
|
args = Terminal {};
|
||||||
|
|
||||||
|
groups = Terminal {};
|
||||||
|
nodes = Terminal {};
|
||||||
|
};
|
||||||
|
|
||||||
|
ROOT_GROUP_NAME = "all";
|
||||||
|
|
||||||
|
parseGroupDecl = groups: let
|
||||||
|
validGroup = g:
|
||||||
|
isAttrs g
|
||||||
|
|| throw ''
|
||||||
|
Cerulean Nexus groups must be provided as attribute sets, got "${typeOf g}" instead!
|
||||||
|
Ensure all the group definitions are attribute sets under your call to `cerulean.mkNexus`.
|
||||||
|
NOTE: Groups can be accessed via `self.groups.PATH.TO.YOUR.GROUP`
|
||||||
|
'';
|
||||||
|
delegate = parent: gName: g: let
|
||||||
|
result =
|
||||||
|
(g
|
||||||
|
// {
|
||||||
|
_name = gName;
|
||||||
|
_parent = parent;
|
||||||
|
})
|
||||||
|
|> mapAttrs (name: value:
|
||||||
|
if elem name ["_name" "_parent"]
|
||||||
|
# ignore metadata fields
|
||||||
|
then value
|
||||||
|
else assert validGroup value; (delegate result name value));
|
||||||
|
in
|
||||||
|
result;
|
||||||
|
in
|
||||||
|
assert validGroup groups;
|
||||||
|
delegate null ROOT_GROUP_NAME groups;
|
||||||
|
|
||||||
|
parseNexus = nexus:
|
||||||
|
assert isAttrs nexus
|
||||||
|
|| abort ''
|
||||||
|
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`.
|
||||||
|
''; 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 = parseGroupDecl base.groups;
|
||||||
|
};
|
||||||
|
|
||||||
|
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
|
||||||
|
);
|
||||||
|
|
||||||
|
final =
|
||||||
|
decl
|
||||||
|
// {
|
||||||
|
nexus = parseNexus (decl.nexus or {});
|
||||||
|
};
|
||||||
|
in
|
||||||
|
final;
|
||||||
|
|
||||||
|
getGroupModules = root: nodeName: node:
|
||||||
|
assert isList node.groups
|
||||||
|
|| throw ''
|
||||||
|
Cerulean Nexus node "${nodeName}" does not declare group membership as a list, got "${typeOf node.groups}" instead!
|
||||||
|
Ensure `nexus.nodes.${nodeName}.groups` is a list under your call to `cerulean.mkNexus`.
|
||||||
|
'';
|
||||||
|
# ensure root group is always added
|
||||||
|
(node.groups
|
||||||
|
++ [
|
||||||
|
{
|
||||||
|
_parent = null;
|
||||||
|
_name = ROOT_GROUP_NAME;
|
||||||
|
}
|
||||||
|
])
|
||||||
|
# ensure all members are actually groups
|
||||||
|
|> map (group: let
|
||||||
|
got =
|
||||||
|
if ! isAttrs group
|
||||||
|
then toString group
|
||||||
|
else
|
||||||
|
group
|
||||||
|
|> attrNames
|
||||||
|
|> map (name: "${name} = <${typeOf (getAttr name group)}>;")
|
||||||
|
|> concatStringsSep " "
|
||||||
|
|> (x: "{ ${x} }");
|
||||||
|
in
|
||||||
|
if group ? _name
|
||||||
|
then group
|
||||||
|
else
|
||||||
|
throw ''
|
||||||
|
Cerulean Nexus node "${nodeName}" is a member of an incorrectly structured group.
|
||||||
|
Got "${got}" of primitive type "${typeOf group}".
|
||||||
|
NOTE: Groups can be accessed via `self.groups.PATH.TO.YOUR.GROUP`
|
||||||
|
'')
|
||||||
|
# add all inherited groups via _parent
|
||||||
|
|> map (let
|
||||||
|
delegate = g:
|
||||||
|
if g._parent == null
|
||||||
|
then [g]
|
||||||
|
else [g] ++ delegate (g._parent);
|
||||||
|
in
|
||||||
|
delegate)
|
||||||
|
# flatten recursion result
|
||||||
|
|> concatLists
|
||||||
|
# find import location
|
||||||
|
|> map (group: findImport (root + "/groups/${group._name}"))
|
||||||
|
# filter by uniqueness
|
||||||
|
|> nt.prim.unique
|
||||||
|
# ignore missing groups
|
||||||
|
|> filter pathExists;
|
||||||
|
in {
|
||||||
|
mkNexus = root: outputsBuilder: let
|
||||||
|
decl = parseDecl outputsBuilder;
|
||||||
|
|
||||||
|
inherit
|
||||||
|
(decl)
|
||||||
|
nexus
|
||||||
|
;
|
||||||
|
customOutputs = removeAttrs decl ["nexus"];
|
||||||
|
|
||||||
|
outputs = rec {
|
||||||
|
nixosConfigurations = mapNodes nexus (
|
||||||
|
{
|
||||||
|
lib,
|
||||||
|
nodeName,
|
||||||
|
node,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
|
nixosDecl = lib.nixosSystem rec {
|
||||||
|
system = node.system;
|
||||||
|
specialArgs =
|
||||||
|
nexus.args
|
||||||
|
// node.args
|
||||||
|
// {
|
||||||
|
inherit root specialArgs;
|
||||||
|
inherit (node) system;
|
||||||
|
_deploy-rs = inputs.deploy-rs;
|
||||||
|
};
|
||||||
|
modules =
|
||||||
|
[
|
||||||
|
self.nixosModules.default
|
||||||
|
(findImport (root + "/hosts/${nodeName}"))
|
||||||
|
|
||||||
|
inputs.home-manager.nixosModules.default
|
||||||
|
# inputs.microvm.nixosModules.microvm
|
||||||
|
]
|
||||||
|
++ (getGroupModules root nodeName node)
|
||||||
|
++ node.modules
|
||||||
|
++ nexus.modules;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
nixosDecl
|
||||||
|
);
|
||||||
|
|
||||||
|
deploy.nodes = mapNodes nexus ({
|
||||||
|
nodeName,
|
||||||
|
node,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
|
inherit
|
||||||
|
(node.deploy)
|
||||||
|
activationTimeout
|
||||||
|
autoRollback
|
||||||
|
confirmTimeout
|
||||||
|
interactiveSudo
|
||||||
|
magicRollback
|
||||||
|
remoteBuild
|
||||||
|
ssh
|
||||||
|
sudo
|
||||||
|
user
|
||||||
|
;
|
||||||
|
|
||||||
|
nixosFor = system: inputs.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) inputs.deploy-rs.lib;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
outputs // customOutputs;
|
||||||
|
}
|
||||||
99
cerulean/nexus/nodes.nix
Normal file
99
cerulean/nexus/nodes.nix
Normal file
|
|
@ -0,0 +1,99 @@
|
||||||
|
# Copyright 2025 Emile Clark-Boman
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
{nt, ...}: let
|
||||||
|
inherit
|
||||||
|
(builtins)
|
||||||
|
isAttrs
|
||||||
|
mapAttrs
|
||||||
|
typeOf
|
||||||
|
;
|
||||||
|
in rec {
|
||||||
|
# abstract node instance that stores all default values
|
||||||
|
templateNode = name: system: let
|
||||||
|
inherit
|
||||||
|
(nt.naive.terminal)
|
||||||
|
Terminal
|
||||||
|
;
|
||||||
|
|
||||||
|
missing = msg: path:
|
||||||
|
Terminal (abort ''
|
||||||
|
Each Cerulean Nexus node is required to specify ${msg}!
|
||||||
|
Ensure `nexus.${path}` exists under your call to `cerulean.mkNexus`.
|
||||||
|
'');
|
||||||
|
in {
|
||||||
|
enabled = true;
|
||||||
|
system = missing "its system architecture" "system";
|
||||||
|
groups = [];
|
||||||
|
modules = [];
|
||||||
|
args = Terminal {};
|
||||||
|
|
||||||
|
base = null;
|
||||||
|
|
||||||
|
deploy = {
|
||||||
|
user = "root";
|
||||||
|
sudo = "sudo -u";
|
||||||
|
interactiveSudo = false;
|
||||||
|
|
||||||
|
remoteBuild = false; # prefer local builds for remote deploys
|
||||||
|
|
||||||
|
autoRollback = true; # reactivate previous profile if activation fails
|
||||||
|
magicRollback = true;
|
||||||
|
|
||||||
|
activationTimeout = 500; # timeout in seconds for profile activation
|
||||||
|
confirmTimeout = 30; # timeout in seconds for profile activation confirmation
|
||||||
|
|
||||||
|
ssh = {
|
||||||
|
host = name;
|
||||||
|
user = "ceru-build"; # ceru-build is the default connection user
|
||||||
|
port = 22;
|
||||||
|
opts = [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
parseNode = name: nodeAttrs:
|
||||||
|
if !(isAttrs nodeAttrs)
|
||||||
|
then
|
||||||
|
# fail if node is not an attribute set
|
||||||
|
abort ''
|
||||||
|
Cerulean Nexus nodes must be provided as an attribute set, got "${typeOf nodeAttrs}" instead!
|
||||||
|
Ensure all `cerulean.nexus.nodes.${name}` declarations are attribute sets under your call to `cerulean.mkNexus`.
|
||||||
|
''
|
||||||
|
else let
|
||||||
|
templateAttrs = templateNode name nodeAttrs.system;
|
||||||
|
in
|
||||||
|
nt.projectOnto templateAttrs nodeAttrs;
|
||||||
|
|
||||||
|
mapNodes = nexus: f:
|
||||||
|
nexus.nodes
|
||||||
|
|> mapAttrs (nodeName: nodeAttrs: let
|
||||||
|
node = parseNode nodeName nodeAttrs;
|
||||||
|
|
||||||
|
# use per-node base or default to nexus base
|
||||||
|
base =
|
||||||
|
if node.base != null
|
||||||
|
then node.base
|
||||||
|
else if nexus.base != null
|
||||||
|
then nexus.base
|
||||||
|
else
|
||||||
|
abort ''
|
||||||
|
Cerulean cannot construct nexus node "${nodeName}" without a base package source.
|
||||||
|
Ensure `nexus.nodes.*.base` or `nexus.base` is a flake reference to the github:NixOS/nixpkgs repository.
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
f {
|
||||||
|
inherit nodeName node;
|
||||||
|
lib = base.lib;
|
||||||
|
});
|
||||||
|
}
|
||||||
108
cerulean/nexus/snow.nix
Normal file
108
cerulean/nexus/snow.nix
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
# Copyright 2026 Emile Clark-Boman
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
{
|
||||||
|
inputs,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}: {
|
||||||
|
# nexus
|
||||||
|
options = let
|
||||||
|
inherit
|
||||||
|
(lib)
|
||||||
|
mkOption
|
||||||
|
types
|
||||||
|
;
|
||||||
|
in {
|
||||||
|
modules = mkOption {
|
||||||
|
type = types.listOf types.path;
|
||||||
|
};
|
||||||
|
args = mkOption {
|
||||||
|
type = types.attrs;
|
||||||
|
};
|
||||||
|
|
||||||
|
groups = mkOption {
|
||||||
|
type = types.attrs;
|
||||||
|
};
|
||||||
|
|
||||||
|
nodes = mkOption {
|
||||||
|
type = types.attrsOf (types.submoduleWith ({...}: {
|
||||||
|
options = {
|
||||||
|
enabled = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = true;
|
||||||
|
};
|
||||||
|
system = mkOption {
|
||||||
|
type = types.enum inputs.systems;
|
||||||
|
};
|
||||||
|
groups = mkOption {
|
||||||
|
type = types.list;
|
||||||
|
};
|
||||||
|
modules = mkOption {
|
||||||
|
type = types.list;
|
||||||
|
};
|
||||||
|
args = mkOption {
|
||||||
|
type = types.attrs;
|
||||||
|
};
|
||||||
|
|
||||||
|
deploy = {
|
||||||
|
user = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
sudoCmd = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
interactiveSudo = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
};
|
||||||
|
|
||||||
|
remoteBuild = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
};
|
||||||
|
autoRollback = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
};
|
||||||
|
magicRollback = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
};
|
||||||
|
|
||||||
|
activationTimeout = mkOption {
|
||||||
|
type = types.int;
|
||||||
|
};
|
||||||
|
confirmTimeout = mkOption {
|
||||||
|
type = types.int;
|
||||||
|
};
|
||||||
|
|
||||||
|
ssh = {
|
||||||
|
host = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
user = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
};
|
||||||
|
port = mkOption {
|
||||||
|
type = types.int;
|
||||||
|
};
|
||||||
|
opts = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright 2025-2026 _cry64 (Emile Clark-Boman)
|
# Copyright 2026 Emile Clark-Boman
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
|
@ -14,39 +14,19 @@
|
||||||
{
|
{
|
||||||
root,
|
root,
|
||||||
system,
|
system,
|
||||||
hostname,
|
_deploy-rs,
|
||||||
node,
|
|
||||||
pkgs,
|
|
||||||
lib,
|
|
||||||
_cerulean,
|
|
||||||
...
|
...
|
||||||
} @ args: {
|
} @ args: {
|
||||||
imports =
|
imports = [
|
||||||
[
|
# user configuration
|
||||||
_cerulean.inputs.sops-nix.nixosModules.sops
|
(import (root + "/nixpkgs.nix"))
|
||||||
# _cerulean.inputs.microvm.nixosModules.microvm
|
# options declarations
|
||||||
|
|
||||||
# add support for `options.legacyImports`
|
|
||||||
# ./legacy-imports.nix
|
|
||||||
|
|
||||||
# nixos options declarations
|
|
||||||
(import ./nixpkgs.nix (args // {contextName = "hosts";}))
|
(import ./nixpkgs.nix (args // {contextName = "hosts";}))
|
||||||
|
|
||||||
# user's nixpkg configuration
|
./home-manager.nix
|
||||||
(import /${root}/nixpkgs.nix)
|
];
|
||||||
]
|
|
||||||
# homemanager options declarations
|
|
||||||
++ (lib.optional (_cerulean.homeManager != null) ./home.nix)
|
|
||||||
# remote deployment configuration
|
|
||||||
++ (lib.optional (node.deploy.ssh.host != null) ./remote-deploy);
|
|
||||||
|
|
||||||
networking.hostName = lib.mkDefault hostname;
|
environment.systemPackages = [
|
||||||
|
_deploy-rs.packages.${system}.default
|
||||||
environment.systemPackages =
|
];
|
||||||
(with pkgs; [
|
|
||||||
sops
|
|
||||||
])
|
|
||||||
++ (with _cerulean.inputs; [
|
|
||||||
deploy-rs.packages.${system}.default
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
50
cerulean/nixos/home-manager.nix
Normal file
50
cerulean/nixos/home-manager.nix
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
# Copyright 2026 Emile Clark-Boman
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
{
|
||||||
|
root,
|
||||||
|
system,
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
specialArgs,
|
||||||
|
...
|
||||||
|
} @ args: let
|
||||||
|
inherit
|
||||||
|
(builtins)
|
||||||
|
attrNames
|
||||||
|
filter
|
||||||
|
pathExists
|
||||||
|
;
|
||||||
|
in {
|
||||||
|
config = {
|
||||||
|
home-manager = {
|
||||||
|
users =
|
||||||
|
config.users.users
|
||||||
|
|> attrNames
|
||||||
|
|> filter (x: pathExists (root + "/homes/${x}"))
|
||||||
|
|> (x: lib.genAttrs x (y: import (root + "/homes/${y}")));
|
||||||
|
|
||||||
|
extraSpecialArgs = {inherit root system;} // (specialArgs.inputs or {});
|
||||||
|
sharedModules = [
|
||||||
|
# user configuration
|
||||||
|
(import (root + "/nixpkgs.nix"))
|
||||||
|
# options declarations
|
||||||
|
(import ./nixpkgs.nix (args // {contextName = "homes";}))
|
||||||
|
];
|
||||||
|
|
||||||
|
# disable home-manager trying anything fancy
|
||||||
|
# we control the pkgs now!!
|
||||||
|
# useGlobalPkgs = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -1,82 +0,0 @@
|
||||||
# Copyright 2025-2026 _cry64 (Emile Clark-Boman)
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
{
|
|
||||||
_cerulean,
|
|
||||||
config,
|
|
||||||
root,
|
|
||||||
lib,
|
|
||||||
...
|
|
||||||
} @ args: let
|
|
||||||
inherit
|
|
||||||
(builtins)
|
|
||||||
pathExists
|
|
||||||
;
|
|
||||||
|
|
||||||
inherit
|
|
||||||
(lib)
|
|
||||||
filterAttrs
|
|
||||||
mapAttrs
|
|
||||||
;
|
|
||||||
in {
|
|
||||||
imports = [
|
|
||||||
_cerulean.homeManager.nixosModules.default
|
|
||||||
];
|
|
||||||
|
|
||||||
options = {
|
|
||||||
users.users = lib.mkOption {
|
|
||||||
type = lib.types.attrsOf (lib.types.submodule {
|
|
||||||
options.manageHome = lib.mkOption {
|
|
||||||
type = lib.types.bool;
|
|
||||||
default = true;
|
|
||||||
example = false;
|
|
||||||
description = ''
|
|
||||||
Whether Cerulean should automatically enable home-manager for this user,
|
|
||||||
and manage their home configuration declaratively.
|
|
||||||
|
|
||||||
Enabled by default, but can be disabled if necessary.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
});
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
config = {
|
|
||||||
home-manager = {
|
|
||||||
useUserPackages = lib.mkDefault false;
|
|
||||||
useGlobalPkgs = lib.mkDefault true;
|
|
||||||
|
|
||||||
overwriteBackup = lib.mkDefault false;
|
|
||||||
backupFileExtension = lib.mkDefault "bak";
|
|
||||||
|
|
||||||
users =
|
|
||||||
config.users.users
|
|
||||||
|> filterAttrs (name: value: value.manageHome && pathExists /${root}/homes/${name})
|
|
||||||
|> mapAttrs (name: _: {...}: {
|
|
||||||
imports = [/${root}/homes/${name}];
|
|
||||||
|
|
||||||
# per-user arguments
|
|
||||||
_module.args.username = name;
|
|
||||||
});
|
|
||||||
|
|
||||||
extraSpecialArgs = _cerulean.specialArgs;
|
|
||||||
sharedModules = [
|
|
||||||
../home
|
|
||||||
|
|
||||||
(import /${root}/nixpkgs.nix)
|
|
||||||
# options declarations
|
|
||||||
(import ./nixpkgs.nix (args // {contextName = "homes";}))
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright 2025-2026 _cry64 (Emile Clark-Boman)
|
# Copyright 2026 Emile Clark-Boman
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright 2025-2026 _cry64 (Emile Clark-Boman)
|
# Copyright 2026 Emile Clark-Boman
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright 2025-2026 _cry64 (Emile Clark-Boman)
|
# Copyright 2026 Emile Clark-Boman
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
|
@ -12,7 +12,6 @@
|
||||||
# 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,
|
||||||
|
|
@ -27,11 +26,12 @@
|
||||||
cfg = config.nixpkgs.channels;
|
cfg = config.nixpkgs.channels;
|
||||||
in {
|
in {
|
||||||
options.nixpkgs.channels = lib.mkOption {
|
options.nixpkgs.channels = lib.mkOption {
|
||||||
type = lib.types.attrs;
|
type = lib.types.attrsOf (lib.types.attrs);
|
||||||
default = {};
|
default = {};
|
||||||
description = "Declare package repositories";
|
description = "Declare package repositories per module context (nixos, home-manager, etc)";
|
||||||
example = {
|
example = {
|
||||||
"npkgs" = {
|
"homes" = {
|
||||||
|
"pkgs" = {
|
||||||
source = "inputs.nixpkgs";
|
source = "inputs.nixpkgs";
|
||||||
system = "x86-64-linux";
|
system = "x86-64-linux";
|
||||||
config = {
|
config = {
|
||||||
|
|
@ -43,53 +43,59 @@ in {
|
||||||
source = "inputs.nixpkgs-unstable";
|
source = "inputs.nixpkgs-unstable";
|
||||||
system = "x86-64-linux";
|
system = "x86-64-linux";
|
||||||
config = {
|
config = {
|
||||||
allowUnfree = false;
|
allowUnfree = true;
|
||||||
allowBroken = true;
|
allowBroken = false;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
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 =
|
||||||
cfg
|
decl
|
||||||
|> (xs: removeAttrs xs ["base"])
|
|
||||||
|> mapAttrs (
|
|> mapAttrs (
|
||||||
name: args:
|
name: args:
|
||||||
lib.mkForce (
|
lib.mkForce (
|
||||||
assert args ? source
|
assert args ? source
|
||||||
|| abort ''
|
|| abort ''
|
||||||
`nixpkgs.channels.${name}` missing required attribute "source"
|
${toString ./.}
|
||||||
|
`nixpkgs.channels.${contextName}.${name}` missing required attribute "source"
|
||||||
'';
|
'';
|
||||||
import args.source ({inherit system;} // (removeAttrs args ["source"]))
|
((removeAttrs args ["source"])
|
||||||
|
// {inherit system;})
|
||||||
|
|> import args.source
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
basePkgs = cfg.base or {};
|
|
||||||
in {
|
in {
|
||||||
# NOTE: _module.args is a special option that allows us to
|
# NOTE: _module.args is a special option that allows us to
|
||||||
# NOTE: set extend specialArgs from inside the modules.
|
# NOTE: set extend specialArgs from inside the modules.
|
||||||
# WARNING: pkgs is a reserved specialArg
|
_module.args = repos;
|
||||||
_module.args = removeAttrs repos ["pkgs" "base"];
|
|
||||||
|
|
||||||
nixpkgs = let
|
nixpkgs = let
|
||||||
nixpkgsConfig = {
|
defaultPkgs =
|
||||||
config = lib.mkForce (basePkgs.config or {});
|
decl.default or (throw ''
|
||||||
overlays = lib.mkForce (basePkgs.overlays or []);
|
Your `nixpkgs.nix` file does not declare a default package source.
|
||||||
};
|
Ensure you set `nixpkgs.channels.*.default = ...;`
|
||||||
|
'');
|
||||||
nixpkgsHostsConfig =
|
|
||||||
nixpkgsConfig
|
|
||||||
// {
|
|
||||||
flake.source = lib.mkForce base;
|
|
||||||
};
|
|
||||||
|
|
||||||
nixpkgsHomesConfig = lib.mkIf (!config.home-manager.useGlobalPkgs) nixpkgsConfig;
|
|
||||||
in
|
in
|
||||||
if contextName == "hosts"
|
if contextName == "hosts"
|
||||||
then nixpkgsHostsConfig
|
then {
|
||||||
|
flake.source = lib.mkOverride 200 defaultPkgs.source;
|
||||||
|
config = lib.mkOverride 200 defaultPkgs.config;
|
||||||
|
}
|
||||||
else if contextName == "homes"
|
else if contextName == "homes"
|
||||||
then nixpkgsHomesConfig
|
then {
|
||||||
|
# XXX: XXX: XXX: OH OH OH OMG, its because aurora never defines pkgs
|
||||||
|
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 []);
|
||||||
|
}
|
||||||
else {};
|
else {};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,82 +0,0 @@
|
||||||
{
|
|
||||||
config,
|
|
||||||
node,
|
|
||||||
lib,
|
|
||||||
pkgs,
|
|
||||||
hostname,
|
|
||||||
...
|
|
||||||
}: let
|
|
||||||
user = node.deploy.ssh.user;
|
|
||||||
cfg = config.users.users.${user};
|
|
||||||
|
|
||||||
DEFAULT_USER = "cerubld";
|
|
||||||
|
|
||||||
isStandardDeployUser = user == DEFAULT_USER;
|
|
||||||
in {
|
|
||||||
assertions = [
|
|
||||||
{
|
|
||||||
assertion = builtins.length node.deploy.ssh.publicKeys != 0;
|
|
||||||
message = ''
|
|
||||||
The Cerulean deployment user `${user}` for node `${hostname}` must have at least
|
|
||||||
one publicKey authorized for ssh deployment! Try setting `nodes.nodes.<name>.deploy.ssh.publicKeys = [ ... ]` <3
|
|
||||||
'';
|
|
||||||
}
|
|
||||||
# {
|
|
||||||
# assertion = cfg.isSystemUser && !cfg.isNormalUser;
|
|
||||||
# message = ''
|
|
||||||
# The Cerulean deployment user `${user}` for node `${hostname}` has been configured incorrectly.
|
|
||||||
# Ensure `users.users.${user}.isSystemUser == true` and `users.users.${user}.isNormalUser == false`.
|
|
||||||
# '';
|
|
||||||
# }
|
|
||||||
];
|
|
||||||
|
|
||||||
warnings = lib.optional (node.deploy.warnNonstandardDeployUser && !isStandardDeployUser) ''
|
|
||||||
The Cerulean deplyment user `${user}` for node `${hostname}` has been overriden.
|
|
||||||
It is recommended to leave this user as `${DEFAULT_USER}` unless you TRULY understand what you are doing!
|
|
||||||
This message can be disabled by setting `<node>.deploy.warnNonstandardBuildUser = false`.
|
|
||||||
'';
|
|
||||||
|
|
||||||
# prefer sudo-rs over sudo
|
|
||||||
security.sudo-rs = {
|
|
||||||
enable = true;
|
|
||||||
wheelNeedsPassword = true;
|
|
||||||
|
|
||||||
# allow the build user to run nix commands
|
|
||||||
extraRules = [
|
|
||||||
{
|
|
||||||
users = [user];
|
|
||||||
runAs = "${node.deploy.user}:ALL";
|
|
||||||
commands = [
|
|
||||||
# "${pkgs.nix}/bin/nix"
|
|
||||||
"ALL" # XXX: WARNING: FIX: TODO: DO NOT FUCKING USE `ALL`
|
|
||||||
];
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
# XXX: WARNING: FIX: TODO: use `trusted-public-keys` instead
|
|
||||||
nix.settings.trusted-users = [user];
|
|
||||||
|
|
||||||
# ensure deployment user has SSH permissions
|
|
||||||
services.openssh.settings.AllowUsers = [user];
|
|
||||||
|
|
||||||
users = lib.mkIf isStandardDeployUser {
|
|
||||||
groups.${user} = {};
|
|
||||||
|
|
||||||
users.${user} = {
|
|
||||||
enable = true;
|
|
||||||
description = "Cerulean's user for building and remote deployment.";
|
|
||||||
|
|
||||||
isSystemUser = true;
|
|
||||||
group = user;
|
|
||||||
|
|
||||||
createHome = true;
|
|
||||||
home = "/var/lib/cerulean/cerubld";
|
|
||||||
|
|
||||||
useDefaultShell = false;
|
|
||||||
shell = pkgs.bash;
|
|
||||||
|
|
||||||
openssh.authorizedKeys.keys = node.deploy.ssh.publicKeys;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,191 +0,0 @@
|
||||||
# Copyright 2025-2026 _cry64 (Emile Clark-Boman)
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
{
|
|
||||||
this,
|
|
||||||
self,
|
|
||||||
inputs,
|
|
||||||
systems,
|
|
||||||
nt,
|
|
||||||
mix,
|
|
||||||
...
|
|
||||||
} @ args: let
|
|
||||||
inherit
|
|
||||||
(builtins)
|
|
||||||
all
|
|
||||||
attrNames
|
|
||||||
elem
|
|
||||||
mapAttrs
|
|
||||||
warn
|
|
||||||
;
|
|
||||||
|
|
||||||
inherit (inputs.nixpkgs) lib;
|
|
||||||
|
|
||||||
inherit (nt) findImport;
|
|
||||||
in
|
|
||||||
mix.newMixture args (mixture: let
|
|
||||||
inherit (mixture) mapNodes;
|
|
||||||
in {
|
|
||||||
includes.private = [
|
|
||||||
./lib/nodes.nix
|
|
||||||
];
|
|
||||||
|
|
||||||
inherit findImport;
|
|
||||||
|
|
||||||
# snow.flake
|
|
||||||
flake = flakeInputs: root: let
|
|
||||||
module = lib.evalModules {
|
|
||||||
class = "snowflake";
|
|
||||||
# TODO: abort if inputs contains reserved names
|
|
||||||
specialArgs =
|
|
||||||
(flakeInputs
|
|
||||||
// {
|
|
||||||
inherit systems root;
|
|
||||||
inherit (this) snow;
|
|
||||||
inputs = flakeInputs;
|
|
||||||
})
|
|
||||||
|> (x: builtins.removeAttrs x ["self" "nodes"]);
|
|
||||||
|
|
||||||
modules = [
|
|
||||||
./module.nix
|
|
||||||
({config, ...}: {
|
|
||||||
_module.args = {
|
|
||||||
self = config;
|
|
||||||
nodes = config.nodes.nodes;
|
|
||||||
};
|
|
||||||
})
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
nodes = module.config.nodes;
|
|
||||||
in rec {
|
|
||||||
nixosConfigurations = mapNodes nodes (
|
|
||||||
{
|
|
||||||
base,
|
|
||||||
lib,
|
|
||||||
name,
|
|
||||||
node,
|
|
||||||
groupModules,
|
|
||||||
...
|
|
||||||
}: let
|
|
||||||
homeManager =
|
|
||||||
if node.homeManager != null
|
|
||||||
then node.homeManager
|
|
||||||
else if nodes.homeManager != null
|
|
||||||
then nodes.homeManager
|
|
||||||
else
|
|
||||||
warn ''
|
|
||||||
[snowflake] Neither `nodes.homeManager` nor `nodes.nodes.${name}.homeManager` were specified!
|
|
||||||
[snowflake] home-manager will NOT be used! User configuration will be ignored!
|
|
||||||
''
|
|
||||||
null;
|
|
||||||
|
|
||||||
userArgs = nodes.args // node.args;
|
|
||||||
ceruleanArgs = {
|
|
||||||
inherit systems root base nodes node;
|
|
||||||
inherit (node) system;
|
|
||||||
inherit (this) snow;
|
|
||||||
hostname = name;
|
|
||||||
|
|
||||||
_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 =
|
|
||||||
[
|
|
||||||
self.nixosModules.default
|
|
||||||
(findImport /${root}/hosts/${name})
|
|
||||||
]
|
|
||||||
++ (groupModules root)
|
|
||||||
++ node.modules
|
|
||||||
++ nodes.modules;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
deploy.nodes = mapNodes nodes ({
|
|
||||||
name,
|
|
||||||
node,
|
|
||||||
...
|
|
||||||
}: let
|
|
||||||
inherit
|
|
||||||
(node.deploy)
|
|
||||||
ssh
|
|
||||||
user
|
|
||||||
interactiveSudo
|
|
||||||
remoteBuild
|
|
||||||
rollback
|
|
||||||
autoRollback
|
|
||||||
magicRollback
|
|
||||||
activationTimeout
|
|
||||||
confirmTimeout
|
|
||||||
;
|
|
||||||
|
|
||||||
nixosFor = system: inputs.deploy-rs.lib.${system}.activate.nixos;
|
|
||||||
in {
|
|
||||||
hostname =
|
|
||||||
if ssh.host != null
|
|
||||||
then ssh.host
|
|
||||||
else "";
|
|
||||||
|
|
||||||
profilesOrder = ["default"]; # profiles priority
|
|
||||||
profiles.default = {
|
|
||||||
path = nixosFor node.system nixosConfigurations.${name};
|
|
||||||
|
|
||||||
user = user;
|
|
||||||
sudo = "sudo -u";
|
|
||||||
interactiveSudo = interactiveSudo;
|
|
||||||
|
|
||||||
fastConnection = false;
|
|
||||||
|
|
||||||
autoRollback = autoRollback -> rollback;
|
|
||||||
magicRollback = magicRollback -> rollback;
|
|
||||||
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 =
|
|
||||||
inputs.deploy-rs.lib
|
|
||||||
|> mapAttrs (system: deployLib:
|
|
||||||
deployLib.deployChecks deploy);
|
|
||||||
};
|
|
||||||
})
|
|
||||||
|
|
@ -1,96 +0,0 @@
|
||||||
# Copyright 2025-2026 _cry64 (Emile Clark-Boman)
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
{nt, ...}: let
|
|
||||||
inherit
|
|
||||||
(builtins)
|
|
||||||
concatLists
|
|
||||||
elem
|
|
||||||
filter
|
|
||||||
isAttrs
|
|
||||||
mapAttrs
|
|
||||||
pathExists
|
|
||||||
typeOf
|
|
||||||
;
|
|
||||||
|
|
||||||
rootGroupName = "all";
|
|
||||||
|
|
||||||
parseGroupsDecl = groups: let
|
|
||||||
validGroup = g:
|
|
||||||
isAttrs g
|
|
||||||
|| throw ''
|
|
||||||
Cerulean Nexus groups must be provided as attribute sets, got "${typeOf g}" instead!
|
|
||||||
Ensure all the group definitions are attribute sets under your call to `cerulean.mkNexus`.
|
|
||||||
NOTE: Groups can be accessed via `self.groups.PATH.TO.YOUR.GROUP`
|
|
||||||
'';
|
|
||||||
delegate = parent: gName: g: let
|
|
||||||
result =
|
|
||||||
(g
|
|
||||||
// {
|
|
||||||
_name = gName;
|
|
||||||
_parent = parent;
|
|
||||||
})
|
|
||||||
|> mapAttrs (name: value:
|
|
||||||
if elem name ["_name" "_parent"]
|
|
||||||
# ignore metadata fields
|
|
||||||
then value
|
|
||||||
else assert validGroup value; (delegate result name value));
|
|
||||||
in
|
|
||||||
result;
|
|
||||||
in
|
|
||||||
assert validGroup groups;
|
|
||||||
delegate null rootGroupName groups;
|
|
||||||
|
|
||||||
getGroupModules = root: groups:
|
|
||||||
# ensure root group is always added
|
|
||||||
groups
|
|
||||||
# add all inherited groups via _parent
|
|
||||||
|> map (let
|
|
||||||
delegate = g:
|
|
||||||
if g._parent == null
|
|
||||||
then [g]
|
|
||||||
else [g] ++ delegate (g._parent);
|
|
||||||
in
|
|
||||||
delegate)
|
|
||||||
# flatten recursion result
|
|
||||||
|> concatLists
|
|
||||||
# find import location
|
|
||||||
|> map (group: nt.findImport /${root}/groups/${group._name})
|
|
||||||
# filter by uniqueness
|
|
||||||
|> nt.prim.unique
|
|
||||||
# ignore missing groups
|
|
||||||
|> filter pathExists;
|
|
||||||
in {
|
|
||||||
mapNodes = nodes: f:
|
|
||||||
nodes.nodes
|
|
||||||
|> mapAttrs (name: node: let
|
|
||||||
# use per-node base or default to nodes' base
|
|
||||||
base =
|
|
||||||
if node.base != null
|
|
||||||
then node.base
|
|
||||||
else if nodes.base != null
|
|
||||||
then nodes.base
|
|
||||||
else
|
|
||||||
abort ''
|
|
||||||
Cerulean cannot construct nodes node "${name}" without a base package source.
|
|
||||||
Ensure `nodes.nodes.*.base` or `nodes.base` is a flake reference to the github:NixOS/nixpkgs repository.
|
|
||||||
'';
|
|
||||||
in
|
|
||||||
f rec {
|
|
||||||
inherit name node base;
|
|
||||||
inherit (base) lib;
|
|
||||||
|
|
||||||
groups = node.groups (parseGroupsDecl nodes.groups);
|
|
||||||
groupModules = root: getGroupModules root groups;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
@ -1,62 +0,0 @@
|
||||||
# Copyright 2025-2026 _cry64 (Emile Clark-Boman)
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
{
|
|
||||||
lib,
|
|
||||||
specialArgs,
|
|
||||||
...
|
|
||||||
}: {
|
|
||||||
options.nodes = let
|
|
||||||
inherit
|
|
||||||
(lib)
|
|
||||||
mkOption
|
|
||||||
types
|
|
||||||
;
|
|
||||||
in
|
|
||||||
mkOption {
|
|
||||||
description = ''
|
|
||||||
Cerulean node declarations.
|
|
||||||
'';
|
|
||||||
type = types.submoduleWith {
|
|
||||||
inherit specialArgs;
|
|
||||||
|
|
||||||
modules = [
|
|
||||||
{
|
|
||||||
imports = [./shared.nix];
|
|
||||||
|
|
||||||
options = {
|
|
||||||
groups = mkOption {
|
|
||||||
type = types.attrs;
|
|
||||||
default = {};
|
|
||||||
example = lib.literalExpression "{ servers = { staging = {}; production = {}; }; }";
|
|
||||||
description = ''
|
|
||||||
Hierarchical groups that nodes can be a member of.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
nodes = mkOption {
|
|
||||||
type = types.attrsOf (types.submoduleWith {
|
|
||||||
inherit specialArgs;
|
|
||||||
modules = [(import ./submodule.nix)];
|
|
||||||
});
|
|
||||||
# example = { ... }; # TODO
|
|
||||||
description = ''
|
|
||||||
Node (host systems) declarations.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,82 +0,0 @@
|
||||||
# Copyright 2025-2026 _cry64 (Emile Clark-Boman)
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
{lib, ...}: let
|
|
||||||
inherit
|
|
||||||
(lib)
|
|
||||||
mkOption
|
|
||||||
types
|
|
||||||
;
|
|
||||||
|
|
||||||
flakeRef = types.either types.str types.path;
|
|
||||||
in {
|
|
||||||
options = {
|
|
||||||
base = lib.mkOption {
|
|
||||||
# In newer Nix versions, particularly with lazy trees, outPath of
|
|
||||||
# flakes becomes a Nix-language path object. We deliberately allow this
|
|
||||||
# to gracefully come through the interface in discussion with @roberth.
|
|
||||||
#
|
|
||||||
# See: https://github.com/NixOS/nixpkgs/pull/278522#discussion_r1460292639
|
|
||||||
type = types.nullOr flakeRef;
|
|
||||||
|
|
||||||
default = null;
|
|
||||||
defaultText = "if (using nixpkgsFlake.lib.nixosSystem) then self.outPath else null";
|
|
||||||
|
|
||||||
example = lib.literalExpression "inputs.nixpkgs";
|
|
||||||
|
|
||||||
description = ''
|
|
||||||
The path to the nixpkgs source used to build a system. A `base` package set
|
|
||||||
is required to be set, and can be specified via either:
|
|
||||||
1. `options.nodes.base` (default `base` used for all systems)
|
|
||||||
2. `options.nodes.nodes.<name>.base` (takes prescedence over `options.nodes.base`)
|
|
||||||
|
|
||||||
This can also be optionally set if the NixOS system is not built with a flake but still uses
|
|
||||||
pinned sources: set this to the store path for the nixpkgs sources used to build the system,
|
|
||||||
as may be obtained by `fetchTarball`, for example.
|
|
||||||
|
|
||||||
Note: the name of the store path must be "source" due to
|
|
||||||
<https://github.com/NixOS/nix/issues/7075>.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
modules = mkOption {
|
|
||||||
type = types.listOf types.raw;
|
|
||||||
default = [];
|
|
||||||
example = lib.literalExpression "[ { environment.systemPackages = [ pkgs.git ]; } ]";
|
|
||||||
description = ''
|
|
||||||
Shared modules to import; equivalent to the NixOS module system's `extraModules`.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
args = mkOption {
|
|
||||||
type = types.attrs;
|
|
||||||
default = {};
|
|
||||||
example = lib.literalExpression "{ inherit inputs; }";
|
|
||||||
description = ''
|
|
||||||
Shared args to provided for each node; equivalent to the NixOS module system's `specialArgs`.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
homeManager = mkOption {
|
|
||||||
type = types.nullOr flakeRef;
|
|
||||||
default = null;
|
|
||||||
example = lib.literalExpression "inputs.home-manager";
|
|
||||||
description = ''
|
|
||||||
The path to the home-manager source. A `homeManager` flake reference
|
|
||||||
is required to be set for `homes/` to be evaluated, and can be specified via either:
|
|
||||||
1. `options.nodes.homeManager` (default `homManager` used for all systems)
|
|
||||||
2. `options.nodes.nodes.<name>.homeManager` (takes prescedence over `options.nodes.homeManager`)
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,204 +0,0 @@
|
||||||
# Copyright 2025-2026 _cry64 (Emile Clark-Boman)
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
{
|
|
||||||
lib,
|
|
||||||
systems,
|
|
||||||
...
|
|
||||||
}: {
|
|
||||||
imports = [./shared.nix];
|
|
||||||
|
|
||||||
options = let
|
|
||||||
inherit
|
|
||||||
(lib)
|
|
||||||
mkOption
|
|
||||||
types
|
|
||||||
;
|
|
||||||
in {
|
|
||||||
enabled = lib.mkOption {
|
|
||||||
type = types.bool;
|
|
||||||
default = true;
|
|
||||||
example = true;
|
|
||||||
description = ''
|
|
||||||
Whether to enable this node. Nodes are enabled by default.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
system = mkOption {
|
|
||||||
type = types.nullOr (types.enum systems);
|
|
||||||
default = null;
|
|
||||||
example = "x86_64-linux";
|
|
||||||
description = ''
|
|
||||||
The target system architecture to compile for.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
groups = mkOption {
|
|
||||||
# TODO: write a custom group type that validates better than types.attrs lol
|
|
||||||
type = types.functionTo (types.listOf types.attrs);
|
|
||||||
default = groups: [];
|
|
||||||
example = lib.literalExpression "( groups: [ groups.servers groups.secure-boot ] )";
|
|
||||||
description = ''
|
|
||||||
A function from the `groups` hierarchy to a list of groups this node inherits from.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
deploy = {
|
|
||||||
user = mkOption {
|
|
||||||
type = types.str;
|
|
||||||
default = "root";
|
|
||||||
example = "admin";
|
|
||||||
description = ''
|
|
||||||
The user that the system derivation will be built with. The command specified in
|
|
||||||
`<node>.deploy.sudoCmd` will be used if `<node>.deploy.user` is not the
|
|
||||||
same as `<node>.deploy.ssh.user` the same as above).
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
warnNonstandardDeployUser = mkOption {
|
|
||||||
type = types.bool;
|
|
||||||
default = true;
|
|
||||||
example = false;
|
|
||||||
description = ''
|
|
||||||
Disables the warning that shows when `deploy.ssh.user` is set to a non-standard value.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
# sudoCmd = mkOption {
|
|
||||||
# type = types.str;
|
|
||||||
# default = "sudo -u";
|
|
||||||
# example = "doas -u";
|
|
||||||
# description = ''
|
|
||||||
# Which sudo command to use. Must accept at least two arguments:
|
|
||||||
# 1. the user name to execute commands as
|
|
||||||
# 2. the rest is the command to execute
|
|
||||||
# '';
|
|
||||||
# };
|
|
||||||
|
|
||||||
interactiveSudo = mkOption {
|
|
||||||
type = types.bool;
|
|
||||||
default = false;
|
|
||||||
example = false;
|
|
||||||
description = ''
|
|
||||||
Whether to enable interactive sudo (password based sudo).
|
|
||||||
NOT RECOMMENDED. Use one of Cerulean's recommended auth methods instead.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
remoteBuild = mkOption {
|
|
||||||
type = types.bool;
|
|
||||||
default = false;
|
|
||||||
example = false;
|
|
||||||
description = ''
|
|
||||||
Whether to build the system derivation on the target system.
|
|
||||||
Will also fetch all external dependencies from the target system's substituters.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
rollback = mkOption {
|
|
||||||
type = types.bool;
|
|
||||||
default = true;
|
|
||||||
example = true;
|
|
||||||
description = ''
|
|
||||||
Enables both `autoRollback` and `magicRollback`.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
autoRollback = mkOption {
|
|
||||||
type = types.bool;
|
|
||||||
default = true;
|
|
||||||
example = true;
|
|
||||||
description = ''
|
|
||||||
If the previous system derivation should be re-activated if activation fails.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
magicRollback = mkOption {
|
|
||||||
type = types.bool;
|
|
||||||
default = true;
|
|
||||||
example = true;
|
|
||||||
description = ''
|
|
||||||
TODO: im fucking lazy
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
activationTimeout = mkOption {
|
|
||||||
type = types.int;
|
|
||||||
default = 500;
|
|
||||||
example = 30;
|
|
||||||
description = ''
|
|
||||||
Time window in seconds allowed for system derivation activation.
|
|
||||||
If timeout occurs, remote deployment is considered to have failed.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
confirmTimeout = mkOption {
|
|
||||||
type = types.int;
|
|
||||||
default = 30;
|
|
||||||
example = 15;
|
|
||||||
description = ''
|
|
||||||
Time window in seconds allowed for activation confirmation.
|
|
||||||
If timeout occurs, remote deployment is considered to have failed.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
ssh = {
|
|
||||||
host = mkOption {
|
|
||||||
type = types.nullOr types.str;
|
|
||||||
default = null;
|
|
||||||
example = "dobutterfliescry.net";
|
|
||||||
description = ''
|
|
||||||
The host to connect to over ssh during deployment
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
user = mkOption {
|
|
||||||
type = types.str;
|
|
||||||
default = "cerubld";
|
|
||||||
example = "custom-user";
|
|
||||||
description = ''
|
|
||||||
The user to connect to over ssh during deployment.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
port = mkOption {
|
|
||||||
type = types.int;
|
|
||||||
default = 22;
|
|
||||||
example = 2222;
|
|
||||||
description = ''
|
|
||||||
The port to connect to over ssh during deployment.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
publicKeys = mkOption {
|
|
||||||
type = types.listOf types.str;
|
|
||||||
default = [];
|
|
||||||
example = ["ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIeyZuUUmyUYrYaEJwEMvcXqZFYm1NaZab8klOyK6Imr me@puter"];
|
|
||||||
description = ''
|
|
||||||
SSH public keys that will be authorized to the deployment user.
|
|
||||||
This key is intended solely for deployment, allowing for fine-grained permission control.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
opts = mkOption {
|
|
||||||
type = types.listOf types.str;
|
|
||||||
default = [];
|
|
||||||
example = ["-i" "~/.ssh/id_rsa"];
|
|
||||||
description = ''
|
|
||||||
Extra ssh arguments to use during deployment.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
81
flake.lock
generated
81
flake.lock
generated
|
|
@ -3,9 +3,7 @@
|
||||||
"deploy-rs": {
|
"deploy-rs": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-compat": "flake-compat",
|
"flake-compat": "flake-compat",
|
||||||
"nixpkgs": [
|
"nixpkgs": "nixpkgs",
|
||||||
"nixpkgs"
|
|
||||||
],
|
|
||||||
"utils": "utils"
|
"utils": "utils"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
|
|
@ -60,27 +58,6 @@
|
||||||
"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": [
|
||||||
|
|
@ -128,6 +105,38 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"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=",
|
||||||
|
|
@ -143,7 +152,7 @@
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs_2": {
|
"nixpkgs_3": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1767313136,
|
"lastModified": 1767313136,
|
||||||
"narHash": "sha256-16KkgfdYqjaeRGBaYsNrhPRRENs0qzkQVUooNHtoy2w=",
|
"narHash": "sha256-16KkgfdYqjaeRGBaYsNrhPRRENs0qzkQVUooNHtoy2w=",
|
||||||
|
|
@ -162,7 +171,7 @@
|
||||||
"nt": {
|
"nt": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nix-unit": "nix-unit",
|
"nix-unit": "nix-unit",
|
||||||
"nixpkgs": "nixpkgs_2",
|
"nixpkgs": "nixpkgs_3",
|
||||||
"systems": "systems_2"
|
"systems": "systems_2"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
|
|
@ -182,28 +191,12 @@
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"deploy-rs": "deploy-rs",
|
"deploy-rs": "deploy-rs",
|
||||||
"microvm": "microvm",
|
"nixpkgs": "nixpkgs_2",
|
||||||
"nixpkgs": "nixpkgs",
|
"nixpkgs-unstable": "nixpkgs-unstable",
|
||||||
"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,
|
||||||
|
|
|
||||||
13
flake.nix
13
flake.nix
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright 2025-2026 _cry64 (Emile Clark-Boman)
|
# Copyright 2025 Emile Clark-Boman
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
|
@ -23,6 +23,11 @@
|
||||||
|
|
||||||
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";
|
||||||
|
|
@ -32,11 +37,6 @@
|
||||||
url = "github:microvm-nix/microvm.nix";
|
url = "github:microvm-nix/microvm.nix";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
|
|
||||||
sops-nix = {
|
|
||||||
url = "github:Mic92/sops-nix";
|
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = {
|
outputs = {
|
||||||
|
|
@ -48,6 +48,5 @@
|
||||||
{
|
{
|
||||||
inherit inputs self nt;
|
inherit inputs self nt;
|
||||||
inherit (nt) mix;
|
inherit (nt) mix;
|
||||||
systems = import inputs.systems;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue