Merge pull request 'Refactor(cli): name set_inv_machine back to set_machine' (#3681) from hsjobeki/clan-core:chores-2 into main

Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/3681
This commit is contained in:
hsjobeki
2025-05-16 16:23:51 +00:00
9 changed files with 54 additions and 20 deletions

View File

@@ -391,14 +391,14 @@ const MachineForm = (props: MachineDetailsProps) => {
return; return;
} }
const machine_response = await callApi("set_inv_machine", { const machine_response = await callApi("update_machine", {
machine: { machine: {
name: props.initialData.machine.name || "My machine", name: props.initialData.machine.name || "My machine",
flake: { flake: {
identifier: curr_uri, identifier: curr_uri,
}, },
}, },
inventory_machine: { update: {
...values.machine, ...values.machine,
// TODO: Remove this workaround // TODO: Remove this workaround
tags: Array.from( tags: Array.from(

View File

@@ -122,8 +122,8 @@ def set_async_ctx(ctx: AsyncContext) -> None:
class AsyncThread(threading.Thread, Generic[P, R]): class AsyncThread(threading.Thread, Generic[P, R]):
function: Callable[P, R] function: Callable[P, R]
args: Any args: tuple[Any, ...]
kwargs: Any kwargs: dict[str, Any]
result: AsyncResult[R] | None result: AsyncResult[R] | None
finished: bool finished: bool
condition: threading.Condition condition: threading.Condition
@@ -155,6 +155,7 @@ class AsyncThread(threading.Thread, Generic[P, R]):
""" """
try: try:
set_async_ctx(self.async_opts.async_ctx) set_async_ctx(self.async_opts.async_ctx)
# Arguments for ParamSpec "P@AsyncThread" are missing
self.result = AsyncResult(_result=self.function(*self.args, **self.kwargs)) self.result = AsyncResult(_result=self.function(*self.args, **self.kwargs))
except Exception as ex: except Exception as ex:
self.result = AsyncResult(_result=ex) self.result = AsyncResult(_result=ex)

View File

@@ -1,7 +1,7 @@
import logging import logging
import os import os
import sys import sys
import urllib import urllib.parse
from enum import Enum from enum import Enum
from pathlib import Path from pathlib import Path
from typing import TYPE_CHECKING from typing import TYPE_CHECKING

View File

@@ -218,7 +218,7 @@ def generate_facts(
raise ClanError(msg) raise ClanError(msg)
if not was_regenerated and len(machines) > 0: if not was_regenerated and len(machines) > 0:
machine.info("All secrets and facts are already up to date") log.info("All secrets and facts are already up to date")
return was_regenerated return was_regenerated

View File

@@ -10,7 +10,7 @@ from clan_cli.machines.machines import Machine
@API.register @API.register
def get_inv_machine(machine: Machine) -> InventoryMachine: def get_machine(machine: Machine) -> InventoryMachine:
inventory_store = InventoryStore(flake=machine.flake) inventory_store = InventoryStore(flake=machine.flake)
inventory = inventory_store.read() inventory = inventory_store.read()
@@ -23,12 +23,12 @@ def get_inv_machine(machine: Machine) -> InventoryMachine:
@API.register @API.register
def set_inv_machine(machine: Machine, inventory_machine: InventoryMachine) -> None: def update_machine(machine: Machine, update: InventoryMachine) -> None:
assert machine.name == inventory_machine["name"], "Machine name mismatch" assert machine.name == update.get("name", machine.name), "Machine name mismatch"
inventory_store = InventoryStore(flake=machine.flake) inventory_store = InventoryStore(flake=machine.flake)
inventory = inventory_store.read() inventory = inventory_store.read()
apply_patch(inventory, f"machines.{machine.name}", inventory_machine) apply_patch(inventory, f"machines.{machine.name}", update)
inventory_store.write( inventory_store.write(
inventory, message=f"Update information about machine {machine.name}" inventory, message=f"Update information about machine {machine.name}"
) )

View File

@@ -6,6 +6,7 @@ from dataclasses import dataclass
from clan_lib.api import API from clan_lib.api import API
from clan_lib.api.disk import MachineDiskMatter from clan_lib.api.disk import MachineDiskMatter
from clan_lib.api.modules import parse_frontmatter from clan_lib.api.modules import parse_frontmatter
from clan_lib.errors import ClanError
from clan_lib.flake.flake import Flake from clan_lib.flake.flake import Flake
from clan_lib.nix_models.inventory import Machine as InventoryMachine from clan_lib.nix_models.inventory import Machine as InventoryMachine
from clan_lib.persist.inventory_store import InventoryStore from clan_lib.persist.inventory_store import InventoryStore
@@ -13,7 +14,7 @@ from clan_lib.persist.inventory_store import InventoryStore
from clan_cli.completions import add_dynamic_completer, complete_tags from clan_cli.completions import add_dynamic_completer, complete_tags
from clan_cli.dirs import specific_machine_dir from clan_cli.dirs import specific_machine_dir
from clan_cli.machines.hardware import HardwareConfig from clan_cli.machines.hardware import HardwareConfig
from clan_cli.machines.inventory import get_inv_machine from clan_cli.machines.inventory import get_machine
from clan_cli.machines.machines import Machine from clan_cli.machines.machines import Machine
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@@ -42,12 +43,19 @@ def list_machines(
nix_options = [] nix_options = []
for inv_machine in inventory.get("machines", {}).values(): for inv_machine in inventory.get("machines", {}).values():
name = inv_machine.get("name")
# Technically, this should not happen, but we are defensive here.
if name is None:
msg = "InternalError: Machine name is required. But got a machine without a name."
raise ClanError(msg)
machine = Machine( machine = Machine(
name=inv_machine["name"], name=name,
flake=flake, flake=flake,
nix_options=nix_options, nix_options=nix_options,
) )
res[machine.name] = machine res[machine.name] = machine
return res return res
@@ -61,8 +69,9 @@ def query_machines_by_tags(flake: Flake, tags: list[str]) -> dict[str, Machine]:
filtered_machines = {} filtered_machines = {}
for machine in machines.values(): for machine in machines.values():
inv_machine = get_inv_machine(machine) inv_machine = get_machine(machine)
if all(tag in inv_machine["tags"] for tag in tags): machine_tags = inv_machine.get("tags", [])
if all(tag in machine_tags for tag in tags):
filtered_machines[machine.name] = machine filtered_machines[machine.name] = machine
return filtered_machines return filtered_machines
@@ -88,7 +97,7 @@ def extract_header(c: str) -> str:
@API.register @API.register
def get_machine_details(machine: Machine) -> MachineDetails: def get_machine_details(machine: Machine) -> MachineDetails:
machine_inv = get_inv_machine(machine) machine_inv = get_machine(machine)
hw_config = HardwareConfig.detect_type(machine) hw_config = HardwareConfig.detect_type(machine)
machine_dir = specific_machine_dir(machine) machine_dir = specific_machine_dir(machine)

View File

@@ -297,6 +297,13 @@ def get_public_age_keys(contents: str) -> set[str]:
if match: if match:
recipient = match[1] recipient = match[1]
if not recipient:
msg = (
f"Empty or invalid recipient on line {line_number}. "
"Expected a valid `# recipient: age1...` comment or a recognized recipient format."
)
raise ClanError(msg)
keys.add(recipient) keys.add(recipient)
if line.startswith("#"): if line.startswith("#"):

View File

@@ -56,17 +56,33 @@ def list_state_folders(machine: Machine, service: None | str = None) -> None:
) )
for service in state: for service in state:
if not service:
continue
print(f"· service: {service}") print(f"· service: {service}")
if folders := state.get(service)["folders"]: service_cfg = state.get(service)
if not service_cfg:
continue # or handle missing config
folders = service_cfg.get("folders")
if folders:
print(" folders:") print(" folders:")
for folder in folders: for folder in folders:
print(f" - {folder}") print(f" - {folder}")
if pre_backup := state.get(service)["preBackupCommand"]:
pre_backup = service_cfg.get("preBackupCommand")
if pre_backup:
print(f" preBackupCommand: {pre_backup}") print(f" preBackupCommand: {pre_backup}")
if pre_restore := state.get(service)["preRestoreCommand"]:
pre_restore = service_cfg.get("preRestoreCommand")
if pre_restore:
print(f" preRestoreCommand: {pre_restore}") print(f" preRestoreCommand: {pre_restore}")
if post_restore := state.get(service)["postRestoreCommand"]:
post_restore = service_cfg.get("postRestoreCommand")
if post_restore:
print(f" postRestoreCommand: {post_restore}") print(f" postRestoreCommand: {post_restore}")
print("") print("")

View File

@@ -4,6 +4,7 @@ import os
import sys import sys
from dataclasses import is_dataclass from dataclasses import is_dataclass
from pathlib import Path from pathlib import Path
from typing import cast
from clan_lib.api import API from clan_lib.api import API
from clan_lib.api.util import JSchemaTypeError, type_to_dict from clan_lib.api.util import JSchemaTypeError, type_to_dict
@@ -108,7 +109,7 @@ def load_dataclass_from_file(
dataclass_type = getattr(module, class_name, None) dataclass_type = getattr(module, class_name, None)
if dataclass_type and is_dataclass(dataclass_type): if dataclass_type and is_dataclass(dataclass_type):
return dataclass_type return cast(type, dataclass_type)
msg = f"Could not load dataclass {class_name} from file: {file_path}" msg = f"Could not load dataclass {class_name} from file: {file_path}"
raise ClanError(msg) raise ClanError(msg)