diff --git a/cerulean/nexus/snow.nix b/cerulean/nexus/snow.nix deleted file mode 100644 index 736779f..0000000 --- a/cerulean/nexus/snow.nix +++ /dev/null @@ -1,108 +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. -{ - 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 = { - }; -} diff --git a/cerulean/snow/default.nix b/cerulean/snow/default.nix new file mode 100644 index 0000000..1fe87f2 --- /dev/null +++ b/cerulean/snow/default.nix @@ -0,0 +1,18 @@ +# 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. +{...}: { + imports = [ + ./nodes + ]; +} diff --git a/cerulean/snow/nodes/default.nix b/cerulean/snow/nodes/default.nix new file mode 100644 index 0000000..c458a41 --- /dev/null +++ b/cerulean/snow/nodes/default.nix @@ -0,0 +1,49 @@ +# 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, ...}: { + options.nodes = let + inherit + (lib) + mkOption + types + ; + in + mkOption { + description = '' + Cerulean node declarations. + ''; + type = types.submoduleWith { + 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 (import ./submodule.nix)); + # example = { ... }; # TODO + description = '' + Node (host systems) declarations. + ''; + }; + }; + }; + }; +} diff --git a/cerulean/snow/nodes/shared.nix b/cerulean/snow/nodes/shared.nix new file mode 100644 index 0000000..c840d22 --- /dev/null +++ b/cerulean/snow/nodes/shared.nix @@ -0,0 +1,82 @@ +# 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..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 + . + ''; + }; + + 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..homeManager` (takes prescedence over `options.nodes.homeManager`) + ''; + }; + }; +} diff --git a/cerulean/snow/nodes/submodule.nix b/cerulean/snow/nodes/submodule.nix new file mode 100644 index 0000000..5d7ffe6 --- /dev/null +++ b/cerulean/snow/nodes/submodule.nix @@ -0,0 +1,166 @@ +# 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. +{ + inputs, + lib, + ... +}: { + 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 = types.nullOr mkOption { + type = types.enum inputs.systems; + default = null; + example = "x86_64-linux"; + description = '' + The target system architecture to compile for. + ''; + }; + + groups = mkOption { + type = types.functionTo types.list; + default = []; + 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 deployed to. The command specified in + `.deploy.sudoCmd` will be used if `.deploy.user` is not the + same as `.deploy.ssh.user` the same as above). + ''; + }; + + 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. + ''; + }; + + autoRollback = mkOption { + type = types.bool; + default = true; + example = true; + description = '' + If the previous system derivation should be re-activated if activation fails. + ''; + }; + + 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.str; + default = ""; + 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. + ''; + }; + + opts = mkOption { + type = types.listOf types.str; + default = []; + example = ["-i" "~/.ssh/id_rsa"]; + description = '' + Extra ssh arguments to use during deployment. + ''; + }; + }; + }; + }; +}