build-clan: support constructing darwinConfigurations

This commit is contained in:
Michael Hoang
2025-03-25 13:39:04 +09:00
parent c2eaf74df5
commit b5a57a72a0
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,51 +49,99 @@ let
} }
); );
# TODO: remove default system once we have a hardware-config mechanism moduleSystemConstructor = {
nixosConfiguration = # TODO: remove default system once we have a hardware-config mechanism
{ nixos =
system ? null, {
name, system ? null,
pkgs ? null, name,
extraConfig ? { }, pkgs ? null,
}: extraConfig ? { },
nixpkgs.lib.nixosSystem { }:
modules = nixpkgs.lib.nixosSystem {
let modules =
hwConfig = "${directory}/machines/${name}/hardware-configuration.nix"; let
diskoConfig = "${directory}/machines/${name}/disko.nix"; hwConfig = "${directory}/machines/${name}/hardware-configuration.nix";
in diskoConfig = "${directory}/machines/${name}/disko.nix";
[ in
[
{
# Autoinclude configuration.nix and hardware-configuration.nix
imports = builtins.filter builtins.pathExists [
"${directory}/machines/${name}/configuration.nix"
hwConfig
diskoConfig
];
}
clan-core.nixosModules.clanCore
extraConfig
(machines.${name} or { })
# Inherit the inventory assertions ?
# { inherit (mergedInventory) assertions; }
{ imports = inventoryClass.machines.${name}.machineImports or [ ]; }
# Import the distribute services
{ imports = config.clanInternals.distributedServices.allMachines.${name} or [ ]; }
(
{
# Settings
clan.core.settings = {
inherit directory;
inherit (config.inventory.meta) name icon;
machine = {
inherit name;
};
};
# Inherited from clan wide settings
# TODO: remove these
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;
};
darwin =
{
system ? null,
name,
pkgs ? null,
extraConfig ? { },
}:
nix-darwin.lib.darwinSystem {
modules = [
{ {
# Autoinclude configuration.nix and hardware-configuration.nix
imports = builtins.filter builtins.pathExists [ imports = builtins.filter builtins.pathExists [
"${directory}/machines/${name}/configuration.nix" "${directory}/machines/${name}/configuration.nix"
hwConfig
diskoConfig
]; ];
} }
clan-core.nixosModules.clanCore (
if !lib.hasAttrByPath [ "darwinModules" "clanCore" ] clan-core then
{ }
else
throw "this should import clan-core.darwinModules.clanCore"
)
extraConfig extraConfig
(machines.${name} or { }) (machines.${name} or { })
# Inherit the inventory assertions ? # TODO: import inventory when it has support for defining `nix-darwin` modules
# { inherit (mergedInventory) assertions; }
{ imports = inventoryClass.machines.${name}.machineImports or [ ]; }
# Import the distribute services
{ imports = config.clanInternals.distributedServices.allMachines.${name} or [ ]; }
( (
{ {
# Settings # TODO: set clan-core settings when clan-core has support for `nix-darwin`
clan.core.settings = {
inherit directory;
inherit (config.inventory.meta) name icon;
machine = {
inherit name;
};
};
# Inherited from clan wide settings
# TODO: remove these
networking.hostName = lib.mkDefault name; networking.hostName = lib.mkDefault name;
@@ -109,16 +158,24 @@ let
) )
]; ];
specialArgs = { specialArgs = {
inherit clan-core; inherit clan-core;
} // specialArgs; } // 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 =
system: configs: if !lib.hasAttrByPath [ "darwinModules" "clanCore" ] clan-core then
nixpkgs.legacyPackages.${system}.writers.writeJSON "machines.json" ( lib.mapAttrs (
lib.mapAttrs (_: m: m.config.system.clan.deployment.data) configs system: configs:
) nixpkgs.legacyPackages.${system}.writers.writeJSON "machines.json" (
) configsPerSystem; lib.mapAttrs (_: m: m.config.system.clan.deployment.data) (
lib.filterAttrs (_n: v: v.class == "nixos") configs
)
)
) 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]]