Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions cookieplone/_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,17 @@ class CookieploneTemplate:
hidden: bool = False


@dataclass
class CookieploneTemplateGroup:
"""A named group of related templates in a cookieplone repository."""

name: str
title: str
description: str
templates: dict[str, "CookieploneTemplate"]
hidden: bool = False


@dataclass
class RepositoryInfo:
"""Resolved repository state for a cookieplone run.
Expand All @@ -32,6 +43,7 @@ class RepositoryInfo:
checkout: str
accept_hooks: bool
config_dict: dict[str, Any]
global_versions: dict[str, str] = field(default_factory=dict)
cleanup_paths: list[Path] = field(default_factory=list)


Expand Down
33 changes: 29 additions & 4 deletions cookieplone/cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from cookieplone.logger import configure_logger, logger
from cookieplone.repository import (
get_base_repository,
get_template_groups,
get_template_options,
)
from cookieplone.utils import console, files, internal
Expand Down Expand Up @@ -100,12 +101,34 @@ def annotate_context(context: dict, repo_path: Path, template: str) -> dict:
return context


def prompt_for_group(
groups: dict[str, t.CookieploneTemplateGroup],
) -> t.CookieploneTemplateGroup:
"""Display template groups and prompt user to choose one."""
choices = {f"{idx}": name for idx, name in enumerate(groups, 1)}
console.welcome_screen(groups=groups)
answer = Prompt.ask("Select a category", choices=list(choices.keys()), default="1")
return groups[choices[answer]]


def prompt_for_template(base_path: Path, all_: bool = False) -> t.CookieploneTemplate:
"""Parse cookiecutter.json in base_path and prompt user to choose."""
templates = get_template_options(base_path, all_)
"""Parse config in base_path and prompt user to choose a template.

When the repository defines groups, a two-step selection is presented:
first the user picks a category, then a template within that category.
Otherwise the flat template list is shown directly.
"""
groups = get_template_groups(base_path, all_)
if groups:
group = prompt_for_group(groups)
console.clear_screen()
templates = group.templates
else:
templates = get_template_options(base_path, all_)
choices = {f"{idx}": name for idx, name in enumerate(templates, 1)}
console.welcome_screen(templates)
console.welcome_screen(templates=templates)
answer = Prompt.ask("Select a template", choices=list(choices.keys()), default="1")
console.clear_screen()
return templates[choices[answer]]


Expand Down Expand Up @@ -139,7 +162,9 @@ def cli(
data.OptionalPath,
typer.Option("--output-dir", "-o", help="Where to generate the code."),
] = None,
tag: Annotated[str, typer.Option("--tag", "--branch", help="Tag.")] = "main",
tag: Annotated[
str, typer.Option("--tag", "--branch", help="Tag.")
] = settings.REPO_DEFAULT_TAG,
info: Annotated[
bool,
typer.Option(
Expand Down
109 changes: 0 additions & 109 deletions cookieplone/config/schemas.py

This file was deleted.

34 changes: 34 additions & 0 deletions cookieplone/config/schemas/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"""JSONSchema definitions for cookieplone configuration formats.

Defines JSON Schemas and validation helpers for:

- **Repository config** (``cookieplone-config.json``): the root file that
lists available templates, groups, global versions, and optional
repository extension.
- **Template config** (``cookieplone.json`` v2): the per-template file that
describes form fields and generator settings.
"""

from cookieplone.config.schemas._types import (
SubTemplate,
TemplateEntry,
TemplateGroup,
)
from cookieplone.config.schemas.repository import (
REPOSITORY_CONFIG_SCHEMA,
validate_repository_config,
)
from cookieplone.config.schemas.template import (
COOKIEPLONE_CONFIG_SCHEMA,
validate_cookieplone_config,
)

__all__ = [
"COOKIEPLONE_CONFIG_SCHEMA",
"REPOSITORY_CONFIG_SCHEMA",
"SubTemplate",
"TemplateEntry",
"TemplateGroup",
"validate_cookieplone_config",
"validate_repository_config",
]
50 changes: 50 additions & 0 deletions cookieplone/config/schemas/_types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
"""TypedDict definitions for cookieplone configuration structures."""

from typing import TypedDict


class TemplateEntry(TypedDict):
"""A template entry from the repository-level ``templates`` mapping.

:param path: Relative filesystem path to the template directory
(e.g. ``"./templates/projects/monorepo"``).
:param title: Human-readable label shown in the template selection menu.
:param description: Short description of what the template generates.
:param hidden: When ``True`` the template is excluded from the default
menu and only shown with ``--all`` or when invoked by name.
"""

path: str
title: str
description: str
hidden: bool


class TemplateGroup(TypedDict):
"""A template group from the repository-level ``groups`` mapping.

:param title: Human-readable label for the group category.
:param description: Short description of the group's purpose.
:param templates: Ordered list of template IDs that belong to this group.
Each ID must match a key in the top-level ``templates`` mapping.
:param hidden: When ``True`` the group is excluded from the default menu.
"""

title: str
description: str
templates: list[str]
hidden: bool


class SubTemplate(TypedDict):
"""A sub-template entry from the ``config.subtemplates`` list.

:param id: Path identifier for the sub-template (e.g. ``"sub/backend"``).
:param title: Human-readable label shown in logs and hooks.
:param enabled: Either a static value (``"0"``/``"1"``) or a Jinja2
expression (e.g. ``"{{ cookiecutter.has_frontend }}"``).
"""

id: str
title: str
enabled: str
74 changes: 74 additions & 0 deletions cookieplone/config/schemas/cookieplone_config.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": ["schema"],
"properties": {
"id": {"type": "string"},
"schema": {
"type": "object",
"required": ["version", "properties"],
"properties": {
"title": {"type": "string"},
"description": {"type": "string"},
"version": {"type": "string", "const": "2.0"},
"properties": {
"type": "object",
"additionalProperties": {
"type": "object",
"required": ["type", "default"],
"properties": {
"type": {"type": "string"},
"title": {"type": "string"},
"description": {"type": "string"},
"default": {},
"format": {"type": "string"},
"validator": {"type": "string"},
"oneOf": {
"type": "array",
"items": {
"type": "object",
"required": ["const", "title"],
"properties": {
"const": {"type": "string"},
"title": {"type": "string"}
}
}
}
}
}
}
}
},
"config": {
"type": "object",
"properties": {
"extensions": {
"type": "array",
"items": {"type": "string"}
},
"no_render": {
"type": "array",
"items": {"type": "string"}
},
"versions": {
"type": "object",
"additionalProperties": {"type": "string"}
},
"subtemplates": {
"type": "array",
"items": {
"type": "object",
"required": ["id", "title", "enabled"],
"properties": {
"id": {"type": "string"},
"title": {"type": "string"},
"enabled": {"type": "string"}
}
}
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}
Loading
Loading