Merge pull request 'build-clan: support constructing darwinConfigurations' (#3115) from mac into main

Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/3115
This commit is contained in:
Michael Hoang
2025-04-08 07:44:09 +00:00
27 changed files with 347 additions and 69 deletions

21
flake.lock generated
View File

@@ -69,6 +69,26 @@
"type": "github" "type": "github"
} }
}, },
"nix-darwin": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1743496612,
"narHash": "sha256-emPWa5lmKbnyuj8c1mSJUkzJNT+iJoU9GMcXwjp2oVM=",
"owner": "LnL7",
"repo": "nix-darwin",
"rev": "73d59580d01e9b9f957ba749f336a272869c42dd",
"type": "github"
},
"original": {
"owner": "LnL7",
"repo": "nix-darwin",
"type": "github"
}
},
"nixos-facter-modules": { "nixos-facter-modules": {
"locked": { "locked": {
"lastModified": 1743671943, "lastModified": 1743671943,
@@ -102,6 +122,7 @@
"data-mesher": "data-mesher", "data-mesher": "data-mesher",
"disko": "disko", "disko": "disko",
"flake-parts": "flake-parts", "flake-parts": "flake-parts",
"nix-darwin": "nix-darwin",
"nixos-facter-modules": "nixos-facter-modules", "nixos-facter-modules": "nixos-facter-modules",
"nixpkgs": "nixpkgs", "nixpkgs": "nixpkgs",
"sops-nix": "sops-nix", "sops-nix": "sops-nix",

View File

@@ -4,6 +4,9 @@
inputs = { inputs = {
nixpkgs.url = "https://nixos.org/channels/nixpkgs-unstable/nixexprs.tar.xz"; nixpkgs.url = "https://nixos.org/channels/nixpkgs-unstable/nixexprs.tar.xz";
nix-darwin.url = "github:LnL7/nix-darwin";
nix-darwin.inputs.nixpkgs.follows = "nixpkgs";
flake-parts.url = "github:hercules-ci/flake-parts"; flake-parts.url = "github:hercules-ci/flake-parts";
flake-parts.inputs.nixpkgs-lib.follows = "nixpkgs"; flake-parts.inputs.nixpkgs-lib.follows = "nixpkgs";

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

@@ -37,6 +37,7 @@ in
nix-unit --eval-store "$HOME" \ nix-unit --eval-store "$HOME" \
--extra-experimental-features flakes \ --extra-experimental-features flakes \
--show-trace \
${inputOverrides} \ ${inputOverrides} \
--flake ${ --flake ${
self.filter { self.filter {

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

@@ -23,6 +23,7 @@ in
nix-unit --eval-store "$HOME" \ nix-unit --eval-store "$HOME" \
--extra-experimental-features flakes \ --extra-experimental-features flakes \
--show-trace \
${inputOverrides} \ ${inputOverrides} \
--flake ${ --flake ${
self.filter { self.filter {

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

@@ -24,6 +24,7 @@ in
export HOME="$(realpath .)" export HOME="$(realpath .)"
nix-unit --eval-store "$HOME" \ nix-unit --eval-store "$HOME" \
--extra-experimental-features flakes \ --extra-experimental-features flakes \
--show-trace \
${inputOverrides} \ ${inputOverrides} \
--flake ${self}#legacyPackages.${system}.evalTests-distributedServices --flake ${self}#legacyPackages.${system}.evalTests-distributedServices

View File

@@ -48,6 +48,7 @@ in
export NIX_ABORT_ON_WARN=1 export NIX_ABORT_ON_WARN=1
nix-unit --eval-store "$HOME" \ nix-unit --eval-store "$HOME" \
--extra-experimental-features flakes \ --extra-experimental-features flakes \
--show-trace \
${inputOverrides} \ ${inputOverrides} \
--flake ${ --flake ${
self.filter { self.filter {

View File

@@ -309,7 +309,13 @@ rec {
option.type.name == "enum" option.type.name == "enum"
# return jsonschema property definition for enum # return jsonschema property definition for enum
then then
exposedModuleInfo // default // example // description // { enum = option.type.functor.payload; } exposedModuleInfo
// default
// example
// description
// {
enum = option.type.functor.payload.values;
}
# parse listOf submodule # parse listOf submodule
else if else if
option.type.name == "listOf" && option.type.nestedTypes.elemType.name == "submodule" option.type.name == "listOf" && option.type.nestedTypes.elemType.name == "submodule"

View File

@@ -50,6 +50,16 @@
]; ];
description = "A list of enabled kernel modules"; description = "A list of enabled kernel modules";
}; };
# enum
colour = lib.mkOption {
type = lib.types.enum [
"red"
"blue"
"green"
];
default = "red";
description = "The colour of the user";
};
destinations = lib.mkOption { destinations = lib.mkOption {
type = lib.types.attrsOf ( type = lib.types.attrsOf (
lib.types.submodule ( lib.types.submodule (

View File

@@ -46,6 +46,12 @@
}, },
"description": "Some attributes" "description": "Some attributes"
}, },
"colour": {
"$exportedModuleInfo": { "path": ["colour"] },
"default": "red",
"description": "The colour of the user",
"enum": ["red", "blue", "green"]
},
"services": { "services": {
"$exportedModuleInfo": { "path": ["services"] }, "$exportedModuleInfo": { "path": ["services"] },
"type": "object", "type": "object",

View File

@@ -23,7 +23,8 @@
export NIX_PATH=nixpkgs=${pkgs.path} export NIX_PATH=nixpkgs=${pkgs.path}
${pkgs.nix-unit}/bin/nix-unit \ ${pkgs.nix-unit}/bin/nix-unit \
${./.}/test.nix \ ${./.}/test.nix \
--eval-store $(realpath .) --eval-store $(realpath .) \
--show-trace
touch $out touch $out
''; '';
}; };

View File

@@ -1,7 +1,14 @@
{ {
"_module.args": {
"declarations": ["lib/modules.nix"],
"description": "Additional arguments passed to each module in addition to ones\nlike `lib`, `config`,\nand `pkgs`, `modulesPath`.\n\nThis option is also available to all submodules. Submodules do not\ninherit args from their parent module, nor do they provide args to\ntheir parent module or sibling submodules. The sole exception to\nthis is the argument `name` which is provided by\nparent modules to a submodule and contains the attribute name\nthe submodule is bound to, or a unique generated name if it is\nnot bound to an attribute.\n\nSome arguments are already passed by default, of which the\nfollowing *cannot* be changed with this option:\n- {var}`lib`: The nixpkgs library.\n- {var}`config`: The results of all options after merging the values from all modules together.\n- {var}`options`: The options declared in all modules.\n- {var}`specialArgs`: The `specialArgs` argument passed to `evalModules`.\n- All attributes of {var}`specialArgs`\n\n Whereas option values can generally depend on other option values\n thanks to laziness, this does not apply to `imports`, which\n must be computed statically before anything else.\n\n For this reason, callers of the module system can provide `specialArgs`\n which are available during import resolution.\n\n For NixOS, `specialArgs` includes\n {var}`modulesPath`, which allows you to import\n extra modules from the nixpkgs package tree without having to\n somehow make the module aware of the location of the\n `nixpkgs` or NixOS directories.\n ```\n { modulesPath, ... }: {\n imports = [\n (modulesPath + \"/profiles/minimal.nix\")\n ];\n }\n ```\n\nFor NixOS, the default value for this option includes at least this argument:\n- {var}`pkgs`: The nixpkgs package set according to\n the {option}`nixpkgs.pkgs` option.\n",
"loc": ["_module", "args"],
"readOnly": false,
"type": "lazy attribute set of raw value"
},
"age": { "age": {
"declarations": [ "declarations": [
"/home/grmpf/synced/projects/clan/clan-core/lib/jsonschema/example-interface.nix" "/Users/enzime/Work/clan/clan-core/lib/jsonschema/example-interface.nix"
], ],
"default": { "default": {
"_type": "literalExpression", "_type": "literalExpression",
@@ -12,9 +19,57 @@
"readOnly": false, "readOnly": false,
"type": "signed integer" "type": "signed integer"
}, },
"colour": {
"declarations": [
"/Users/enzime/Work/clan/clan-core/lib/jsonschema/example-interface.nix"
],
"default": {
"_type": "literalExpression",
"text": "\"red\""
},
"description": "The colour of the user",
"loc": ["colour"],
"readOnly": false,
"type": "one of \"red\", \"blue\", \"green\""
},
"destinations": {
"declarations": [
"/Users/enzime/Work/clan/clan-core/lib/jsonschema/example-interface.nix"
],
"default": {
"_type": "literalExpression",
"text": "{ }"
},
"description": null,
"loc": ["destinations"],
"readOnly": false,
"type": "attribute set of (submodule)"
},
"destinations.<name>.name": {
"declarations": [
"/Users/enzime/Work/clan/clan-core/lib/jsonschema/example-interface.nix"
],
"default": {
"_type": "literalExpression",
"text": "\"name\""
},
"description": "the name of the backup job",
"loc": ["destinations", "<name>", "name"],
"readOnly": false,
"type": "string matching the pattern ^[a-zA-Z0-9._-]+$"
},
"destinations.<name>.repo": {
"declarations": [
"/Users/enzime/Work/clan/clan-core/lib/jsonschema/example-interface.nix"
],
"description": "the borgbackup repository to backup to",
"loc": ["destinations", "<name>", "repo"],
"readOnly": false,
"type": "string"
},
"isAdmin": { "isAdmin": {
"declarations": [ "declarations": [
"/home/grmpf/synced/projects/clan/clan-core/lib/jsonschema/example-interface.nix" "/Users/enzime/Work/clan/clan-core/lib/jsonschema/example-interface.nix"
], ],
"default": { "default": {
"_type": "literalExpression", "_type": "literalExpression",
@@ -27,7 +82,7 @@
}, },
"kernelModules": { "kernelModules": {
"declarations": [ "declarations": [
"/home/grmpf/synced/projects/clan/clan-core/lib/jsonschema/example-interface.nix" "/Users/enzime/Work/clan/clan-core/lib/jsonschema/example-interface.nix"
], ],
"default": { "default": {
"_type": "literalExpression", "_type": "literalExpression",
@@ -40,7 +95,7 @@
}, },
"name": { "name": {
"declarations": [ "declarations": [
"/home/grmpf/synced/projects/clan/clan-core/lib/jsonschema/example-interface.nix" "/Users/enzime/Work/clan/clan-core/lib/jsonschema/example-interface.nix"
], ],
"default": { "default": {
"_type": "literalExpression", "_type": "literalExpression",
@@ -53,7 +108,7 @@
}, },
"services": { "services": {
"declarations": [ "declarations": [
"/home/grmpf/synced/projects/clan/clan-core/lib/jsonschema/example-interface.nix" "/Users/enzime/Work/clan/clan-core/lib/jsonschema/example-interface.nix"
], ],
"description": null, "description": null,
"loc": ["services"], "loc": ["services"],
@@ -62,7 +117,7 @@
}, },
"services.opt": { "services.opt": {
"declarations": [ "declarations": [
"/home/grmpf/synced/projects/clan/clan-core/lib/jsonschema/example-interface.nix" "/Users/enzime/Work/clan/clan-core/lib/jsonschema/example-interface.nix"
], ],
"default": { "default": {
"_type": "literalExpression", "_type": "literalExpression",
@@ -75,7 +130,7 @@
}, },
"userIds": { "userIds": {
"declarations": [ "declarations": [
"/home/grmpf/synced/projects/clan/clan-core/lib/jsonschema/example-interface.nix" "/Users/enzime/Work/clan/clan-core/lib/jsonschema/example-interface.nix"
], ],
"default": { "default": {
"_type": "literalExpression", "_type": "literalExpression",

View File

@@ -4,6 +4,6 @@
slib ? (import ./. { inherit lib; } { }), slib ? (import ./. { inherit lib; } { }),
}: }:
{ {
# parseOption = import ./test_parseOption.nix { inherit lib slib; }; parseOption = import ./test_parseOption.nix { inherit lib slib; };
parseOptions = import ./test_parseOptions.nix { inherit lib slib; }; parseOptions = import ./test_parseOptions.nix { inherit lib slib; };
} }

View File

@@ -25,6 +25,7 @@ in
export NIX_ABORT_ON_WARN=1 export NIX_ABORT_ON_WARN=1
nix-unit --eval-store "$HOME" \ nix-unit --eval-store "$HOME" \
--extra-experimental-features flakes \ --extra-experimental-features flakes \
--show-trace \
${inputOverrides} \ ${inputOverrides} \
--flake ${ --flake ${
self.filter { self.filter {

View File

@@ -23,6 +23,7 @@ in
nix-unit --eval-store "$HOME" \ nix-unit --eval-store "$HOME" \
--extra-experimental-features flakes \ --extra-experimental-features flakes \
--show-trace \
${inputOverrides} \ ${inputOverrides} \
--flake ${ --flake ${
self.filter { self.filter {

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]]

View File

@@ -1,12 +1,13 @@
import importlib import importlib
import json import json
import logging import logging
import re
from dataclasses import dataclass, field from dataclasses import dataclass, field
from functools import cached_property from functools import cached_property
from pathlib import Path from pathlib import Path
from typing import TYPE_CHECKING, Any, Literal from typing import TYPE_CHECKING, Any, Literal
from clan_cli.errors import ClanError from clan_cli.errors import ClanCmdError, ClanError
from clan_cli.facts import public_modules as facts_public_modules from clan_cli.facts import public_modules as facts_public_modules
from clan_cli.facts import secret_modules as facts_secret_modules from clan_cli.facts import secret_modules as facts_secret_modules
from clan_cli.flake import Flake from clan_cli.flake import Flake
@@ -56,10 +57,22 @@ class Machine:
kwargs.update({"extra": {"command_prefix": self.name}}) kwargs.update({"extra": {"command_prefix": self.name}})
log.error(msg, *args, **kwargs) log.error(msg, *args, **kwargs)
@property
# `class` is a keyword, `_class` triggers `SLF001` so we use a sunder name
def _class_(self) -> str:
try:
return self.flake.select(
f"clanInternals.inventory.machineClass.{self.name}"
)
except ClanCmdError as e:
if re.search(f"error: attribute '{self.name}' missing", e.cmd.stderr):
return "nixos"
raise
@property @property
def system(self) -> str: def system(self) -> str:
return self.flake.select( return self.flake.select(
f"nixosConfigurations.{self.name}.pkgs.hostPlatform.system" f"{self._class_}Configurations.{self.name}.pkgs.hostPlatform.system"
) )
@property @property

View File

@@ -238,6 +238,8 @@ def update_command(args: argparse.Namespace) -> None:
if len(args.machines) == 0: if len(args.machines) == 0:
ignored_machines = [] ignored_machines = []
for machine in get_all_machines(args.flake, args.option): for machine in get_all_machines(args.flake, args.option):
if machine._class_ == "darwin":
continue
if machine.deployment.get("requireExplicitUpdate", False): if machine.deployment.get("requireExplicitUpdate", False):
continue continue
try: try:
@@ -265,6 +267,11 @@ def update_command(args: argparse.Namespace) -> None:
machine.override_build_host = args.build_host machine.override_build_host = args.build_host
machine.host_key_check = HostKeyCheck.from_str(args.host_key_check) machine.host_key_check = HostKeyCheck.from_str(args.host_key_check)
for machine in machines:
if machine._class_ == "darwin":
machine.error("Updating macOS machines is not yet supported")
sys.exit(1)
deploy_machines(machines) deploy_machines(machines)
except KeyboardInterrupt: except KeyboardInterrupt:
log.warning("Interrupted by user") log.warning("Interrupted by user")