WIP: dont merge

This commit is contained in:
Johannes Kirschbauer
2025-10-03 11:36:23 +02:00
parent 1465b18820
commit 1f13ce2732
4 changed files with 254 additions and 24 deletions

View File

@@ -1,7 +1,8 @@
# tests for the nixos options to jsonschema converter # tests for the nixos options to jsonschema converter
# run these tests via `nix-unit ./test.nix` # run these tests via `nix-unit ./test.nix`
{ {
lib ? (import <nixpkgs> { }).lib, lib ? import /home/johannes/git/nixpkgs/lib,
# lib ? (import <nixpkgs> { }).lib,
slib ? (import ./. { inherit lib; }), slib ? (import ./. { inherit lib; }),
}: }:
let let
@@ -67,17 +68,24 @@ in
}; };
}; };
}; };
test_no_default = { test_no_default =
expr = stableView ( let
slib.getPrios {
options = configuration = (
(eval [ eval [
{ {
options.foo.bar = lib.mkOption { options.foo.bar = lib.mkOption {
type = lib.types.bool; type = lib.types.bool;
}; };
} }
]).options; ]
);
in
{
inherit configuration;
expr = stableView (
slib.getPrios {
options = configuration.options;
} }
); );
expected = { expected = {

View File

@@ -0,0 +1,53 @@
let
lib = import /home/johannes/git/nixpkgs/lib;
clanLib = import ../../../../lib { inherit lib; };
inherit (lib) evalModules mkOption types;
eval = evalModules {
modules = [
{
options.foos = mkOption {
type = types.attrsOf (
types.submodule {
options.bar = mkOption { };
}
);
};
# config.foos = lib.mkForce { this.bar = 42; };
config.instances.a = { };
# config.foo = lib.mkForce {
# bar = 42;
# };
}
{
_file = "inventory.json";
# instances.a = { setting = };
}
# {
# options.foo = mkOption {
# type = types.attrsOf (types.attrsOf (types.submoduleWith { modules = [
# {
# options.bar = mkOption {};
# }
# ]; }));
# default = { bar = { }; };
# };
# }
# {
# _file = "static.nix";
# foo.static.thing = { bar = 1; }; # <- Can: Op.Modify
# }
# {
# _file = "inventory.json";
# foo.managed.thing = { bar = 1; }; # <- Can: Op.Delete, Op.Modify
# #
# }
];
};
in
{
inherit clanLib eval;
}

View File

@@ -1,7 +1,11 @@
from enum import Enum
from typing import Any, TypedDict from typing import Any, TypedDict
from clan_lib.errors import ClanError from clan_lib.errors import ClanError
from clan_lib.persist.path_utils import PathTuple, path_to_string from clan_lib.persist.path_utils import (
PathTuple,
path_to_string,
)
WRITABLE_PRIORITY_THRESHOLD = 100 # Values below this are not writeable WRITABLE_PRIORITY_THRESHOLD = 100 # Values below this are not writeable
@@ -189,3 +193,69 @@ def compute_write_map(
""" """
return _determine_writeability_recursive(priorities, all_values, persisted) return _determine_writeability_recursive(priorities, all_values, persisted)
class RawAttributes(TypedDict):
headType: str
nullable: bool
prio: int
total: bool
files: list[str]
class OpType(Enum):
MODIFY = "modify"
DELETE = "delete"
def transform_attribute_properties(
introspection: dict[str, Any],
all_values: dict[str, Any],
persisted: dict[str, Any],
# Passthrough for recursion
curr_path: PathTuple = (),
parent_attributes: RawAttributes | None = None,
) -> dict[PathTuple, set[OpType]]:
"""Transform attribute properties to ensure correct types and defaults."""
results: dict[PathTuple, set[OpType]] = {}
for key, key_meta in introspection.items():
if key in {"__this", "__list"}:
continue
path = (*curr_path, key)
results[path] = set()
local_attributes: RawAttributes = key_meta.get("__this")
key_priority = local_attributes["prio"] or None
effective_priority = key_priority or (
parent_attributes["prio"] if parent_attributes else None
)
if effective_priority is None:
msg = f"Priority for path '{path_to_string(path)}' is not defined and no parent to inherit from. Cannot determine effective priority."
raise ClanError(msg)
if isinstance(key_meta, dict):
subattrs = transform_attribute_properties(
key_meta,
all_values.get(key, {}),
persisted.get(key, {}),
curr_path=path,
parent_attributes=local_attributes,
)
results.update(dict(subattrs.items()))
return results
# Only defined in inventory.json -> We might be able to delete it, because we defined it.
# But we could also have some option default somewhere else, so we cannot be sure.
# if all(f.endswith("inventory.json") for f in raw_attributes["files"]):
# operations.add(OpType.DELETE)
# if (
# raw_attributes["prio"] >= WRITABLE_PRIORITY_THRESHOLD
# or ".json" in raw_attributes["files"]
# ):
# operations.add(OpType.MODIFY)

View File

@@ -5,11 +5,110 @@ import pytest
from clan_lib.flake.flake import Flake from clan_lib.flake.flake import Flake
from clan_lib.persist.inventory_store import InventoryStore from clan_lib.persist.inventory_store import InventoryStore
from clan_lib.persist.write_rules import compute_write_map from clan_lib.persist.write_rules import (
compute_write_map,
transform_attribute_properties,
)
if TYPE_CHECKING: if TYPE_CHECKING:
from clan_lib.nix_models.clan import Clan from clan_lib.nix_models.clan import Clan
# foos.this = lib.mkForce { bar = 42; };
# ->
# {
# foos = {
# __this = {
# files = [
# "inventory.json"
# "<unknown-file>"
# ];
# headType = "attrsOf";
# nullable = false;
# prio = 100;
# total = false;
# };
# this = {
# __this = {
# files = [ "<unknown-file>" ];
# headType = "submodule";
# nullable = false;
# prio = 50;
# total = true;
# };
# bar = {
# __this = {
# files = [ "<unknown-file>" ];
# headType = "unspecified";
# nullable = false;
# prio = 100;
# total = false;
# };
# };
# };
# };
# }
def test_write_new() -> None:
all_data: dict = {"foo": {"bar": 42}}
persisted_data: dict = {}
introspection: dict = {
"foo": {
"__this": {
"files": ["/dir/file.nix"],
"headType": "unspecified",
"nullable": False,
"prio": 100, # <- default prio
"total": False,
},
"bar": {
"__this": {
"files": ["/dir/file.nix"],
"headType": "int",
"nullable": False,
"prio": 100, # <- default prio
"total": False,
}
},
}
}
res = transform_attribute_properties(introspection, all_data, persisted_data)
breakpoint()
# No operations allowed, because mkForce
# We cannot modify this value in ANY possible way.
# inventory.json definitions and children definition are filtered out by the module system
# assert attributes == {"operations": set(), "path": ["foo", "bar"]}
# normal_prio_attrs: RawAttributes = {
# "files": ["/dir/file.nix"],
# "headType": "attrsOf",
# "nullable": False,
# "prio": 100, # <- default prio
# "total": False,
# }
# attributes = transform_attribute_properties(("foo", "bar"), normal_prio_attrs)
# # We can modify this value, because its a normal prio
# # This means keys can be added/removed/changed respecting their individual local constraints
# assert attributes == {"operations": { OpType.MODIFY }, "path": ["foo", "bar"]}
# default_prio_attrs: RawAttributes = {
# "files": ["/dir/file.nix"],
# "headType": "attrsOf",
# "nullable": False,
# "prio": 100, # <- default prio
# "total": False,
# }
# attributes = transform_attribute_properties(("foo", "bar"), default_prio_attrs)
# # We can modify this value, because its a normal prio
# # This means keys can be added/removed/changed respecting their individual local constraints
# assert attributes == {"operations": { OpType.MODIFY, OpType.DELETE }, "path": ["foo", "bar"]}
# Integration test # Integration test
@pytest.mark.with_core @pytest.mark.with_core