Compare commits
1 Commits
update-nix
...
write-acce
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f13ce2732 |
@@ -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,31 +68,38 @@ 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
|
||||||
expected = {
|
{
|
||||||
foo = {
|
inherit configuration;
|
||||||
bar = {
|
expr = stableView (
|
||||||
__this = {
|
slib.getPrios {
|
||||||
files = [ ];
|
options = configuration.options;
|
||||||
prio = 9999;
|
}
|
||||||
total = false;
|
);
|
||||||
|
expected = {
|
||||||
|
foo = {
|
||||||
|
bar = {
|
||||||
|
__this = {
|
||||||
|
files = [ ];
|
||||||
|
prio = 9999;
|
||||||
|
total = false;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
|
||||||
|
|
||||||
test_submodule = {
|
test_submodule = {
|
||||||
expr = stableView (
|
expr = stableView (
|
||||||
|
|||||||
53
pkgs/clan-cli/clan_lib/persist/modules.nix
Normal file
53
pkgs/clan-cli/clan_lib/persist/modules.nix
Normal 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;
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user