diff --git a/pkgs/clan-cli/clan_cli/api/disk.py b/pkgs/clan-cli/clan_cli/api/disk.py index 07427f2ff..53b3e9427 100644 --- a/pkgs/clan-cli/clan_cli/api/disk.py +++ b/pkgs/clan-cli/clan_cli/api/disk.py @@ -184,7 +184,7 @@ def set_machine_disk_schema( raise ClanError(msg, description=f"Valid options: {ph.options}") header = f"""# --- -# schema = "{schema_name}"; +# schema = "{schema_name}" # --- # This file was automatically generated! # CHANGING this configuration requires wiping and reinstalling the machine diff --git a/pkgs/clan-cli/clan_cli/api/modules.py b/pkgs/clan-cli/clan_cli/api/modules.py index 5ee5e942f..31ef7541c 100644 --- a/pkgs/clan-cli/clan_cli/api/modules.py +++ b/pkgs/clan-cli/clan_cli/api/modules.py @@ -59,19 +59,12 @@ class Frontmatter: raise ValueError(msg) -def extract_frontmatter(readme_content: str, err_scope: str) -> tuple[Frontmatter, str]: +def parse_frontmatter(readme_content: str) -> tuple[dict[str, Any] | None, str]: """ - Extracts TOML frontmatter from a README file content. - - Parameters: - - readme_content (str): The content of the README file as a string. - - Returns: - - str: The extracted frontmatter as a string. - - str: The content of the README file without the frontmatter. + Extracts TOML frontmatter from a string Raises: - - ValueError: If the README does not contain valid frontmatter. + - ClanError: If the toml frontmatter is invalid """ # Pattern to match YAML frontmatter enclosed by triple-dashed lines frontmatter_pattern = r"^---\s+(.*?)\s+---\s?+(.*)$" @@ -92,11 +85,32 @@ def extract_frontmatter(readme_content: str, err_scope: str) -> tuple[Frontmatte msg = f"Error parsing TOML frontmatter: {e}" raise ClanError( msg, - description=f"Invalid TOML frontmatter. {err_scope}", + description="Invalid TOML frontmatter", location="extract_frontmatter", ) from e - return Frontmatter(**frontmatter_parsed), remaining_content + return frontmatter_parsed, remaining_content + return None, readme_content + + +def extract_frontmatter(readme_content: str, err_scope: str) -> tuple[Frontmatter, str]: + """ + Extracts TOML frontmatter from a README file content. + + Parameters: + - readme_content (str): The content of the README file as a string. + + Returns: + - str: The extracted frontmatter as a string. + - str: The content of the README file without the frontmatter. + + Raises: + - ValueError: If the README does not contain valid frontmatter. + """ + frontmatter_raw, remaining_content = parse_frontmatter(readme_content) + + if frontmatter_raw: + return Frontmatter(**frontmatter_raw), remaining_content # If no frontmatter is found, raise an error msg = "Invalid README: Frontmatter not found." diff --git a/pkgs/clan-cli/clan_cli/machines/list.py b/pkgs/clan-cli/clan_cli/machines/list.py index 55c6a2017..3d69678c6 100644 --- a/pkgs/clan-cli/clan_cli/machines/list.py +++ b/pkgs/clan-cli/clan_cli/machines/list.py @@ -6,10 +6,13 @@ from pathlib import Path from typing import Literal from clan_cli.api import API +from clan_cli.api.modules import parse_frontmatter from clan_cli.cmd import run_no_output from clan_cli.completions import add_dynamic_completer, complete_tags +from clan_cli.dirs import specific_machine_dir from clan_cli.errors import ClanCmdError, ClanError from clan_cli.inventory import Machine, load_inventory_eval, set_inventory +from clan_cli.machines.hardware import HardwareConfig from clan_cli.nix import nix_eval, nix_shell from clan_cli.tags import list_nixos_machines_by_tags @@ -34,32 +37,48 @@ def list_inventory_machines(flake_url: str | Path) -> dict[str, Machine]: @dataclass class MachineDetails: machine: Machine - has_hw_specs: bool = False - # TODO: - # has_disk_specs: bool = False + hw_config: HardwareConfig | None = None + disk_schema: str | None = None + + +import re + + +def extract_header(c: str) -> str: + header_lines = [] + for line in c.splitlines(): + match = re.match(r"^\s*#(.*)", line) + if match: + header_lines.append(match.group(1).strip()) + else: + break # Stop once the header ends + return "\n".join(header_lines) @API.register -def get_inventory_machine_details( - flake_url: str | Path, machine_name: str -) -> MachineDetails: +def get_inventory_machine_details(flake_url: Path, machine_name: str) -> MachineDetails: inventory = load_inventory_eval(flake_url) machine = inventory.machines.get(machine_name) if machine is None: msg = f"Machine {machine_name} not found in inventory" raise ClanError(msg) - hw_config_path = ( - Path(flake_url) / "machines" / Path(machine_name) / "hardware-configuration.nix" - ) + hw_config = HardwareConfig.detect_type(flake_url, machine_name) - return MachineDetails( - machine=machine, - has_hw_specs=hw_config_path.exists(), - ) + machine_dir = specific_machine_dir(flake_url, machine_name) + disk_schema: str | None = None + disk_path = machine_dir / "disko.nix" + if disk_path.exists(): + with disk_path.open() as f: + content = f.read() + header = extract_header(content) + data, _rest = parse_frontmatter(header) + if data: + disk_schema = data.get("schema") + + return MachineDetails(machine=machine, hw_config=hw_config, disk_schema=disk_schema) -@API.register def list_nixos_machines(flake_url: str | Path) -> list[str]: cmd = nix_eval( [