Merge pull request 'Templates/list: display templates via exposed nix value' (#4219) from templates-list into main

Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4219
This commit is contained in:
hsjobeki
2025-07-06 12:49:58 +00:00
6 changed files with 56 additions and 45 deletions

View File

@@ -229,8 +229,6 @@ in
clanInternals = { clanInternals = {
inventoryClass = inventoryClass =
let let
localModuleSet =
lib.filterAttrs (n: _: !inventory._legacyModules ? ${n}) inventory.modules // config.modules;
flakeInputs = config.self.inputs; flakeInputs = config.self.inputs;
in in
{ {
@@ -240,7 +238,7 @@ in
imports = [ imports = [
../inventoryClass/builder/default.nix ../inventoryClass/builder/default.nix
(lib.modules.importApply ../inventoryClass/service-list-from-inputs.nix { (lib.modules.importApply ../inventoryClass/service-list-from-inputs.nix {
inherit flakeInputs clanLib localModuleSet; inherit flakeInputs clanLib;
}) })
{ {
inherit inventory directory; inherit inventory directory;

View File

@@ -1,12 +1,9 @@
{ {
flakeInputs, flakeInputs,
clanLib, clanLib,
localModuleSet,
}: }:
{ lib, config, ... }: { lib, config, ... }:
let let
inspectModule = inspectModule =
inputName: moduleName: module: inputName: moduleName: module:
let let
@@ -28,16 +25,30 @@ in
{ {
options.modulesPerSource = lib.mkOption { options.modulesPerSource = lib.mkOption {
# { sourceName :: { moduleName :: {} }} # { sourceName :: { moduleName :: {} }}
readOnly = true;
type = lib.types.raw;
default = default =
let let
inputsWithModules = lib.filterAttrs (_inputName: v: v ? clan.modules) flakeInputs; inputsWithModules = lib.filterAttrs (_inputName: v: v ? clan.modules) flakeInputs;
in in
lib.mapAttrs ( lib.mapAttrs (
inputName: v: lib.mapAttrs (inspectModule inputName) v.clan.modules inputName: v: lib.mapAttrs (inspectModule inputName) v.clan.modules
) inputsWithModules; ) inputsWithModules;
}; };
options.localModules = lib.mkOption { options.localModules = lib.mkOption {
default = lib.mapAttrs (inspectModule "self") localModuleSet; readOnly = true;
type = lib.types.raw;
default = config.modulesPerSource.self;
};
options.templatesPerSource = lib.mkOption {
# { sourceName :: { moduleName :: {} }}
readOnly = true;
type = lib.types.raw;
default =
let
inputsWithTemplates = lib.filterAttrs (_inputName: v: v ? clan.templates) flakeInputs;
in
lib.mapAttrs (_inputName: v: lib.mapAttrs (_n: t: t) v.clan.templates) inputsWithTemplates;
}; };
} }

View File

@@ -7,17 +7,38 @@ log = logging.getLogger(__name__)
def list_command(args: argparse.Namespace) -> None: def list_command(args: argparse.Namespace) -> None:
template_list = list_templates("clan", args.flake) templates = list_templates(args.flake)
print("Available local templates:") builtin_clan_templates = templates.builtins.get("clan", {})
for name, template in template_list.self.items():
print(f" {name}: {template['description']}")
print("Available templates from inputs:") print("Available templates")
for input_name, input_templates in template_list.inputs.items(): print("├── <builtin>")
print(f" {input_name}:") for i, (name, template) in enumerate(builtin_clan_templates.items()):
for name, template in input_templates.items(): is_last_template = i == len(builtin_clan_templates.items()) - 1
print(f" {name}: {template['description']}") if not is_last_template:
print(f"│ ├── {name}: {template.get('description', 'no description')}")
else:
print(f"│ └── {name}: {template.get('description', 'no description')}")
for i, (input_name, input_templates) in enumerate(templates.custom.items()):
custom_clan_templates = input_templates.get("clan", {})
is_last_input = i == len(templates.custom.items()) - 1
prefix = "" if not is_last_input else " "
if not is_last_input:
print(f"├── inputs.{input_name}:")
else:
print(f"└── inputs.{input_name}:")
for i, (name, template) in enumerate(custom_clan_templates.items()):
is_last_template = i == len(builtin_clan_templates.items()) - 1
if not is_last_template:
print(
f"{prefix} ├── {name}: {template.get('description', 'no description')}"
)
else:
print(
f"{prefix} └── {name}: {template.get('description', 'no description')}"
)
def register_list_parser(parser: argparse.ArgumentParser) -> None: def register_list_parser(parser: argparse.ArgumentParser) -> None:

View File

@@ -17,7 +17,6 @@ from clan_lib.templates import (
TemplateName, TemplateName,
get_clan_nix_attrset, get_clan_nix_attrset,
get_template, get_template,
list_templates,
) )
from clan_lib.templates.filesystem import copy_from_nixstore from clan_lib.templates.filesystem import copy_from_nixstore
@@ -96,10 +95,6 @@ def test_clan_core_templates(
expected_templates = ["default", "flake-parts", "minimal", "minimal-flake-parts"] expected_templates = ["default", "flake-parts", "minimal", "minimal-flake-parts"]
assert clan_core_template_keys == expected_templates assert clan_core_template_keys == expected_templates
vlist_temps = list_templates("clan", clan_dir)
list_template_keys = list(vlist_temps.inputs[InputName("clan-core")].keys())
assert list_template_keys == expected_templates
default_template = get_template( default_template = get_template(
TemplateName("default"), TemplateName("default"),
"clan", "clan",

View File

@@ -168,7 +168,6 @@ def list_modules(base_path: str) -> ModuleLists:
modules = flake.select( modules = flake.select(
"clanInternals.inventoryClass.{?modulesPerSource,?localModules}" "clanInternals.inventoryClass.{?modulesPerSource,?localModules}"
) )
print("Modules found:", modules)
return modules return modules

View File

@@ -1,10 +1,11 @@
import logging import logging
from dataclasses import dataclass, field from dataclasses import dataclass
from typing import Any, Literal, NewType, TypedDict, cast from typing import Any, Literal, NewType, TypedDict, cast
from clan_lib.dirs import clan_templates from clan_lib.dirs import clan_templates
from clan_lib.errors import ClanCmdError, ClanError from clan_lib.errors import ClanCmdError, ClanError
from clan_lib.flake import Flake from clan_lib.flake import Flake
from clan_lib.nix_models.clan import ClanTemplatesType
from clan_lib.templates.filesystem import realize_nix_path from clan_lib.templates.filesystem import realize_nix_path
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@@ -168,32 +169,18 @@ class InputPrio:
@dataclass @dataclass
class TemplateList: class TemplateList:
inputs: dict[InputName, dict[TemplateName, Template]] = field(default_factory=dict) builtins: ClanTemplatesType
self: dict[TemplateName, Template] = field(default_factory=dict) custom: dict[str, ClanTemplatesType]
def list_templates( def list_templates(flake: Flake) -> TemplateList:
template_type: TemplateType, clan_dir: Flake | None = None
) -> TemplateList:
""" """
List all templates of a specific type from a flake, without a path attribute. Show information about a module
As these paths are not yet downloaded into the nix store, and thus cannot be used directly.
""" """
clan_exports = get_clan_nix_attrset(clan_dir) custom_templates = flake.select("clanInternals.inventoryClass.templatesPerSource")
result = TemplateList() builtin_templates = flake.select("clanInternals.templates")
for template_name, template in clan_exports["self"]["templates"][ return TemplateList(builtin_templates, custom_templates)
template_type
].items():
result.self[template_name] = template
for input_name, attrset in clan_exports["inputs"].items():
for template_name, template in attrset["templates"][template_type].items():
if input_name not in result.inputs:
result.inputs[input_name] = {}
result.inputs[input_name][template_name] = template
return result
def get_template( def get_template(