For either(oneOf) types, we skip introspection as we cannot
determine which branch of the union was taken without more context
This *should* be safe, as it can currently mostly be triggered through
The `extraModules` setting of inventory modules.
Example:
```
importer.roles.default.extraModules = [
../../modules/nixos/common.nix
];
```
Error Message:
```
Traceback (most recent call last):
File "/nix/store/zaz4r4fic03m4whgz46p5jjszzlkq694-clan-cli/lib/python3.13/site-packages/clan_lib/templates/handler.py", line 91, in machine_template
yield dst_machine_dir
File "/nix/store/zaz4r4fic03m4whgz46p5jjszzlkq694-clan-cli/lib/python3.13/site-packages/clan_cli/machines/create.py", line 95, in create_machine
inventory_store.write(inventory, message=f"machine '{machine_name}'")
~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/nix/store/zaz4r4fic03m4whgz46p5jjszzlkq694-clan-cli/lib/python3.13/site-packages/clan_lib/persist/inventory_store.py", line 269, in write
write_info = self._write_map()
File "/nix/store/zaz4r4fic03m4whgz46p5jjszzlkq694-clan-cli/lib/python3.13/site-packages/clan_lib/persist/inventory_store.py", line 214, in _write_map
current_priority = self._get_inventory_current_priority()
File "/nix/store/zaz4r4fic03m4whgz46p5jjszzlkq694-clan-cli/lib/python3.13/site-packages/clan_lib/persist/inventory_store.py", line 206, in _get_inventory_current_priority
return self._flake.select("clanInternals.inventoryClass.introspection")
~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/nix/store/zaz4r4fic03m4whgz46p5jjszzlkq694-clan-cli/lib/python3.13/site-packages/clan_lib/flake/flake.py", line 1129, in select
self.get_from_nix([selector])
~~~~~~~~~~~~~~~~~^^^^^^^^^^^^
File "/nix/store/zaz4r4fic03m4whgz46p5jjszzlkq694-clan-cli/lib/python3.13/site-packages/clan_lib/flake/flake.py", line 1054, in get_from_nix
raise ClanSelectError(
...<3 lines>...
) from e
clan_lib.flake.flake.ClanSelectError: Error on: $ clan select 'clanInternals.inventoryClass.introspection'
Reason: Yet Unsupported type: either
Removing left-over machine directory: /tmp/hyperconfig/machines/trooo
Error on: $ clan select 'clanInternals.inventoryClass.introspection'
Reason: Yet Unsupported type: either
```
Closes: #5387
191 lines
5.6 KiB
Nix
191 lines
5.6 KiB
Nix
{
|
|
lib ? import <nixpkgs/lib>,
|
|
}:
|
|
let
|
|
filterOptions = lib.filterAttrs (
|
|
name: _:
|
|
!builtins.elem name [
|
|
"_module"
|
|
"_freeformOptions"
|
|
]
|
|
);
|
|
|
|
pushPositions = map (
|
|
def:
|
|
lib.mapAttrs (_n: v: {
|
|
inherit (def) file;
|
|
value = v;
|
|
}) def.value
|
|
);
|
|
|
|
unwrapNullOr =
|
|
type:
|
|
let
|
|
typeName = type.name or null;
|
|
in
|
|
if typeName == "nullOr" then type.nestedTypes.name or null else typeName;
|
|
|
|
mergeAttrs =
|
|
{ type, definitionsWithLocations }:
|
|
let
|
|
# Vendored merge from lib.types.attrsOf
|
|
# Because we still cannot access highest prio for the individual attrs yet.
|
|
elemType = type.nestedTypes.elemType;
|
|
mergedAttrs = lib.zipAttrsWith (name: defs: lib.modules.mergeDefinitions ([ name ]) elemType defs) (
|
|
pushPositions definitionsWithLocations
|
|
);
|
|
headType = unwrapNullOr elemType;
|
|
nullable = elemType.name or null == "nullOr";
|
|
total = elemType.name or null == "submodule";
|
|
in
|
|
lib.mapAttrs (_name: merged: {
|
|
__this = {
|
|
prio = merged.defsFinal'.highestPrio;
|
|
files = map (def: def.file) merged.defsFinal'.values;
|
|
inherit
|
|
headType
|
|
nullable
|
|
total
|
|
;
|
|
};
|
|
}) mergedAttrs;
|
|
|
|
/**
|
|
Takes a set of options as returned by `configuration`
|
|
|
|
Returns a recursive structure that contains '__this' along with attribute names that map to the same structure.
|
|
|
|
Within the reserved attribute '__this' the following attributes are available:
|
|
|
|
- prio: The highest priority this option was defined with
|
|
- files: A list of files this option was defined in
|
|
- type: The type of this option (e.g. "string", "attrsOf
|
|
- total: Whether this is a total object. Meaning all attributes are fixed. No additional attributes can be added. Or one of them removed.
|
|
|
|
Example Result:
|
|
{
|
|
foo = {
|
|
__this = { ... };
|
|
bar = {
|
|
__this = { ... };
|
|
};
|
|
baz = {
|
|
__this = { ... };
|
|
};
|
|
};
|
|
}
|
|
*/
|
|
getPrios =
|
|
{
|
|
options,
|
|
}:
|
|
let
|
|
filteredOptions = filterOptions options;
|
|
in
|
|
lib.mapAttrs (
|
|
_: opt:
|
|
let
|
|
headType = unwrapNullOr opt.type;
|
|
nullable = opt.type.name or null == "nullOr";
|
|
total = opt.type.name or null == "submodule";
|
|
|
|
definitionInfo = {
|
|
__this = {
|
|
prio = opt.highestPrio or null;
|
|
files = opt.files or [ ];
|
|
inherit headType nullable total;
|
|
};
|
|
};
|
|
|
|
# TODO: respect freeformType
|
|
submodulePrios = getPrios {
|
|
options =
|
|
filterOptions
|
|
opt.valueMeta.configuration.options or (throw "Please use a newer nixpkgs version >=25.11");
|
|
};
|
|
|
|
/**
|
|
Maps attrsOf and lazyAttrsOf
|
|
*/
|
|
handleAttrsOf =
|
|
type: defs: attrs:
|
|
lib.mapAttrs (
|
|
name: meta:
|
|
(mergeAttrs {
|
|
inherit type;
|
|
definitionsWithLocations = defs;
|
|
}).${name}
|
|
// handleMeta {
|
|
inherit meta;
|
|
definitionsWithLocations =
|
|
(builtins.zipAttrsWith (_name: values: values) (pushPositions defs)).${name};
|
|
}
|
|
) attrs;
|
|
|
|
/**
|
|
Maps attrsOf and lazyAttrsOf
|
|
*/
|
|
handleListOf = list: { __list = lib.map (item: handleMeta { meta = item; }) list; };
|
|
|
|
/**
|
|
Unwraps the valueMeta of an option based on its type
|
|
*/
|
|
handleMeta =
|
|
{
|
|
meta,
|
|
definitionsWithLocations ? [ ],
|
|
}:
|
|
let
|
|
hasType = meta ? _internal.type;
|
|
type = meta._internal.type;
|
|
in
|
|
if !hasType then
|
|
{ }
|
|
else if type.name == "submodule" then
|
|
# TODO: handle types
|
|
getPrios { options = filterOptions meta.configuration.options; }
|
|
else if type.name == "attrsOf" || type.name == "lazyAttrsOf" then
|
|
handleAttrsOf meta._internal.type definitionsWithLocations meta.attrs
|
|
# TODO: Add index support in nixpkgs first
|
|
# else if type.name == "listOf" then
|
|
# handleListOf meta.list
|
|
else if type.name == "either" then
|
|
# For either(oneOf) types, we skip introspection as we cannot
|
|
# determine which branch of the union was taken without more context
|
|
# This *should* be safe, as it can currently mostly be triggered through
|
|
# The `extraModules` setting of inventory modules and seems to be better
|
|
# than just aborting entirely.
|
|
{ }
|
|
else
|
|
throw "Yet Unsupported type: ${type.name}";
|
|
in
|
|
if opt ? type && opt.type.name == "submodule" then
|
|
(definitionInfo) // submodulePrios
|
|
else if opt ? type && (opt.type.name == "attrsOf" || opt.type.name == "lazyAttrsOf") then
|
|
definitionInfo // (handleAttrsOf opt.type opt.definitionsWithLocations opt.valueMeta.attrs)
|
|
# TODO: Add index support in nixpkgs, otherwise we cannot
|
|
else if opt ? type && (opt.type.name == "listOf") then
|
|
definitionInfo // (handleListOf opt.valueMeta.list)
|
|
else if opt ? type && opt._type == "option" then
|
|
definitionInfo
|
|
else
|
|
getPrios { options = opt; }
|
|
) filteredOptions;
|
|
|
|
getPriosLegacy = import ./getPriosLegacy.nix { inherit lib; };
|
|
|
|
wrap =
|
|
{
|
|
options,
|
|
}:
|
|
# Test _module.check for valueMeta
|
|
# This option should always exist if options comes from a module evaluation
|
|
if options._module.check ? valueMeta then
|
|
getPrios { inherit options; }
|
|
else
|
|
getPriosLegacy { inherit options; };
|
|
in
|
|
{
|
|
getPrios = wrap;
|
|
}
|