build-clan: support constructing darwinConfigurations

This commit is contained in:
Michael Hoang
2025-03-25 13:39:04 +09:00
parent 66e4c41142
commit 6de4735c81
11 changed files with 207 additions and 57 deletions

View File

@@ -34,7 +34,7 @@ in
type = types.submoduleWith { type = types.submoduleWith {
specialArgs = { specialArgs = {
inherit clan-core self; inherit clan-core self;
inherit (inputs) nixpkgs; inherit (inputs) nixpkgs nix-darwin;
# TODO: inject the inventory interface # TODO: inject the inventory interface
# inventoryInterface = {}; # inventoryInterface = {};
}; };

View File

@@ -4,6 +4,7 @@
{ {
lib, lib,
nixpkgs, nixpkgs,
nix-darwin ? null,
... ...
}: }:
{ {
@@ -54,6 +55,7 @@
inherit inherit
lib lib
nixpkgs nixpkgs
nix-darwin
clan-core clan-core
self self
; ;

View File

@@ -1,6 +1,7 @@
{ {
lib, lib,
nixpkgs, nixpkgs,
nix-darwin ? null,
clan-core, clan-core,
self, self,
specialArgs ? { }, specialArgs ? { },
@@ -9,7 +10,12 @@
module: module:
(lib.evalModules { (lib.evalModules {
specialArgs = { specialArgs = {
inherit self clan-core nixpkgs; inherit
self
clan-core
nixpkgs
nix-darwin
;
}; };
modules = [ modules = [
./interface.nix ./interface.nix

View File

@@ -123,6 +123,15 @@ in
}; };
# Outputs # Outputs
darwinConfigurations = lib.mkOption {
# Hide from documentation.
# Exposed at the top-level of the flake, clan.darwinConfigurations should not used by the user.
# Instead, the user should use the `.#darwinConfigurations` attribute of the flake output.
visible = false;
type = types.lazyAttrsOf types.raw;
default = { };
};
nixosConfigurations = lib.mkOption { nixosConfigurations = lib.mkOption {
# Hide from documentation. # Hide from documentation.
# Exposed at the top-level of the flake, clan.nixosConfigurations should not used by the user. # Exposed at the top-level of the flake, clan.nixosConfigurations should not used by the user.

View File

@@ -3,6 +3,7 @@
config, config,
clan-core, clan-core,
nixpkgs, nixpkgs,
nix-darwin,
lib, lib,
... ...
}: }:
@@ -48,8 +49,9 @@ let
} }
); );
moduleSystemConstructor = {
# TODO: remove default system once we have a hardware-config mechanism # TODO: remove default system once we have a hardware-config mechanism
nixosConfiguration = nixos =
{ {
system ? null, system ? null,
name, name,
@@ -114,11 +116,66 @@ let
} // specialArgs; } // specialArgs;
}; };
darwin =
{
system ? null,
name,
pkgs ? null,
extraConfig ? { },
}:
nix-darwin.lib.darwinSystem {
modules = [
{
imports = builtins.filter builtins.pathExists [
"${directory}/machines/${name}/configuration.nix"
];
}
(
if !lib.hasAttrByPath [ "darwinModules" "clanCore" ] clan-core then
{ }
else
throw "this should import clan-core.darwinModules.clanCore"
)
extraConfig
(machines.${name} or { })
# TODO: import inventory when it has support for defining `nix-darwin` modules
(
{
# TODO: set clan-core settings when clan-core has support for `nix-darwin`
networking.hostName = lib.mkDefault name;
# For vars we need to override the system so we run vars
# generators on the machine that runs `clan vars generate`. If a
# users is using the `pkgsForSystem`, we don't set
# nixpkgs.hostPlatform it would conflict with the `nixpkgs.pkgs`
# option.
nixpkgs.hostPlatform = lib.mkIf (system != null && (pkgsForSystem system) != null) (
lib.mkForce system
);
}
// lib.optionalAttrs (pkgs != null) { nixpkgs.pkgs = lib.mkForce pkgs; }
)
];
specialArgs = {
inherit clan-core;
} // specialArgs;
};
};
allMachines = inventory.machines or { } // machines; allMachines = inventory.machines or { } // machines;
nixosConfigurations = lib.mapAttrs (name: _: nixosConfiguration { inherit name; }) allMachines; machineClass = lib.mapAttrs (name: _: inventory.machineClass.${name} or "nixos") allMachines;
# This instantiates nixos for each system that we support: configurations = lib.mapAttrs (
name: _: moduleSystemConstructor.${machineClass.${name}} { inherit name; }
) allMachines;
nixosConfigurations = lib.filterAttrs (name: _: machineClass.${name} == "nixos") configurations;
darwinConfigurations = lib.filterAttrs (name: _: machineClass.${name} == "darwin") configurations;
# This instantiates NixOS for each system that we support:
# configPerSystem = <system>.<machine>.nixosConfiguration # configPerSystem = <system>.<machine>.nixosConfiguration
# We need this to build nixos secret generators for each system # We need this to build nixos secret generators for each system
configsPerSystem = builtins.listToAttrs ( configsPerSystem = builtins.listToAttrs (
@@ -127,7 +184,7 @@ let
lib.nameValuePair system ( lib.nameValuePair system (
lib.mapAttrs ( lib.mapAttrs (
name: _: name: _:
nixosConfiguration { moduleSystemConstructor.${machineClass.${name}} {
inherit name system; inherit name system;
pkgs = pkgsFor.${system}; pkgs = pkgsFor.${system};
} }
@@ -142,7 +199,7 @@ let
lib.nameValuePair system ( lib.nameValuePair system (
lib.mapAttrs ( lib.mapAttrs (
name: _: args: name: _: args:
nixosConfiguration ( moduleSystemConstructor.${machineClass.${name}} (
args args
// { // {
inherit name system; inherit name system;
@@ -194,6 +251,7 @@ in
]; ];
inherit nixosConfigurations; inherit nixosConfigurations;
inherit darwinConfigurations;
clanInternals = { clanInternals = {
moduleSchemas = clan-core.lib.modules.getModulesSchema config.inventory.modules; moduleSchemas = clan-core.lib.modules.getModulesSchema config.inventory.modules;
@@ -223,11 +281,17 @@ in
# machine specifics # machine specifics
machines = configsPerSystem; machines = configsPerSystem;
machinesFunc = configsFuncPerSystem; machinesFunc = configsFuncPerSystem;
all-machines-json = lib.mapAttrs ( all-machines-json =
if !lib.hasAttrByPath [ "darwinModules" "clanCore" ] clan-core then
lib.mapAttrs (
system: configs: system: configs:
nixpkgs.legacyPackages.${system}.writers.writeJSON "machines.json" ( nixpkgs.legacyPackages.${system}.writers.writeJSON "machines.json" (
lib.mapAttrs (_: m: m.config.system.clan.deployment.data) configs lib.mapAttrs (_: m: m.config.system.clan.deployment.data) (
lib.filterAttrs (_n: v: v.class == "nixos") configs
) )
) configsPerSystem; )
) configsPerSystem
else
throw "remove NixOS filter and support nix-darwin as well";
}; };
} }

View File

@@ -14,5 +14,6 @@
topLevel = [ topLevel = [
"clanInternals" "clanInternals"
"nixosConfigurations" "nixosConfigurations"
"darwinConfigurations"
]; ];
} }

View File

@@ -76,7 +76,7 @@ in
}; };
directory = ./.; directory = ./.;
imports = [ imports = [
# What the user needs to specif # What the user needs to specify
{ {
directory = ./.; directory = ./.;
inventory.meta.name = "test"; inventory.meta.name = "test";
@@ -165,10 +165,9 @@ in
}; };
directory = ./.; directory = ./.;
meta.name = "test"; meta.name = "test";
inventory.machines.machine1.meta.name = "machine1";
inventory.machines.machine1 = { };
machines.machine2 = { }; machines.machine2 = { };
}; };
in in
{ {
@@ -200,4 +199,55 @@ in
expr = result.nixosConfigurations.machine2.config.networking.hostName; expr = result.nixosConfigurations.machine2.config.networking.hostName;
expected = "dream2nix"; expected = "dream2nix";
}; };
test_buildClan_darwin_machines =
let
result = buildClan {
self = {
inputs = { };
};
directory = ./.;
meta.name = "test";
machines.machine1 = { };
machines.machine2 = { };
machines.machine3 = { };
inventory.machineClass.machine2 = "darwin";
inventory.machineClass.machine3 = "nixos";
};
in
{
expr = {
nixos = builtins.attrNames result.nixosConfigurations;
darwin = builtins.attrNames result.darwinConfigurations;
};
expected = {
nixos = [
"machine1"
"machine3"
];
darwin = [ "machine2" ];
};
};
test_buildClan_all_machines_laziness =
let
result = buildClan {
self = {
inputs = { };
};
directory = ./.;
meta.name = "test";
machines.machine1.non_existent_option = throw "eval error";
inventory.machines.machine1.other_non_existent_option = throw "different eval error";
};
in
{
expr = builtins.attrNames result.nixosConfigurations;
expected = [
"machine1"
];
};
} }

View File

@@ -2,6 +2,7 @@
lib, lib,
self, self,
nixpkgs, nixpkgs,
nix-darwin ? null,
... ...
}: }:
# Produces the # Produces the
@@ -20,7 +21,7 @@ lib.fix (clanLib: {
clan-core = self; clan-core = self;
pkgs = nixpkgs.legacyPackages.x86_64-linux; pkgs = nixpkgs.legacyPackages.x86_64-linux;
}; };
buildClanModule = clanLib.callLib ./build-clan { inherit nixpkgs; }; buildClanModule = clanLib.callLib ./build-clan { inherit nixpkgs nix-darwin; };
buildClan = clanLib.buildClanModule.buildClanWith { clan-core = self; }; buildClan = clanLib.buildClanModule.buildClanWith { clan-core = self; };
# ------------------------------------ # ------------------------------------

View File

@@ -15,7 +15,7 @@ rec {
]; ];
flake.clanLib = import ./default.nix { flake.clanLib = import ./default.nix {
inherit lib inputs self; inherit lib inputs self;
inherit (inputs) nixpkgs; inherit (inputs) nixpkgs nix-darwin;
}; };
# TODO: remove this legacy alias # TODO: remove this legacy alias
flake.lib = flake.clanLib; flake.lib = flake.clanLib;

View File

@@ -277,6 +277,22 @@ in
) )
); );
}; };
machineClass = lib.mkOption {
default = { };
type = types.attrsOf (
types.enum [
"nixos"
"darwin"
]
);
description = ''
The module system that should be used to construct the machine
Set this to `darwin` for macOS machines
'';
};
instances = lib.mkOption { instances = lib.mkOption {
# Keep as internal until all de-/serialization issues are resolved # Keep as internal until all de-/serialization issues are resolved
visible = false; visible = false;

View File

@@ -29,6 +29,7 @@ Service = dict[str, Any]
class Inventory(TypedDict): class Inventory(TypedDict):
machineClass: NotRequired[dict[str, Any]]
machines: NotRequired[dict[str, Machine]] machines: NotRequired[dict[str, Machine]]
meta: NotRequired[Meta] meta: NotRequired[Meta]
modules: NotRequired[dict[str, Any]] modules: NotRequired[dict[str, Any]]